Proof of Concept

Static Reflection System for C++

Clang-based code generator that automatically produces boilerplate from annotated C++ symbols. Eliminates manual registration and binding code for game engines by parsing AST and generating custom output based on developer annotations.

The Problem

Manual Boilerplate Hell

Game engines require extensive boilerplate: ECS system registration, Lua binding generation, component inspector integration, and serialization code. Developers spend significant time writing repetitive registration functions and keeping them synchronized with class definitions.

Automated Code Generation

Clang-powered tool that parses C++ source files, extracts annotated symbols, and generates all required boilerplate automatically. Developers annotate classes once with simple macros, and the system handles everything else at build time.

0
Runtime Reflection Cost
6
Annotation Macros
100%
Compile-Time Generation
AST
Clang Integration

Core Features

Zero Runtime Cost

All reflection and code generation happens at compile time. Annotation macros compile to nothing, leaving zero runtime overhead while providing maximum development convenience.

🔧

Clang AST Integration

Leverages Clang's powerful Abstract Syntax Tree parsing to achieve 100% accurate C++ symbol extraction, supporting complex templates and modern C++ features.

🎯

Flexible Code Generation

Generates custom boilerplate for ECS registration, Lua bindings, component inspector integration, and any other repetitive patterns your engine requires.

📝

Simple Annotation System

Six intuitive macros (CGCLASS, CGMEMBER, CGMETHOD, etc.) that compile to nothing but provide rich metadata for code generation without polluting your codebase.

🔄

Build System Integration

Runs as a pre-build step, taking your project files and output directory as arguments. Seamlessly integrates with any build system that can run executables.

🎮

Game Engine Focused

Specifically designed for game engine development patterns: ECS systems, component inspection, Lua scripting integration, and automatic registration workflows.

Technical Architecture

The system uses Clang's LibTooling to parse C++ source files, extract annotated symbols, and generate custom boilerplate code.

Code Generation Pipeline

Annotated C++
Clang AST
Symbol Extraction
Generated Code
Simple Annotation Example
// Include the macro definitions (compile to nothing)
#include "CodeGenerator.hpp"

CGCLASS(LuaType="Player", LuaComponent, CanAccessComponents)
class Player {
public:
    CGCONSTRUCTOR(LuaConstructor)
    Player(float health, float speed) : health_(health), speed_(speed) {}

    CGMETHOD(LuaFunction)
    void TakeDamage(float amount) {
        health_ -= amount;
        if (health_ <= 0) Die();
    }

    CGVARIABLE(LuaVariable)
    float health_;

    CGVARIABLE(LuaEntity)
    EntityID entity_id_;

private:
    CGVARIABLE(LuaVariable)
    float speed_;

    void Die() { /* implementation */ }
};

Clang AST Parsing Implementation

The core of the system uses Clang's LibTooling to traverse the Abstract Syntax Tree and extract annotated symbols:

Main Processing Loop
int main(int argc, char *argv[]) {
    // Parse Visual Studio project file to get headers
    XmlParser xmlParser{solution_file};
    const auto headers = xmlParser.GetAllHeaders();

    // Configure Clang arguments
    std::vector<std::string> args{};
    args.emplace_back("clang");
    args.emplace_back("-fsyntax-only");  // AST only, no compilation
    args.emplace_back("-std=c++17");
    args.emplace_back("-IC:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.38.33130/include");

    // Create compilation database and run Clang tool
    SimpleOptionParser optionParser{args, headers};
    clang::tooling::ClangTool tool{optionParser, optionParser.getAllFiles()};

    // Reserve space for AST parsers
    ASTFileParser::Reserve(headers.size());

    // Parse all files and extract symbols
    tool.run(clang::tooling::newFrontendActionFactory<ASTFrontendAction>().get());

    // Generate code based on extracted symbols
    FileGenerator generator{};
    generator.ParseClass = HandleClass;
    generator.ParseMember = HandleMember;
    generator.ParseMethod = HandleMethod;

    for (const auto &parser: ASTFileParser::GetParsers()) {
        generator.Parse(parser);
    }

    // Write generated files
    FileGenerator::WriteFiles();
    return 0;
}

Generated Code Examples

The system automatically generates different types of boilerplate based on the annotations it finds.

