Staged metaprogramming in C++ for C/C++

About gencpp


An attempt at simple staged metaprogramming for C/C++. Reflect and generate code for your codebase at runtime!

Code_-_Insiders_2024-12-15_22-57-58.gif Code_-_Insiders_2024-12-15_23-04-07.gif

The library API is a composition of code element constructors, and a non-standards-compliant single-pass C/C++ parser.
These build up a code AST to then serialize with a file builder, or can be traversed for staged-reflection of C/C++ code.

This code base attempts follow the handmade philosophy.
Its not meant to be a black box metaprogramming utility, it should be easy to integrate into a user's project domain.

Documentation

Notes

This project is still in development (very much an alpha state), so expect bugs and missing features.
See issues for a list of known bugs or todos.

The library can already be used to generate code just fine, but the parser is where the most work is needed. If your C++ isn't "down to earth" expect issues.

A natvis and natstepfilter are provided in the scripts directory (its outdated, I'll update this readme when its not).
Minor update: I've been using RAD Debugger with this and the code structures should be easy to debug even without natvis.

Usage

A metaprogram is built to generate files before the main program is built. We'll term runtime for this program as GEN_TIME. The metaprogram's core implementation are within gen.hpp and gen.cpp in the project directory.

gen.cpp `s main() is defined as gen_main() which the user will have to define once for their program. There they may reflect and/or generate code.

In order to keep the locality of this code within the same files the following pattern may be used (although this pattern isn't the best to use):

Within program.cpp :

#ifdef GEN_TIME
#include "gen.hpp"

...

u32 gen_main()
{
    gen::Context ctx;
    gen::init(& ctx);
    ...
    gen::deinit(& ctx);
    return 0;
}
#endif

// "Stage" agnostic code.

#ifndef GEN_TIME
#include "program.gen.cpp"

    // Regular runtime dependent on the generated code here.
#endif

The design uses a constructive builder API for the code to generate.
The user is provided Code objects that are used to build up the AST.

Example using each construction interface:

Upfront

Validation and construction through a functional interface.

CodeTypename t_uw           = def_type( name(usize) );
CodeTypename t_allocator    = def_type( name(allocator) );
CodeTypename t_string_const = def_type( name(char), def_specifiers( args( ESpecifier::Const, ESpecifier::Ptr ) ));

CodeStruct header;
{
    CodeVar  num       = def_variable( t_uw,        name(Num) );
    CodeVar  cap       = def_variable( t_uw,        name(Capacity) );
    CodeVar  mem_alloc = def_variable( t_allocator, name(Allocator) );
    CodeBody body      = def_struct_body( args( num, cap, mem_alloc ) );

    header = def_struct( name(ArrayHeader), { body });
}

Parse

Validation through ast construction.

CodeStruct header = parse_struct( code(
    struct ArrayHeader
    {
        usize     Num;
        usize     Capacity;
        allocator Allocator;
    };
));

Untyped

No validation, just glorified text injection.

Code header = code_str(
    struct ArrayHeader
    {
        usize     Num;
        usize     Capacity;
        allocator Allocator;
    };
);

name is a helper macro for providing a string literal with its size, intended for the name parameter of functions.
code is a helper macro for providing a string literal with its size, but intended for code string parameters.
args is a helper macro for providing the number of arguments to varadic constructors.
code_str is a helper macro for writing untyped_str( code( <content> ))

All three construction interfaces will generate the following C code:

struct ArrayHeader
{
    usize     Num;
    usize     Capacity;
    allocator Allocator;
};

Note: The formatting shown here is not how it will look. For your desired formatting its recommended to run a pass through the files with an auto-formatter.
(The library currently uses clang-format for formatting, beware its pretty slow...)

Building

See the scripts directory.

Read more
Filters

Recent Activity

Recently got my staged metaprogramming project for C/C++ generating a C11 version. Been also inspired by the sokol libraries to setup a context struct so everything can be configured from there (allocators, viewing state, etc).
Goal now is to improve error handling, code path simplification, and just using it more on some projects.
&gencpp
https://github.com/Ed94/gencpp

View original message on Discord