C++继任者?Carbon语法速览!

举报
C语言与CPP编程 发表于 2022/07/30 22:29:03 2022/07/30
【摘要】 7月19日Cpp North大会上,谷歌的C++专家Chandler Carruth发表了『C++: What Comes Next? (Announcing the Carbon Language experiment) 』的主题演讲,官宣了正在实验中的Carbon语言,其目标是成为C++的继任者。该消息迅速火爆全球,中文互联网...

7月19日Cpp North大会上,谷歌的C++专家Chandler Carruth发表了『C++: What Comes Next? (Announcing the Carbon Language experiment) 』的主题演讲,官宣了正在实验中的Carbon语言,其目标是成为C++的继任者。该消息迅速火爆全球,中文互联网圈在7月20号也开始大量报道这一消息。

3c12e5b8be7126537ac1f9cc720269ae.png

Chandler Carruth在谷歌负责LLVM编译器的优化,也是谷歌在 C++ 委员会的代表。0103a235568976b095a1d9aa6480d68f.png

目前Carbon语言在Github上已经开源,地址:https://github.com/carbon-language/carbon-lang

7月22日,Cpp North的Youtube频道公开了这一演讲的视频:https://www.youtube.com/watch?v=omrY53kbVoA&ab_channel=CppNorth

接下来让我们来速览一下Carbon的语法,首先声明本文的Carbon代码都是基于Chandler Carruth的Keynote以及当前Carbon的github仓库中的代码。

由于Carbon还在实验阶段,Chandler Carruth的Keynote中演示某些语法,当前的Carbon还不支持编译……,比如abstract class、类外定义函数等等。当然已经实现的语法部分可能不稳定,后期也可能会再有重大调整也未可知。

由于当下并且没有支持Carbon语法的代码高亮插件,所以下面代码中的高亮并不准,敬请见谅。

Hello World

package ExplorerTest api;
  

第一行是定义了package(包),包名是ExplorerTest。后面的api,那个不是包名的一部分,但是又不能省略(当前可以是api或impl)。


   
  1. fn Main() -> i32 {
  2.   var s: auto = "Hello world!";
  3.   Print(s);
  4.   return 0;
  5. }

定义了main函数,Carbon中使用的Main()。从这个Hello World的例子中,变量语法、函数定义语法可见一斑。

变量

var

即变量:


   
  1. var x: i64 = 42;
  2. x = 7;

笔者点评:个人感觉还不如C++的语法简洁…… int64_t x = 42;

冒号后面是变量的类型,i64是int64,也可以使用auto做类型推导:

var x: auto = 42;
  

let

基本等同于C++中常量,Rust也采用let表示常量。

let x: i64 = 42;
  

同样也支持auto

let x: auto = 42;
  

控制流

