Carbon,下一个 C++ 挑战者?

2,591 阅读8分钟

Introduction

  • JavaScript → TypeScript
  • Java → Kotlin
  • C++ → Carbon

Note that Carbon is not ready for use.

Carbon: 完美的C++接班人

添加图片注释,不超过 140 字(可选)

最近,Google向社区贡献了一个全新的语言——Carbon,并且兴致勃勃的想要做新时代的C++接班人!

github.com/carbon-lang…

Related Work

C++

众所周知,c++ 应用广泛,很多性能敏感的软件和系统在构建时都会优先考虑c++,它在诞生之初也是一个单方面兼容C(C的超集)的高级语言。这也带来了很多弊病,很多大型的代码基础库也都是积年累月几代猿猴用cpp 呕心沥血编写而成。从c++11 开始,大多数程序猿终于摆脱了c with class 的印象派写法。但是对于没写几年cpp的程序猿来说,使用cpp的一些根本性问题依然存在:

  • ABI 问题
  • 当你用一个库,里面必然存着很多信息(比如实际的机器码、函数表),总得有一个规定说明他们存储的结构(PE/dll,elf/so),这就是ABI的一部分。
  • 当你想用库里的一个函数,你总得去查找函数表。但是对于C++,并不是存函数名就够的。C++允许重载,即两个函数可以同名但不同参数,所以要把参数信息一起编码进去才能进行查找,这个就是name mangling。
  • 此外,不同的架构(如x86和arm,x86的32位/64位)有着不同的资源/寄存器,也会导致calling convension的不同。相互之间机器码不能互相兼容,所以也要纳入ABI的考量。
  • 一般开源库都提供源码,无法用binary形式发布,且一般强制使用c的ABI来给其他调用者使用
  • 系统库性能问题

标准库性能堪忧,功能不全,需要引入三方库或者自己手撸来补齐能力

  • 异常不可用
  • 各个toolchain 以及厂商之间的实现导致异常处理并不统一,没有放之四海而皆准的异常捕获方式。只能弃用异常,任由程序疯狂中断。
  • 没有一个统一成熟的包管理机制

但是瑕不掩瑜,c++ 超高的性能、超强的拓展性又让人欲罢不能。

为了规避c++ 因为过于自由而产生的安全性问题,Rust 应运而生。

Rust

Advantages

  • 零成本抽象

即我使用到的特性,我“付费”,我没有使用的特性,跟我也没关系。

  • 类型安全与内存安全

Rust通过强大的类型系统和所有权模型来保证内存安全与线程安全,在编译时消除很多可能存在的错误。

Rust里每一个引用和指针都有一个lifetime,对象则只不允许在同一时间有两个和两个以上的可变引用。

使用Rust语言编程不需要手动分配(malloc)释放(free)内存和资源。Rust通过所有权系统来判断资源分配和释放的时机,在资源不再使用自动调用析构函数并释放资源。(这里看起来其实和C++的RAII一样,也是C++精髓所在)所有权系统是Rust与其他语言最不同的一点,这也是Rust学习曲线较为陡峭的原因之一。

  • Rust使用特征(trait)机制来实现抽象和代码复用
  • 包管理 Cargo

Disadvantages

心智负担高,通过编写代码时的规约来限制程序的行为,透过编译器来约束代码边界。Rust 适合新项目,很难与原始的c++项目无负担的混编

Carbon

Google 推出的 Carbon 旨在像在android 项目中使用kotlin 替换 java,像在前端项目中用TS替换JS一样,迁移的成本小,兼容老代码,使用新特性。c++对于开发者的要求实在是太高了,除了c++之父,几乎没人敢说自己精通Cpp,无论是模板套模板能把人绕晕,还是析构之后四处崩溃的变量。让人始终不能专注于业务的开发与拓展,当然这也是高性能语言的魅力。Carbon 的诞生主要是因为Google 在 c++标准委员会里四处碰壁,有关协程以及其他相关的提案日常被否。所以Google 干脆另起炉灶,自己构建了一个号称c++继任者——Carbon,号称既能保证高性能,又能跟C++在同一文件中混编,还让开发者没有那么高的心智负担。非常适合一些陈年老项目的迁移。当然,目前它还处于相对不完善的状态,我们来看看它的使命愿景和一些相关的探索

  • 性能对标C++
  • 与 C++ 的无缝、双向互操作性,因此现有 C++ 堆栈中的任何库都可以采用 Carbon,而无需移植其余部分。
  • 不那么陡峭的学习曲线
  • 同样可以对地址进行low level的访问
  • 更高的安全性,更强的包管理机制

这个无缝双向互操作性其实就是针对cpp 的头文件生成AST(抽象语法树),遍历一遍,然后对应的在Carbon也生成一份对应的 Carbon AST,然后其他的就都交给clang,帮我们做好所有烦心的事情。

Safety

其实看起来,好像也就是简化版C++,但是,汲取了Rust 的相关经验,Carbon 在语言层面的安全性上也做了一些设计

  • 内存安全:不允许越界访问,取消null指针,取消未初始化的指针,禁止访问已经释放的地址
  • 类型安全:不允许用不正确的类型来访问有效的内存
  • 数据竞争安全:防止多个线程在没有“同步”的情况下对内存地址进行读写

