从 C++11 升级至 C++17,它们让嵌入式系统更好了!小米汽车再换阵:前麦格纳高管黄振宇接管供应链

2023-06-02 0 251

2011 年 8 月,ISO 理事会正式发布了 C++11,2017 年 12 月又正式发布了 C++17 国际标准,每天C语言新版本的插值,会因很多项目组也开始著手升级换代合作开发自然环境,比如责任编辑翻译者。所以从 C++11

书名镜像:https://interrupt.memfault.com/blog/cpp-17-for-embedded

需经容许,明令禁止转发!

翻译者 | a layan D kme

翻译者 | 弯月 白眉林 | 郑丽媛

公司出品 | CSDN(ID:CSDNnews)

前段时间,他们项目组已经开始升级换代合作开发自然环境,试著采用很多辅助工具和C语言的新版本。在这个操作过程中,较为十分困难的几项工作是将他们的PDP应用流程的标识符库从 C++11 升级换代到 C++17。

在责任编辑中,我将展现在PDP当今世界中十分管用的很多 C++17 的优点(特别注意:从 C++11 北迁到 C++17 也囊括了 C++14,因而我也会提及 C++14 的很多优点)。

查阅完备的 C++17 优点条目,可赶赴:https://github.com/AnthonyCalandra/modern-cpp-features#c17-language-features。

原本,他们从 C++03 北迁到了 C++11,与之较之,从 C++11 升级换代到 C++14 时看见的升级换代较为小。因而,能在PDP控制系统中采用的 C++14 独有机能事实上并不多。

十进制字面上量

如果你时常须要继续执行按位演算或修正暂存器,所以一定很讨厌那些字面上量。很多C++具备全力支持这类字面上量的扩充,那些字面上量在前述的国际标准中也有先机。

uint8_t a = 0b110; // == 6uint8_t b = 0b11111111; // == 255

constexpr**

在 C++14 中,能在 constexpr 表达式中采用的句法获得了扩充。constexpr 特别适用于PDP合作开发,因为它能在编译时进行计算并将很多标识符简化为常量。请特别注意,只有当表达式的所有需求都能在编译期间确定时,才能在编译时计算表达式。

constexpr int factorial ( int n ) {if ( n <= 1 ) {return 1;} else {return n * factorial ( n – 1 ) ;}}factorial ( 5 ) ; // == 120 ( Calculated at compile time )

与 C++14 较之,C++17 国际标准有了很大的变化,但无需担心,你仍然能采用已有的机能。除了已有机能之外,你还将拥有更强大的 C++17 句法和库。

(1)属性

首先,他们来介绍三个新属性: [ [ fallthrough ] ] 、 [ [ nodiscard ] ] 和 [ [ maybe_unused ] ] 。因为那些属性只在编译时考虑,所以你根本不须要担心它的效率。它的存在就是为了提升标识符合作开发。

[ [ fallthrough ] ]

你能利用这个属性将两个相邻的 case 分支的主体合并到一个 switch 中,而不会收到来自C++的任何警告。你能通过这个属性告诉C++前一个 case 主体结束是有意为之。

