如果在剧本开始时说C ++代码挂在墙上,那么到最后,它肯定会让您大跌眼镜。
比尼亚·斯特鲁斯特鲁普(Bjarne Stroustrup)
从10月31日至11月1日,圣彼得堡举办了C ++俄罗斯Piter会议,这是JUG Ru集团组织的俄罗斯最大的编程会议之一。 受邀的演讲者包括C ++标准化委员会的成员,CppCon的演讲者,O'Reilly的著作的作者以及LLVM,libc ++和Boost等项目的维护者。 该会议面向有经验的C ++开发人员,他们希望加深他们的专业知识并交流现场交流的经验。 向学生,研究生和大学教授提供非常优惠的折扣。
明年四月已经可以参观莫斯科会议的版本,但是现在我们的学生会告诉您他们在上一届活动中学到了什么有趣的东西。
会议相册的照片关于我们
圣彼得堡高等经济学院的两名学生从事以下工作:
- 丽莎·瓦西连科(Lisa Vasilenko)是四年级的本科生,研究“编程语言”的方向,并将其作为“应用数学和计算机科学”课程的一部分。 大学一年级熟悉C ++语言,随后在行业实习中获得了使用C ++语言的经验。 对通用语言和函数式编程语言的热情尤其在会议的报告选择上留下了印记。
- Danya Smirnov是硕士课程“数据编程和分析”的一年级学生。 在学校的时候,他用C ++编写了Olympiad问题,然后由于某种原因该语言在教育活动中不断浮出水面,从而成为主要的语言。 我决定参加会议,以增进我的知识并了解新的机会。
在时事通讯中,教职员工经常分享与我们专业相关的教育事件的信息。 9月,我们看到了有关C ++ Russia的信息,并决定注册为侦听器。 这是我们参加此类会议的第一次经验。
会议结构
在两天的时间里,专家们阅读了30份报告,重点介绍了许多热门话题:用于解决应用问题的语言功能的巧妙应用,由于新标准而导致的语言更新,C ++设计的折衷以及在处理后果时的预防措施,有趣的项目体系结构示例,以及语言基础架构的某些引擎部分。 同时,进行了3场演出,大多数是俄语的2场表演和英语的1场表演。
演讲结束后,所有未解决的问题和不完整的讨论都被转移到带有标记板的演讲者指定的交流区域。 在表演之间进行休息的一种好方法,可以使对话愉快。
如果要作简短报告,可以在标记板上注册一个晚上的闪电谈话,并有五分钟的时间来谈论有关会议主题的任何事情。 例如,快速介绍了C ++的消毒剂(事实证明它是新的),或者一个关于正弦曲线生成中的错误的故事,您只能听到但看不到。
另一种形式是“与灵魂委员会”的小组讨论。 在舞台上,有一些标准化委员会的成员,在放映机上有个壁炉(官方上是为了营造一种深情的气氛,但是“因为一切都着火了”的原因似乎更有趣),这些问题是关于C ++的标准和一般性的,没有激烈的技术讨论和轻描淡写。 事实证明,活着的人也坐在委员会中,他们可能不完全确定某些事情,或者可能不知道某些事情。
对于爱好爱好者来说,第三场比赛仍然存在-BOF会议“反对C ++”。 在会议开始之前,我们带一个Go爱好者,一个C ++爱好者,他们共同准备了100500张关于该主题的幻灯片(例如C ++中的程序包问题或Go中缺少泛型),然后他们在彼此之间和听众之间进行了热烈的讨论,听众试图一次理解两种观点。 如果大头目不营业,主持人会干预并调解双方。 这种格式令人上瘾:开始几小时后,仅完成了一半的幻灯片。 最后必须大大加快。
会议的合作伙伴在大厅里代表他们-在展位上谈论当前的项目,提供实习和就业,举行测验和小型比赛,还颁发了不错的奖项。 但是,有些公司甚至提出要接受采访的初始阶段,这不仅对那些来听报告的人有用。
报告的技术细节
我们这两天都听了报告。 有时很难从并行运行的报告中选择一份报告-我们同意共享和交流在休息期间获得的知识。 即使这样,似乎很多东西仍然丢失了。 在这里,我们想谈谈一些我们认为最有趣的报告的内容
通过编译器优化的角度来看C ++中的异常Roman Rusyaev
演示幻灯片顾名思义,Roman以LLVM为例研究了处理异常的方法。 同时,对于那些在工作中不使用Clang的人来说,该报告仍然可以对如何潜在地优化代码有所了解。 这是因为编译器的开发人员和相应的标准库相互通信,并且许多成功的解决方案可能会重合。
因此,要处理异常,您需要执行许多操作:调用处理代码(如果有)或释放当前级别的资源,然后将堆栈展开得更高。 所有这些导致了这样一个事实,即编译器添加了可能引发调用的其他指令。 因此,如果实际上没有引起异常,则程序仍将开始执行不必要的操作。 为了以某种方式减少开销成本,LLVM提供了几种启发式方法来确定不需要添加异常处理代码或可以减少“不必要”指令数量的情况。
演讲者考虑了其中的十几种,并展示了它们有助于加快程序执行速度的情况以及这些方法不适用的情况。
因此,罗曼·鲁斯亚耶夫(Roman Rusyaev)带领听众得出结论:包含异常工作的代码绝不能总是以零开销执行,并给出以下提示:
- 开发库时,原则上应该放弃异常;
- 如果您仍然需要例外,则应尽可能添加noexcept(和const)修饰符,以便编译器可以进行尽可能多的优化。
总体而言,发言者重申了这样一种观点,即最好将例外情况减到最少或将其全部放弃。
该报告幻灯片位于:
[“通过LLVM编译器优化的棱镜实现C ++异常”]生成器,协程和其他解开大脑的甜味,Adi Shavit
演示幻灯片这次会议上许多致力于C ++ 20创新的报告之一,不仅被色彩斑presentation的演示文稿所记住,而且还清楚地标明了收集处理逻辑中存在的问题(例如,回调循环)。
Adi Shavit强调了以下内容:当前可用的方法遍历整个集合,并且不提供对某些内部中间状态的访问(或在回调的情况下提供,但具有许多不愉快的副作用,例如相同的Callback Hell)。 看起来好像有迭代器,但是使用它们并不是那么顺利:没有通用的入口和出口点(开始→结束与rbegin→递归等),目前尚不清楚我们将迭代多少? 从C ++ 20开始,解决了这些问题!
第一种选择:范围。 由于迭代器之上的包装器,我们为迭代的开始和结束以及组成的可能性提供了一个公共接口。 所有这些使构建完整的数据处理管道变得很容易。 但是,并非所有事情都那么顺利:计算逻辑的一部分在特定迭代器的实现内部,该迭代器会使感知和调试的代码复杂化。
演示幻灯片好吧,在这种情况下,协程在C ++ 20中添加了(其行为类似于Python中的生成器的函数):可以通过返回一些当前值同时保持中间状态来延迟执行。 因此,我们不仅实现了数据出现时的处理,而且将所有逻辑封装在特定的协程内部。
但美中不足的是:目前它们仅得到现有编译器的部分支持,并且实现的程度也不如我们希望的那样准确:例如,协程中不应使用链接和临时对象。 另外,对协程可能有一些限制,此列表中不包括constexpr函数,构造函数/析构函数以及main。
因此,协程通过数据处理逻辑的简单性解决了很大一部分问题,但是它们的当前实现需要改进。
材料:
Yandex.Taxi,Anton Polukhin的C ++技巧
在他的专业活动中,有时您必须实现纯粹的辅助性工作:内部接口和某些库的API之间的包装,日志记录或解析。 但是,通常不需要任何其他优化。 但是,如果这些组件在Runet的某些最受欢迎的服务中使用,该怎么办? 在这种情况下,您将不得不每小时仅处理1 TB的日志! 然后,每一毫秒都是至关重要的,因此您必须诉诸各种技巧-Anton Polukhin谈到了它们。
也许最有趣的示例是指针到实现(pimpl)模式的实现。
#include <third_party/json.hpp> //PROBLEMS! struct Value { Value() = default; Value(Value&& other) = default; Value& operator=(Value&& other) = default; ~Value() = default; std::size_t Size() const { return data_.size(); } private: third_party::Json data_; };
在此示例中,首先要摆脱外部库的头文件-它将更快地编译,并且可以保护自己免受可能的名称冲突和其他类似错误的影响。
好的,将#include移到.cpp文件:您需要包装API的前向声明以及std :: unique_ptr。 现在,我们有了动态分配和其他不愉快的事情,例如分散在堆中的数据和减少的保证。 通过所有这些,std :: aligned_storage可以提供帮助。
struct Value {
唯一的问题:您需要为每个包装指定大小和对齐方式-我们将使用参数<T,SizeT,AlignmentT>制作我们的pimpl模板,使用一些任意值,并向析构函数添加一个检查我们猜到的一切:
~FastPimpl() noexcept { validate<sizeof(T), alignof(T)>(); Ptr()->~T(); } template <std::size_t ActualSize, std::size_t ActualAlignment> static void validate() noexcept { static_assert( Size == ActualSize, "Size and sizeof(T) mismatch" ); static_assert( Alignment == ActualAlignment, "Alignment and alignof(T) mismatch" ); }
由于在析构函数的处理过程中已经确定了T,因此该代码将被正确地反汇编,并且在编译阶段将以错误的形式出现,它将显示需要输入的必要大小和对齐值。 因此,以另外一次编译为代价,我们摆脱了包装类的动态分配,通过实现将API隐藏在.cpp文件中,还获得了更适合处理器缓存的设计。
日志和解析似乎不那么令人印象深刻,因此在本评论中将不会提及。
报告幻灯片可在以下链接获得:
[“ C ++出租车技巧”]保持代码干燥的现代技术,BjörnFahller
在这次演讲中,BjörnFahller展示了几种处理样式缺陷的方法,例如重复的条件检查:
assert(a == IDLE || a == CONNECTED || a == DISCONNECTED);
熟悉吗? 使用最新标准中出现的几种强大的C ++技术,您可以优雅地实现相同的功能,而不会造成丝毫性能损失。 比较:
assert(a == any_of(IDLE, CONNECTED, DISCONNECTED));
要处理无限数量的支票,系统会立即要求您使用可变参数模板和折叠表达式。 假设我们要检查几个变量与enum'a state_type元素的相等性。 首先想到的是编写is_any_of helper函数:
enum state_type { IDLE, CONNECTED, DISCONNECTED }; template <typename ... Ts> bool is_any_of(state_type s, const Ts& ... ts) { return ((s == ts) || ...); }
这样的中间结果令人失望。 到目前为止,代码尚未变得可读:
assert(is_any_of(state, IDLE, DISCONNECTING, DISCONNECTED));
非类型模板参数将有助于稍微改善这种情况。 在他们的帮助下,我们将枚举的枚举元素转移到模板参数列表中:
template <state_type ... states> bool is_any_of(state_type t) { return ((t == states) | ...); } assert(is_any_of<IDLE, DISCONNECTING, DISCONNECTED>(state));
在非典型模板参数(C ++ 17)中使用auto时,该方法可以简单地推广到不仅与state_type元素进行比较,而且还与可以用作非类型模板参数的基本类型进行比较:
template <auto ... alternatives, typename T> bool is_any_of(const T& t) { return ((t == alternatives) | ...); }
通过这些增量改进,可以实现所需的粗略检查语法:
template <class ... Ts> struct any_of : private std::tuple<Ts ...> {
在此示例中,推论指南用于将所需的模板结构参数提示给知道构造函数自变量类型的编译器。
更有趣。 Bjorn讲授除了==之外,还对比较运算符进行泛化,然后对任意运算进行泛化。 与用例一起,说明了诸如no_unique_address属性(C ++ 20)和lambda函数(C ++ 20)中的模板参数之类的功能。 (是的,现在lambda语法更容易记住-这些是四对连续的各种括号。)使用函数作为构造器部件的最终解决方案确实使我感到振奋,更不用说在lambda微积分的最佳传统中使用元组表达式了。
最后,别忘了掩饰一下:
- 回想一下,lambdas是免费的constexpr;
- 添加完善的转发并查看其在lambda闭包中应用于参数包的丑陋语法;
- 让我们为编译器提供更多的条件无条件优化选项。
- 由于有明显的lambda返回值,因此我们将在模板中提供更清晰的错误输出。 这将迫使编译器在实际调用模板函数之前进行更多检查-在类型检查阶段。
有关详细信息,请参阅讲座材料:
我们的印象
我们第一次参加C ++ Russia就是因为它的丰富性。 C ++俄罗斯给人的印象是情感活动,学习与现场交流之间的界限几乎不明显。 从演讲者的心情到活动合作伙伴的竞赛,一切都有助于进行激烈的讨论。 会议的内容包括报告,涵盖了相当广泛的主题,包括C ++创新,大型项目实践中的示例以及意识形态体系结构方面的考虑。 但是剥夺事件的社会组成部分的注意力将是不公平的,这不仅有助于克服与C ++有关的语言障碍。
我们感谢会议的组织者有机会参加这样的活动!
您可以
在JUG Ru博客上看到组织者关于C ++ Russia的过去,现在和将来的帖子。
感谢您的阅读,我们希望对事件的重述对您有所帮助!