ECS System Registration (_Gen_Engine.cpp)
void HandleClass(FileGenerator &fileGenerator, const Class &class_) {
    // Automatically register ECS systems
    if (FileGenerator::GetProperty(class_.properties, "System") != nullptr) {
        auto& file = fileGenerator.files["_Gen_Engine.cpp"];
        file.includes.insert("core/engine.hpp");
        file.includes.insert(class_.path);

        auto& function = file.functions["bee::EngineClass::GeneratedInitialize"];

        std::string line = "ecs_->AddSystem<" + class_.fullNamespace + ">();";
        function.body.emplace_back(line);
    }

    // Register components with inspector
    auto property = FileGenerator::GetProperty(class_.properties, "InspectableComponent");
    if (property != nullptr) {
        auto& file = fileGenerator.files["_Gen_Engine.cpp"];
        file.includes.insert("core/engine.hpp");
        file.includes.insert(class_.path);

        auto& function = file.functions["bee::EngineClass::GeneratedInitialize"];

        std::string line = "ecs_->RegisterComponent<" + class_.fullNamespace + ">(\"" + class_.name + "\");";
        function.body.emplace_back(line);
    }
}
Lua Binding Generation (_Gen_LuaBindings.cpp)
// Generate Lua type registration
property = FileGenerator::GetProperty(class_.properties, "LuaType");
if (property != nullptr) {
    auto& source = fileGenerator.files["_Gen_LuaBindings.cpp"];
    auto& function = source.functions["CreateLuaBindings_Gen_"];

    std::string tableName = "L";

    // Register in component table if it's a component
    if (FileGenerator::GetProperty(class_.properties, "LuaComponent") != nullptr) {
        tableName = "componentTable";
        static bool first = true;
        if (first) {
            first = false;
            function.body.emplace_back("sol::table componentTable = L.create_table(\"Components\");");
        }
    }

    // Create usertype binding
    std::string line = "sol::usertype<" + class_.fullNamespace + "> " +
                      std::get<std::string>(property->value) + " = " + tableName +
                      ".new_usertype<" + class_.fullNamespace + ">(\"" +
                      std::get<std::string>(property->value) + "\");";
    function.body.emplace_back(line);

    // Generate constructor bindings
    std::deque<const Function*> constructors;
    for (auto& member : class_.functions) {
        if (FileGenerator::GetProperty(member.properties, "LuaConstructor") != nullptr){
            constructors.emplace_back(&member);
        }
    }

    // Bind constructors
    if (!constructors.empty()) {
        line = std::get<std::string>(property->value) + "[sol::meta_function::construct] = sol::constructors<";
        for (const auto& constructor : constructors) {
            line += constructor->GetNamespace() + "(/* parameters */);";
        }
        function.body.emplace_back(line);
    }
}

Annotation System

Available Macros

The system provides six core macros for annotating C++ symbols:

Macro Definitions (CodeGenerator.hpp)
// These macros compile to nothing - zero runtime cost
#define CGCLASS(...)      // Annotate classes
#define CGMEMBER(...)     // Annotate class members
#define CGMETHOD(...)     // Annotate methods
#define CGCONSTRUCTOR(...)// Annotate constructors
#define CGVARIABLE(...)   // Annotate variables
#define CGFUNCTION(...)   // Annotate functions
🏗️

CGCLASS Properties

LuaType: Register class in Lua
System: Auto-register ECS system
InspectableComponent: Add to inspector
CanAccessComponents: Generate component accessors

🔧

CGMETHOD/CGFUNCTION

LuaFunction: Expose to Lua scripting
LuaConstructor: Enable Lua instantiation
Custom properties can be added for specific code generation needs

📊

CGVARIABLE Properties

LuaVariable: Expose variable to Lua
LuaEntity: Mark as entity reference
Future: Serialization, editor exposure, validation

🎯

Extensible System

Properties are completely customizable. The HandleClass/HandleMethod functions can be modified to generate any type of boilerplate based on custom properties.

Build System Integration

CMake Integration Example

The code generator builds as a standalone executable and integrates with any build system:

CMakeLists.txt Configuration
cmake_minimum_required(VERSION 3.20)
project(CodeGenerator)

set(CMAKE_CXX_STANDARD 17)

# Define source files
set(SOURCE_FILES
    src/XmlParser.cpp
    src/FileGenerator.cpp
    src/FileParser.cpp
    src/SimpleOptionParser.cpp
    main.cpp
)

add_executable(CodeGenerator ${SOURCE_FILES})

# Add Clang/LLVM as subdirectory
set(LLVM_ENABLE_PROJECTS "clang" CACHE STRING "" FORCE)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/External/llvm/llvm)

# Link against Clang tooling
target_link_libraries(CodeGenerator PRIVATE clangTooling pugixml)

# Include Clang headers
target_include_directories(CodeGenerator PRIVATE
    ${LLVM_EXTERNAL_CLANG_SOURCE_DIR}/include
    ${CMAKE_CURRENT_BINARY_DIR}/External/llvm/llvm/tools/clang/include
    ${CMAKE_CURRENT_SOURCE_DIR}/External/llvm/llvm/include
)

Usage in Build Pipeline

  • Pre-Build Step: Run CodeGenerator.exe before compiling your main project
  • Input: Visual Studio project file (.vcxproj) and output directory
  • Output: Generated .cpp/.hpp files (_Gen_Engine.cpp, _Gen_LuaBindings.cpp, etc.)
  • Integration: Include generated files in your main project's compilation
  • Workflow: Annotate → Generate → Compile → Link
Command Line Usage
# Run code generation before build
./CodeGenerator.exe MyProject.vcxproj ./generated_output/

# Generated files are ready for compilation
# _Gen_Engine.cpp      - ECS system registration
# _Gen_LuaBindings.cpp - Lua type bindings
# _Gen_LuaBindings.hpp - Lua binding headers

Current Status & Future Development

Completed Proof of Concept

The current system successfully demonstrates Clang AST parsing, symbol extraction, and code generation for ECS and Lua binding use cases. It's functional and ready for integration into game engine projects that need automated boilerplate generation.

Ongoing Redesign

Working on a more flexible redesign that exposes extracted symbols to scripting languages, allowing users to customize generated output without recompiling the tool. This will enable broader applications beyond the current ECS/Lua focus.

Planned Improvements

  • Scripted Generation: Expose symbol data to Lua/Python for custom code generation
  • Template Engine: User-configurable templates for different output formats
  • IDE Integration: Visual Studio and other IDE plugins for seamless workflow
  • Broader Applications: Serialization, networking, validation code generation
  • Performance Optimization: Incremental parsing and caching for large projects
  • Cross-Platform: Better support for GCC and other compiler toolchains
POC
Current Status
C++17
Language Standard
Clang
AST Parser
Extensibility

Learn More

This project represents a deep dive into compiler technology and practical code generation. The technical blog provides detailed implementation notes and lessons learned from building a Clang-based tool for game engine development.