if else


   
  1. if (fruit.IsYellow()) {
  2.   Console.Print("Banana!");
  3. else if (fruit.IsOrange()) {
  4.   Console.Print("Orange!");
  5. else {
  6.   Console.Print("Vegetable!");
  7. }

和C/C++一样。

while


   
  1. var x: i32 = 0;
  2. while (x < 3) {
  3.   Console.Print(x);
  4.   ++x;
  5. }
  6. Console.Print("Done!");

也和C++一样

for


   
  1. for (var step: Step in steps) {
  2.   if (step.IsManual()) {
  3.     Console.Print("Reached manual step!");
  4.     break;
  5.   }
  6.   if (step.NotReady()) {
  7.     continue;
  8.   }
  9.   step.Process();
  10. }

语法是for-range循环的语法。同样也能发现Carbon支持break和continue。

match

Carbon中没有switch,但是有match:


   
  1. fn Bar() -> (i32, (f32, f32));
  2. fn Foo() -> f32 {
  3.   match (Bar()) {
  4.     case (42, (x: f32, y: f32)) => {
  5.       return x - y;
  6.     }
  7.     case (p: i32, (x: f32, _: f32)) if (p < 13) => {
  8.       return p * x;
  9.     }
  10.     case (p: i32, _: auto) if (p > 3) => {
  11.       return p * Pi;
  12.     }
  13.     default => {
  14.       return Pi;
  15.     }
  16.   }
  17. }

不过C++中的switch能比较的只能是整型、枚举。

函数


   
  1. fn Add(a: i64, b: i64) -> i64 {
  2.   return a + b;
  3. }

Carbon中这个Add函数的写法和Rust中实现一个Add几乎一模一样。比如声明参数的时候类型在后,并且冒号分割的参数写法。还有fn关键字。

当然在函数体内逻辑复杂的时候,会和Rust不同,因为Rust还有不带分号的表达式语法,表达式的值就是整个函数的值。Carbon没有那种怪异的东西。

另外Carbon也能进行返回值的类型推导:


   
  1. fn Add(a: i64, b: i64) -> auto {
  2.   return a + b;
  3. }

一个更复杂的函数的例子:


   
  1. package Geometry api;
  2. import Math; // 导入其他包
  3. class Circle { // 定义一个Circle类
  4.     var r: f32;
  5. }
  6. fn ScaleAreaAndAppend(circle: Circle, log2_scale: i32,
  7.                       results: Vector(f32)*) {
  8.   var area: f32 = Math.Pi * c.r * c.r;
  9.   let scale: i32 = 1 << log2_scale;
  10.   area *= scale;
  11.   results->append(area);
  12. }

参数默认都是常量,除非声明成指针类型。

面向对象

类与对象


   
  1. class Point {
  2.   var x: i32;
  3.   var y: i32;
  4. }
  5.  
  6. fn Main() -> i32 {
  7.   var p1: Point = {.x = 1, .y = 2}; 
  8.   var p2: auto = p1; 
  9.   p2.x = 3;
  10.   return p1.x - 1;
  11. }

成员变量与成员函数


   
  1. class NewsAriticle {
  2.   // 类似C++的static
  3.   fn Make(headline: String, body_html: String) -> NewsAritcle();
  4.   // 只读方法
  5.   fn AsHtml[me: Self]() -> String;
  6.   // 可修改方法
  7.   fn Publish[addr me: Self*]() { me->published = DateTime.Now(); }
  8.   private var headline: String;
  9.   private var body_html: String;
  10.   private var published: Optional(Datetime);
  11. }
  12. // 类外定义成员函数
  13. fn NewsAriticle.AsHtml[me: Self]() -> String{ ... }

Carbon中类中成员访问控制权限默认都是public,如果需要声明成私有则需要单独加private关键字。这个行为和C/C++的struct相同,但是和主流语言的class都不同。

定义在类中的函数,如果有[me: Self]表示是只读的成员函数,在函数中不能修改类对象的成员变量。me在函数体中是表示对当前对象的引用,类似C++的(*this)

如果有[addr me: Self*]表示的是可对当前对象进行修改的函数。me在函数体中类似C++的this指针。

[me: Self][addr me: Self*]的成员函数,也可以称作方法(method),如果类中的函数没有[me: Self][addr me: Self*],则表示是一个和对象无关的函数,等价于C++中的static成员函数。这个设计很像python中的类中成员函数的设计。

继承与抽象

Carbon只支持单继承,这没的说。值得注意的是普通的class关键字定义的类型默认都是final的,即不能被继承生成子类(俗称『绝育』)。但abstrct classbase class关键字定义的类型可以被继承:


   
  1. // 抽象类(abstract class)不能被实例化,因为其中可能包含抽象方法
  2. abstract class UIWidget {
  3.   // 抽象方法(abstract fn)没有实现
  4.   abstract fn Draw[me: Self](s: Screen);
  5.   abstract fn Click[addr me: Self*](x: i32, y: i32);
  6. }
  7. // base class 允许扩展和实例化 
  8. base class Button extends UIWidget {
  9.   // 实现抽象方法
  10.   impl fn Draw[me: Self](s: Screen) { ... }
  11.   impl fn Click[addr me: Self*];
  12.   // 新增了一个虚函数(virtual fn)
  13.   virtual fn MoveTo[addr me: Self*](x: i32, y: i32);
  14. }
  15. // 类外实现方法
  16. fn Button.Click[addr me: Self*](x: i32, y: i32) { ... }
  17. fn Button.MoveTo[addr me: Self*](x: i32, y: i32) { ... }
  18. class ImageButton extends Button {
  19.   ...
  20. }

abstrct class就是抽象类,它不能被实例化,因为其中有抽象方法。抽象类与抽象方法的概念和Java类似。抽象方法等同于C++中的纯虚函数

base class不仅是可以被继承(扩展)的类,还能实例化。因为它里面不会有抽象方法,所有继承而来的抽象方法都要被实现。base class中也能用virtual修饰成员函数,这个语法是从C++中的虚函数而来的。

泛型

泛型接口

定义泛型接口来做泛型代码的类型检查


   
  1. interface Summary {
  2.   fn Summarize[me: Self]() -> String;
  3. }

这个interface不是Java中的interface,而是有点像C++中的Concept,对泛型做类型检查用的。

泛型函数


   
  1. fn PrintSummary[T:! Summary](x: T) {
  2.   Console.Print(x.Summarize());
  3. }

定义了一个支持Summary泛型接口的泛型函数PrintSummary

实现泛型接口


   
  1. class NewsArticle {
  2.   ...
  3.   impl as Summary {
  4.     fn Summarize[me: Self]() -> String { ... }
  5.   }
  6. }

所以可以使用泛型函数来调用实现了泛型接口的类对象


   
  1. // n 是 NewsArticle类型
  2.   PrintSummary(n);

也可以直接调用


   
  1. // n 是 NewsArticle类型
  2.   n.Summarize();

扩展其他包的API


   
  1. import OtherPackage;
  2. interface Summary {
  3.   fn Summarize[me: Self]() -> String;
  4. }
  5. // 泛型函数
  6. fn PrintSummary[T:! Summary](x: T) {
  7.   Console.Print(x.Summarize());
  8. }
  9. // 扩展外部API的接口
  10. external impl OtherPackege.Tweet as Summary {
  11.   fn Summarize[me: Self]() -> String { ... }
  12. }
  13. fn SummarizeTweet(t: Tweet) {
  14.   PrintSummary(t);
  15. }

我们导入了一个外部的包OtherPackege,它之中有一个Tweet类型,然后我们可以通过external impl来扩展它支持它本不存在的泛型接口。

指针

基本语法:


   
  1. // 定义i32类型变量x,值为5
  2.   var x: i32 = 5;
  3.   // 把x的值改成10
  4.   x = 10;
  5.   // 定义i32*类型的指针p,指向变量x的地址
  6.   var p: i32* = &x;
  7.   // 通过指针修改x的值为7
  8.   *p = 7;
  9.   // 定义i32*类型的指针q,使用&*p,同样指向变量x的地址
  10.   var q: i32* = &*p;
  11.   // 通过指针q修改x的值为0
  12.   *q = 0;
  13.   // 定义一个i32类型的变量y,值为0
  14.   var y: i32 = *p;

另外Carbon的指针不支持空指针,如果想表示不存在,使用Optional。

与C++互操作

与C++互操作是Carbon宣传的重点,也是最大难点。现在Carbon语言还不完善,这里举一个Keynote中演示的例子。

有一个C++的头文件circle.h


   
  1. struct Circle {
  2.   float r;
  3. }

Carbon调用C++

然后编写一个Carbon代码文件:geometry.carbon


   
  1. package Geometry api;
  2. import Math;
  3. import Cpp library "circle.h";
  4. fn PrintArea(circles: Slice(Cpp.Circle)) {
  5.   var area: f32 = 0;
  6.   for (c: Cpp.Circle in circles) {
  7.     area += Math.Pi * c.r * c.r;
  8.   }
  9.   Print("Total area: {0}", area);
  10. }

可以通过 import Cpp library "circle.h"; 这种语法来引用C++头文件中声明的类型。

C++调用Carbon

写一个C++的源文件:


   
  1. #include <vector>
  2. #include "circle.h"
  3. #include "geometry.carbon.h"
  4. auto main(int argc, char* argv) -> int {
  5.   std::vector<Circle> circles = {{1.0}, {2.0}};
  6.   Geometry::PrintArea(circles);
  7.   return 0;
  8. }

最后提醒

最后再次提醒,某些KeyNote中演示的Carbon语法仅仅是当前规划中的设计,但还没有被实现。当前已经实现,能够通过编译语法,参考Github仓库中explorer/testdata目录中的代码。

文章来源: blog.csdn.net,作者:程序员编程指南,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/weixin_41055260/article/details/126066663

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。