cppfront Documentation

repository·main·Indexed 27 days ago

https://github.com/hsutter/cppfront

cppfront is a compiler that translates an experimental C++ 'syntax 2' (Cpp2) into standard C++ (Cpp1). It serves as a prototyping tool for future C++ features, implementing concepts such as the spaceship operator (<=>), reflection, metaclasses, pattern matching, and advanced parameter passing (in, inout, out, move, forward). Cpp2 acts as a skin for C++ that maintains full compatibility with C++20 (or higher) compilers, libraries, and tools.

Tokens
22.1K
Snippets
56
Records
119
Agent score
86%

What's inside cppfront

  1. Overview of cppfront and Cpp2

    main

    cppfront is a compiler that translates an experimental C++ 'syntax 2' (Cpp2) into today's standard 'syntax 1' (Cpp1).

    Key characteristics:

    • Purpose: It is designed to prototype and prove out concepts for the evolution of the ISO C++ standard, rather than being a C++ successor.
    • Compatibility: Cpp2 is a 'skin' for C++ that uses Standard C++ modules and concept requirements. It works with all existing C++20 (or higher) compilers, libraries, and tools with zero overhead and no changes required to existing ecosystems.
    • Integration: It does not replace your standard C++ compiler or tools; it generates standard C++ code that can be used with them.
  2. Understand implemented features in cppfront

    main

    cppfront implements several key features derived from ISO C++ proposals, including:

    • Spaceship operator (<=>): Supports consistent comparison, including chained comparisons.
    • Reflection, generation, and metaclasses: Implements generative C++ concepts (though writing custom metafunctions is a planned future feature).
    • Parameter passing: Implements in, inout, out, move, and forward semantics, including a unified operator= for user-defined types.
    • Pattern matching: Provides support for is, as, and basic inspect expressions.
  3. Understand Cpp2 and cppfront

    main

    Cpp2

    Cpp2 ("C++ syntax 2") is a simplified and safer syntax for writing ordinary C++ types, functions, and objects. It is designed to be a "skin" for C++ that makes best practices the default and prevents common security pitfalls (like type-unsafe casts) without breaking backward compatibility with standard C++.

    cppfront

    cppfront is a compiler that translates Cpp2 syntax into standard C++ (Cpp1) syntax. This allows you to use Cpp2 in any existing C++20 or higher project by simply renaming source files to .cpp2 and adding a translation step to your build system. The resulting C++ code is fully compatible with all existing C++ compilers, debuggers, build systems, and sanitizers.

  4. Bring namespace names into scope with `using` statements

    main

    You can use #!cpp using statements to bring names from a namespace into the current scope, avoiding the need for explicit qualification.

    • Specific name: #!cpp using a_namespace::a_name; brings only a_name into the current scope.
    • Wildcard (all names): #!cpp using a_namespace::_; brings all names from a_namespace into the current scope using the _ wildcard.
    //  A namespace to put all the names provided by a widget library
    widgetlib: namespace = {
        widget: type = { /*...*/ }
        // ... more things ...
    }
    
    main: () = {
        //  Explicit name qualification
        w: widgetlib::widget = /*...*/;
    
        {
            //  Using the specific name, no widgetlib:: qualification needed
            using widgetlib::widget;
            w2: widget = /*...*/;
            // ...
        }
    
        {
            //  Using the whole namespace, no widgetlib:: qualification needed
            using widgetlib::_;
            w3: widget = /*...*/;
            // ...
        }
    
        // ...
    }
  5. Use unnamed function expressions (lambdas)

    main

    Cpp2 does not have a separate

    // Using unnamed functions in standard library algorithms
    std::ranges::for_each( a, :(x) = std::cout << x );
    
    std::ranges::transform( a, std::back_inserter(b), :(x) = x+1 );
    
    // Capturing local variables (e.g., waldo$) is done by writing them in the body
    where_is = std::ranges::find_if( b, :(x) = x == waldo$ );
  6. Compile Cpp2 code to C++ using cppfront

    main

    Use the cppfront CLI to transpile .cpp2 files into standard C++20 files.

    To produce a C++ file with the standard library available (using either modules or headers depending on compiler support), use the -p (or --pure-cpp2) flag. This flag implies either -im (--import-std) or -in (--include-std).

    cppfront hello.cpp2 -p
  7. Implement inheritance using `#!cpp this`

    main

    In Cpp2, base types are declared as members named #!cpp this. There is no separate base list or member initializer list; base and member subobjects are declared and initialized within the same type body.

    Because they are declared in the same place, you can interleave base and member declarations. Cpp2 guarantees safe initialization in the declared order, allowing you to declare a data member before a base subobject so that the member naturally outlives the base.

    derived: type
    = {
        // 'this' is-an 'abstract_base'
        this: abstract_base;
    
        // ...
    }
  8. Use Preconditions, Postconditions, and Assertions in Cpp2

    main

    Cpp2 supports three types of contracts to ensure code correctness:

    • Preconditions (pre): Evaluated before entering a function body. Use pre(condition, "message") to specify a requirement and an optional error message.
    • Postconditions (post): Evaluated immediately before a normal return from a function body. If the function exits via an exception, postconditions are not evaluated. Use the $ syntax to capture values at the time of function entry (e.g., post(container.ssize() == container.ssize$ + 1)).
    • Assertions (assert): Evaluated when control flow passes through them within a function body.

    Contracts can be assigned to specific contract groups using angle brackets: pre<group_name>(condition) or assert<group_name>(condition). If no group is specified, the contract belongs to the default group (accessible as cpp2_default in Cpp1 code).

    insert_at: (container, where: int, val: int)
        pre<bounds_safety>( 0 <= where <= container.ssize(), "position (where)$ is outside 'container'" )
        post              ( container.ssize() == container.ssize$ + 1 )
    = {
        _ = container.insert( container.begin()+where, val );
    }
  9. Declare thread-safe local static variables with `#!cpp static`

    main
    Using #!cpp static as the first token of a local variable declaration at function scope creates a 'magic static' (function-local static). The variable is initialized thread-safely the first time the line is executed.
  10. Write a Cpp2 program

    main

    Cpp2 uses a consistent, context-free syntax for declarations: _name_ : _kind_ = _statement_. This eliminates the 'vexing parse' problem found in standard C++.

    Key features include:

    • Order-independence: No forward declarations are required; functions can call each other regardless of definition order.
    • String Interpolation: Use #!cpp "text (variable)$!" to embed variables directly into strings. This supports standard C++ format specifications.
    • Default Safety: Subscript access (e.g., vector[0]) is bounds-checked by default.
    • Parameter Passing: The default is in (read-only). Other modes include inout, copy, out, move, and forward.
    • Standard Library: The full C++ standard library is available by default when using specific compiler flags.
    main: () = {
        words: std::vector = ( "Alice", "Bob" );
        hello( words[0] );
        hello( words[1] );
    }
    
    hello: (msg: std::string_view) = {
        std::cout << "Hello, (msg)$!\n";
    }
  11. Build and run the generated C++ file

    main

    Once cppfront has generated a .cpp file, you can compile it with any recent C++20 compiler. You must provide the path to the cppfront/include directory using the appropriate include flag for your compiler.

    MSVC (Visual Studio 2019 16.11+)

    cl hello.cpp -std:c++20 -EHsc -I CPPFRONT_INCLUDE

    GCC (GCC 10+)

    g++ hello.cpp -std=c++20 -ICPPFRONT_INCLUDE -o hello

    Clang (Clang 12+)

    clang++ hello.cpp -std=c++20 -ICPPFRONT_INCLUDE -o hello
  12. Define the `main` entry point

    main

    The main function serves as the program entry point. It can be defined in two ways:

    1. No parameters: #!cpp main: ()
    2. With args parameter: #!cpp main: (args)

    When using (args), the parameter is of implicit type cpp2::args_t. This type behaves like a #!cpp const std::array<std::string_view> and performs zero heap allocations. You can access raw C/C++ parameters via args.argc and args.argv.

    Return types:

    • #!cpp void (default): The compiled Cpp1 code will return int automatically.
    • #!cpp int: If no #!cpp return is present, it defaults to #!cpp return 0;.
    • Other types supported by your Cpp1 compiler as nonstandard extensions.
    // Print out command line arguments, then invoke
    // a Qt event loop for a non-UI Qt application
    main: (args) -> int
    = {
        for args do (arg) {
            std::cout << arg << "\n";
        }
    
        app: QCoreApplication = (args.argc, args.argv);
        return app.exec();
    }