一个处于实验中的新编译器:CppFront

描述

上个月,在CppCon2022上Herb Sutter介绍了他的一个处于实验中的新编译器:CppFront。

他通过该编译器来实践一种潜在的C++替换语法,简称为Cpp2,C++当前语法则称为Cpp1。Herb从2015年就着手设计该项目,如今推了出来,一时激起不小浪花,项目地址为https://github.com/hsutter/cppfront。官方描述如下:
Cppfront is a experimental compiler from a potential C++ 'syntax 2' (Cpp2) to today's 'syntax 1' (Cpp1), to learn some things, prove out some concepts, and share some ideas. This compiler is a work in progress and currently hilariously incomplete… basic functions work, classes will be next, then metaclasses and lightweight exceptions.
C++这是在自我破局,开辟第二曲线啊!标准是想再次实现C++11那种巨大的成功,使C++再次焕然一新,更加现代、简单、高效。(虽然Herb说这个项目只代表个人的实践,但是好的点子标准也不会放过,而且是实践可行的点子)其实近几年C++的发展速度真不慢,许多特性迟久未入,只是不想刚引入就遭淘汰。当你再次见到C++更新时,很可能又会是像见到了一个新语言一样,语法完全变化,近几次的标准已有这个趋势。下面我将提供一些Cpp2的例子,这些例子都是可以使用CppFront手动编译运行的,大家可以看看它的语法。Cpp2的目标可以概括为两个词:安全和简洁,语法也以此为导向。先来看一个最简单的例子:

		
			main: () -> int = {     s: std::string = "world";     std::cout << "Hello " << s << " "; }这就是使用纯Cpp2编写的代码,所有的声明都由原来的r-to-l变为了l-to-r。其实从trailing-return-type开始,C++已经慢慢变成了这种l-to-r形式的语言。可以混合使用Cpp1和Cpp2的语法,但是对于纯Cpp2语法,它可以隐式地引入C++ 23的"import std"模块,因此无需手动添加任何头文件。接着来看一个文件读写的例子:

		
			main: () -> int = {     s: std::string = "Lily";     myfile := fopen("dog""w");     myfile.fprintf("Hello %s! ", s.c_str());     myfile.fclose(); }这个代码的确简单不少,而且fprintf(), fclose()是作为成员函数存在的,这意味着可以触发IDE的智能提示,提升开发效率。下面是一个类型安全的例子:

		
			main: () -> int = {     v: std::variant<intdouble> = 42.0;     a: std::any = "xyzzy" as std::string;     o: std::optional<int> = ();     test_generic(3.14);     test_generic(v);     test_generic(a);     test_generic(o);     std::cout << " ";     v = 1;     a = 2;     o = 3;     test_generic(42);     test_generic(v);     test_generic(a);     test_generic(o); } test_generic: ( x: _ ) = {     std::cout         << std::setw(30) << typeid(x).name()         << " value is "         << inspect x -> std::string {             is int = std::to_string(x as int);             is std::string = x as std::string;             is _ = "not an int or a string";         }         << " "; }Cpp2中函数不需要前置声明,它具有顺序无关性(其实是生成代码的时候自动提供前置声明了)。其中的"_"是隐式模板的通配符,相当于T,而inspect is as等等这些都是Pattern Matching的语法。在CppFront中,typeid().name()返回的类型名称是可读的,因此最终输出如下图。编译器对于指针,许多运算符都被禁止了,看如下例子:

		
			main: () -> int = {     words: std::vector<std::string> =          ( "decorated""hello""world" );     first: *std::string = words.front()&;     last: *std::string = words.back()&;     while first <= last {         print_and_decorate(first*);         first++; // unsafe         // first + 1;         // first[1];         // first~;         // delete first;     } } print_and_decorate: (thing:_) =     std::cout << ">> " << thing << " ";当试图对指针使用这些操作时,将产生编译期错误。上述代码将报错:

		
			error: ++ - pointer arithmetic is illegal - use std::span or gsl::span instead它会推荐你使用更好的替代特性,有些特性甚至内置为了原生特性,比如智能指针,它会推荐你使用unique.new, shared.new来管理指针的生命周期。为了防止越界访问,Cpp2支持边界安全。比如:

		
			main: () -> int = {     cpp2::Bounds.set_handler(call_my_framework);     words: std::vector<std::string> =          ( "decorated""hello""world" );     s: std::span = words;     print_and_decorate( s[3] ); } print_and_decorate: (thing:_) =     std::cout << ">> " << thing << " "; call_my_frameword: (msg: * const char) =     std::cout         << "sending error to my framework... ["         << msg << "] ";代码中访问s[3]越界,编译时指定-s标志,便可以帮你开启边界检查,运行之时进行报错。此外,还可以通过第2行的代码设置一个handler,来捕获错误。只有一个表达式的函数,"{}"将作为可选项,省略掉要更加简洁。 此外,Cpp2也支持初始化安全,对此,指针不可赋值为nullptr/0/NULL。看一个例子:

		
			main: () -> int = {     cpp2::Bounds.set_handler(call_my_framework);     p: *std::string;     if std::rand() % 2 {         p = words.front()&;     }     // else {     //     p = words.back()&;     // }     print_and_decorate( p* ); } print_and_decorate: (thing:_) =     std::cout << ">> " << thing << " ";例子中,p是一个指针,但是赋值语句没有遍及各个分支,访问之时肯定会存在错误。因此它会直接产生编译期错误:

		
			example.cpp2(4,5): error: local variable p must be initialized on both branches or neither branch example.cpp2(6,5): error: "if" initializes p on:   branch starting at line 6 but not on:   implicit else branch   ==> program violates initialization safety guarantee - see previous errors这个特性可以保证变量在使用前初始化。最后,还有一个有意思的特性用于函数多返回值,一个简单的例子:

		
			f: () -> (i: int, s: std::string) = {     i = 10;     s = "haha";     return; } auto main() -> int {     auto [a, b] = f();     std::cout << a << " " << b << " "; }这个代码混合了Cpp1和Cpp2。函数f()具有多个返回值,可以不用借用std::paire与std::tuple等等组件。实际上,它会自动生成一个结构体,作为函数的返回值。该例子生成的为:

		struct f__ret {     int i;     std::string s; };这些例子都来自Herb的演讲内容,他说Cpp2会比原来的C++语法简单和安全10倍,想看的地址为:https://www.youtube.com/watch?v=ELeZAKCN4tYCpp2一直只是Herb个人的一个实验性产品,项目必然存在很多缺陷,但正是这些不断的探索,为C++提供了更多可能。 大家觉得目前的Cpp2设计怎样呢?有没有哪些不错的点子?

 审核编辑 :李倩


打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分