也就是说,在编译阶段,就通过编译器来约束代码的行为,将人类的先验知识,用编译器行为来表达出来,从而保障基础的安全性。

Features

当然,它也拥有一些特性:

  • 函数入参可以类比成Const T&,是只读的

  • 指针提供间接访问和可变性

  • 通过包名来导入API

  • 默认情况下 class 是final的,abstract/base/final

  • 功能强大、经过定义检查的泛型以及接口实现 interface,类似 Rust 的trait

但是与Rust 相比,Carbon的第一优先级是保障迁移性,也即是保障和C++之间的互操作性,但是Rust 的第一优先级是安全性,从安全性上来说,Rust 要比 Carbon更安全。Rust 引入所有权机制来保障空悬指针和缓冲区溢出的问题。其实,C++提供的shared_ptr 和weak_ptr 组合起来也能实现这种所有权机制,即对于对象A,只能有其他一个对象拥有 A 的 shared_ptr,可以有多个其他对象拥有 A 的 weak_ptr。即永远不要用 shared_ptr 分享指针,分享要用 weak_ptr。但这种非强制性的要求并不能保障使用的安全性,因为你不能指望所有的合作者都和你一样在意这些细节。Carbon在这条路径上好像并没有超越Rust,只是比Cpp多了一些安全性的检查。

在ABI层面,Carbon 也还是没有做出什么革新,不能保障ABI的统一性

标准库目前也不够完善,亟需社区来补齐能力。

Example

Talk is Cheap, Show me the Code

// C++ code used in both Carbon and C++:
struct Circle {
  float r;
};

// Carbon exposing a function for C++:
package Geometry api;
import Cpp library "circle.h";
import Math;

fn PrintTotalArea(circles: Slice(Cpp.Circle)) {
  var area: f32 = 0;
  for (c: Cpp.Circle in circles) {
    area += Math.Pi * c.r * c.r;
  }
  Print("Total area: {0}", area);
}

// C++ calling Carbon:
#include <vector>
#include "circle.h"
#include "geometry.carbon.h"

auto main(int argc, char** argv) -> int {
  std::vector<Circle> circles = {{1.0}, {2.0}};
  // Carbon's `Slice` supports implicit construction from `std::vector`,
  // similar to `std::span`.
  Geometry::PrintTotalArea(circles);
  return 0;
}
// src/main.rs

#[cxx::bridge]
mod ffi {
    unsafe extern "C++" {
        include!("cxx-demo/include/blobstore.h");

        type BlobstoreClient;

        fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
    }
}

fn main() {
    let client = ffi::new_blobstore_client();
}


// include/blobstore.h

#pragma once
#include <memory>

class BlobstoreClient {
public:
  BlobstoreClient();
};

std::unique_ptr<BlobstoreClient> new_blobstore_client();

// src/blobstore.cc

#include "cxx-demo/include/blobstore.h"

BlobstoreClient::BlobstoreClient() {}

std::unique_ptr<BlobstoreClient> new_blobstore_client() {
  return std::unique_ptr<BlobstoreClient>(new BlobstoreClient());
}

// 或者使用autocxx
autocxx::include_cpp! {
    #include "url/origin.h"
    generate!("url::Origin")
    safety!(unsafe_ffi)
}

fn main() {
    let o = ffi::url::Origin::CreateFromNormalizedTuple("https",
        "google.com", 443);
    let uri = o.Serialize();
    println!("URI is {}", uri.to_str().unwrap());
}

Conclusion

总而言之,这是一个实验性的语言,目前还不够完善,但是等它慢慢在社区的完善下走向成熟后,是有机会能够成为真正的C++ 接班人的。目前无论是工具链、IDE、统一包管理等都还需要进一步的开发,但是它确实提出了一些美好的愿景,在后C++时代为程序猿提供了更简单的高性能选择。

In Google

在Google 内部,有两个叫Carbon的项目,一个是平台,另一个即为Carbon language,目前从infra内部来看,Carbon还是属于啥都没有的PPT阶段,有着一些美好愿景,但是项目目前还是处于非常初期的状态,亟需开源反哺。Chromium 从2020年开始就开始接触Rust团队,为cxx也产出了一些贡献,但是目前也没有看到源码中出现Rust 的身影。Mozilla 的firefox的插件系统是由Rust编写的,目前线上的代码占比大概是10% 左右。这样看来,Carbon在Google内部的发展也是任重道远。

For Us?

如果 Carbon 真的能够成长起来,对广大开发者来说也是一种福音。期待 Carbon 成长成熟起来,不论是作为 C++ 的有力补充,或者是像 Kotlin 一样找到自己的着力点,总之期待它先在Google内部活下来,才有机会真正挑战 C++ 的霸主地位。

Reference

clang.llvm.org/docs/Introd…

en.cppreference.com/w/c/languag…

www.foonathan.net/2022/07/car…

github.com/google/auto…

cxx.rs/concepts.ht…

www.chromium.org/Home/chromi…github.com/carbon-lang…

carbon.compiler-explorer.com/