The Circle Compile is a modern C++ compiler that extends C++17 with advanced metaprogramming capabilities through a built-in, bit-accurate C++ interpreter. It provides same-language reflection and introspection, enabling powerful compile-time code generation in an imperative style. Circle compiler Git
Similarities and differences with C++
Similarities
- A superset of C++: Circle is a language extension of C++. It supports existing C++ code and all its core features, including name lookup, overload resolution, and the preprocessor.
- Familiar syntax: Because Circle operates within the C++ syntax, developers with C++ knowledge can learn and use Circle’s new features quickly.
- Uses C++ libraries: Since the interpreter is bit-accurate with the C++ ABI, Circle’s metaprogramming can use standard C++ libraries. For example, a developer can run
std::coutduring compilation.
Differences
- Metaprogramming approach: C++ relies on template metaprogramming, which uses templates and template specialization to compute and generate code at compile time. Circle offers a more intuitive, imperative approach with its integrated interpreter, allowing developers to use standard control flow and language features to create code programmatically.
- Compile-time vs. runtime: In standard C++, compile-time computation is mostly limited to
constexprfunctions. The Circle interpreter can run any function at compile time if its definition is available, collapsing the traditional separation between compilation and execution. - Integrated tooling: Circle adds features like integrated reflection and introspection keywords (e.g.,
@member_count,@type_string), which expose type information from the compiler to metaprograms. In contrast, standard C++ reflection proposals are still under development.
Metaprogramming with the embedded C++ interpreter
The interpreter is the foundation for Circle’s metaprogramming, using the special @meta keyword to run code at compile time.
- Integrated interpreter: The compiler contains a “second backend” for executing code, not just compiling it. The interpreter is “bit-accurate” with the host environment, allowing it to evaluate C++ expressions and functions during translation.
- Meta scopes: The
@metakeyword specifies that the following statement or block should be executed at compile time. This allows developers to use standard C++ constructs likeifandforloops to control code generation. - Same-language reflection: Rather than relying on a complex API for generating Abstract Syntax Tree (AST) nodes, Circle uses “same-language reflection.” Statements inside a meta scope, like a
@meta forloop, are programmatically deposited into the final AST for the compiled program. - Introspection keywords: Circle provides built-in keywords for querying type information at compile time, such as counting data members, getting their names, or iterating over enum values. These queries, combined with meta control flow, are used to generate code based on a type’s structure.
SafeC++ using Circle
SafeC++ is an extension of Circle that aims to bring Rust’s rigorous memory safety guarantees to C++ while maintaining backward compatibility. It is not a new language but an opt-in extension. SafeC++ Proposal - D3390
Key features of SafeC++:
- New Intermediate Representation (IR): SafeC++ adds a middle-end IR subsystem that enables rigorous memory safety checks, including initialization analysis and borrow checking.
- Opt-in safety: Existing C++ code compiles normally. The user explicitly enables safety features for specific parts of the codebase, often using a
safespecifier for functions. - Borrow checking: The system employs a borrow checker to prevent common memory errors like use-after-free and iterator invalidation at compile time.
- Checked references: SafeC++ introduces new reference types,
T^(mutable borrow) andconst T^(shared borrow), that are tracked by the borrow checker. - Safe-specifier: Functions can be marked
safeto indicate that they abide by the new safety rules and have soundness preconditions. Conversely, unsafe operations must be placed withinunsafeblocks. - Safe standard library: A modified standard library provides safer implementations of containers and algorithms. For instance, safe slice iterators replace raw
begin()andend()iterators. - Choice types and pattern matching: It introduces “choice types” (similar to Rust’s enums) and pattern matching to safely handle optional values, preventing common programming mistakes.