switch ( n ) {case 1: [ [ fallthrough ] ] // …// no `break;`case 2:// …break;}

[ [ nodiscard ] ]

你是不是也时常忘记检查表达式的返回值?有了这个属性,丢弃返回值就会收到C++的警告。

[ [ nodiscard ] ] bool do_something ( ) {return is_success; // true for success, false for failure}do_something ( ) ; /* warning: ignoring the return value of function declared with attribute nodiscard */

[ maybe_unused ] ]

为了避免收到警告,必须将未采用的变量转换为 void,你是不是也感到不耐烦?试试看这个属性,你就能摆脱那些烦人的警告。

void my_callback ( std::string msg, [ [ maybe_unused ] ] bool error ) {// Dont care if `msg` is an error message, just log it.log ( msg ) ;}

(2)编译时的力量

编译时的检查是我最讨厌 C++ 的地方。在 C++17 中,这种能力通过很多新优点获得进一步增强。想一想很多PDP控制系统中繁琐的调试操作过程,如今甚至不须要部署标识符就能检查结果,是不是觉得是个特大好消息?传输可继续执行文件、准备自然环境和测试等一系列工作都十分艰巨,而且很耗时。但采用编译时编程,这部分头疼的工作都能省略。

没有消息的静态断言

你可能认为,他们已经有了 static_assert ( .. ) ,能在编译时进行检查。而如今,断言机制甚至不须要错误消息。这样,标识符看上去会更加清晰。

static_assert ( false ) ;

if constexpr

我最讨厌的一个语句!他们能利用 if constexpr 编写很多标识符,那些标识符能根据编译时的条件,有选择地进行实例化。

template<typename T>auto length ( const T& value ) noexcept {if constexpr ( std::integral<T>::value ) { // is numberreturn value;}else {return value.length ( ) ;}}int main ( ) noexcept {int a = 5;std::string b = “foo”;std::cout << length ( a ) << << length ( b ) << n; // Prints “5 3”}

在 C++17 之前,上面这段标识符须要编写两个不同的表达式,分别用于字符串和整数输入,如下所示。

int length ( const int& value ) noexcept {return value;}std::size_t length ( const std::string& value ) noexcept {return value.length ( ) ;

constexpr lambda

如果你也讨厌在标识符中采用 lambda 表达式,所以肯定会讨厌这个机能。此外,Lambdas 的调用也能采用直接声明为 constexpr 的形式。

auto identity = [ ] ( int n ) constexpr { return n; };static_assert ( identity ( 123 ) == 123 ) ;

在 C++17 中,有很多机能能帮助你编写更漂亮的标识符。即使它的存在对运行时性能没有明显的影响,但你会很讨厌它。

折叠表达式

如果你有过采用可变参数模板来编写具备可变输入或插值次数的递归算法的经历,所以就可能遇到必须为该可变参数模板表达式实现终止符的问题。比如,下面的标识符是用 C++11 编写的,作用是累加给定的数字。

int sum ( ) { return 0; } // Termination functiontemplate<typename …Args>int sum ( const int& arg, Args… args ) {return arg + sum ( args… ) ;}

如果他们没有实现不接受任何输入的终止符,这段标识符将无法通过编译。但有了折叠表达式,你就不必实现终止符了,而标识符看上去也更快,如下所示。

template<typename …Args>int sum ( Args&&… args ) {return ( args + … ) ;}

嵌套命名空间

不知道为什么 C++ 理事会以前没有想到这一点。无需多说,分别看下面 C++11 和 C++17 中嵌套命名空间的定义,你就能发现区别。

// C++11namespace A {namespace B {namespace C {int i;}}}// C++17namespace A::B::C {int i;}

加强版的条件语句

如果所有条件语句都像 for 语句一样具备初始化,那是不是更强大?在 C++17 中,条件语句也增加了初始化部分。

这是迄今为止我所见过的最强大的机能之一,因为你无需在输入一系列 if-else 语句或 switch-case 之前,编写一堆局部变量。

if ( int i = 4; i % 2 == 0 ) {cout << i << ” is even number” << endl;}switch ( int i = rand ( ) % 100; i ) {default:cout << “i = ” << i << endl;break;}

内联变量

在 C++17 之前,他们必须在源文件中实例化类内静态变量。如今,你能采用内联变量将声明和初始赋值合并到类定义中,如下所示。

struct BabaMrb {static const int value = 10;static inline std::string className = “Hello Class”;}

C++17 中还有很多我不知道如何归类的的其他优点。下面,他们来逐一介绍。

复制省略

复制省略(Copy elision),即返回值优化,是大多数C++为防止在某些情况下出现额外副本而实现的优化。从 C++17 开始,直接返回对象时必然会触发复制省略。在某些情况下,即使只有一次复制操作也会影响控制系统的性能,比如对实时性有严格要求的控制系统。遇到这种情况,他们最好确保避免复制,以免降低控制系统性能。

struct C {C ( ) { std::cout << “Default constructor” << std::endl; }C ( const C& ) { std::cout << “Copy constructor” << std::endl; }};C f ( ) {return C ( ) ; // Definitely performs copy elision}C g ( ) {C c;return c; // May perform copy elision}int main ( ) {C obj = f ( ) ; // Copy constructor isnt called}

共享互斥锁

在采用共享互斥锁后,他们就能按需读取对象而无需加锁,而写调用能照往常一样采用常规互斥锁来锁定对象。共享互斥锁能加快只读访问操作的速度,因为读取操作能同步进行。

硬件干涉大小

这个新的库机能能帮助你在编译期间确定 L1 缓存行的大小。有了这个机能,你就能根据 L1 缓存行的大小调整结构、缓冲区等。我在采用 C++11 为 ARM Cortex-A9 内核实现低级裸机 DMA 驱动流程时就会用到这个功能,因为在编写那些标识符时,我须要手动管理高速缓存和主内存之间的一致性。

尽管此机能十分强大,但直到版本 12 才在所有版本的 GCC 中实现,因而很可能你当前的C++并不全力支持。如下标识符是一个示例,能帮助你更快地理解这个机能。

#ifdef __cpp_lib_hardware_interference_size // Undefined prior to C++17using std::hardware_constructive_interference_size;using std::hardware_destructive_interference_size;#else// 64 bytes on x86-64 │ L1_CACHE_BYTES │ L1_CACHE_SHIFT │ __cacheline_aligned │ …constexpr std::size_t hardware_constructive_interference_size = 64;constexpr std::size_t hardware_destructive_interference_size = 64;#endifstruct alignas ( hardware_constructive_interference_size ) OneCacheLiner { // occupies one cache linestd::atomic_uint64_t x{};std::atomic_uint64_t y{};};

与 C++14 不同,C++17 引入了很多新优点。其中很多机能对PDP控制系统合作开发十分有帮助。

不同产品之间,PDP设备的计算能力差异很大。由于 CPU 性能、缺乏C++全力支持、验证必要性等多种原因,我选择的某些机能可能不适用于你的固件。总体而言,北迁到 C++17 可能须要花费大量的时间和精力,请认真考虑是否须要北迁。

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务