Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

C++  Module 22 – C++ for game development

C++ in Unreal, Godot and custom engines

How Unreal Engine, Godot and custom game engines use C++: reflection macros and generated code, garbage collection, and builds without exceptions or RTTI.

  • Intermediate
  • 15 minutes
  • Examples run with Clang 21.0.0, GCC 16.2.0 and Python 3.14.8
  • By MySmartCoPilot
Free core, Pro depth: 6 of 15 minutes free
Free: Every engine keeps a description of its classes · Unreal generates the registration code, Godot writes it by hand · A registry you can run shows what a table cannot check · Practise: two ways to build the table. With a pass: 2 in-depth parts and 3 interview answers, about 9 minutes.

What you will learn

  • Explain what game engines add to standard C++ and what that description drives
  • Recognise how Unreal Engine and Godot register classes and their members
  • Build a property table with macros, and name the mistake it cannot catch
  • Compare a table kept by hand, a generated one and one built by C++26 reflection
  • Reproduce a pointer a garbage collector cannot see, and replace RTTI and exceptions

Before you start

What you’ll learn · Before you start

What you will learn

  • Explain what game engines add to standard C++ and what that description drives
  • Recognise how Unreal Engine and Godot register classes and their members
  • Build a property table with macros, and name the mistake it cannot catch
  • Compare a table kept by hand, a generated one and one built by C++26 reflection
  • Reproduce a pointer a garbage collector cannot see, and replace RTTI and exceptions

Before you start

On this page

A game editor shows a player’s name, health and speed in a panel, a save file stores them, and a console command can change them, yet none of that code was written for the Player class. It all reads a description of the class that the engine keeps, something standard C++ long lacked. C++ in Unreal is written with macro: A name defined with #define that the preprocessor replaces by its text wherever it appears later in the file, as SQ(a + 1) becomes ((a + 1) * (a + 1)). A macro has no type and no scope, and its arguments are pasted as text. More in “C++ preprocessor and feature-test macros” such as UCLASS, UPROPERTY and UFUNCTION, which a separate program, the Unreal Header Tool, reads before the compiler runs, to generate the code that registers each class. Godot’s C++ bindings, godot-cpp, register a class with the GDCLASS macro and a _bind_methods function. Custom engines keep a table of their own, written by hand or generated.

Engines also bring their own containers, and decide module by module whether exceptions and run-time type information (RTTI): The description of its own type that the compiler keeps for each class with virtual functions, which dynamic_cast's checks and typeid read while the program runs. With -fno-rtti GCC and Clang leave it out and reject those uses. More in “C++ casts and run-time type information (RTTI)” may be used. C++26 reflection lets the compiler build such a table from the class itself. You will run a registry that shows a class it never names, and see the mistake no such table catches.

Every engine keeps a description of its classes

Game engines are built with ordinary C++ compilers, and each adds that description its own way. Called reflection, it drives the editor’s property panels, saving and loading, scripting, network replication and, in Unreal Engine, garbage collector: A part of some languages' run-time systems that finds memory a program no longer uses and frees it automatically, at moments the program does not choose. Standard C++ has none: objects are destroyed at points the code determines. More in “What C++ is used for today and how to learn it”. The three kinds of engine build it, and free their objects, in different ways:

Engine Classes made visible by Objects freed by
Unreal Engine Macros a header tool reads A garbage collector
Godot (godot-cpp) GDCLASS and a binding function Reference counts, or by hand
Custom engines A table, by hand or generated Pools, handles or counts

Follow the three paths into the compiler. Each ends in the same description, the only thing the engine’s generic code reads:

Three ways into a class description: markers read by a header tool, a list written in code, or C++26 annotations read by the compiler.Markers:UCLASS,UPROPERTYHeader tool(UHT)GeneratedC++A listin codeC++26annotationsC++ compilerThe class'sdescriptionEditor, saving,scripts, networking,garbage collectionfirstby handreflection

Three ways a class's description is built, and what the engine does with it

Text description of the diagram

The diagram shows three paths into the C++ compiler, which all end in the same class description.

  1. Unreal's path: markers such as UCLASS and UPROPERTY sit in the class. First, a header tool, the Unreal Header Tool (UHT), reads them and writes generated C++; the compiler then compiles the class and the generated code together.
  2. A list in code, written by hand, such as the REFLECT macros of this lesson or Godot's _bind_methods, goes to the compiler with the class.
  3. C++26 annotations on the members are read by the compiler itself, with reflection.

From the compiler comes the class's description, its properties with their types and flags, which the engine uses for editor panels, saving, scripts, networking and garbage collection.

Not affiliated

Based on public information about Epic Games. MySmartCoPilot is not affiliated with Epic Games, and Epic Games’s real Unreal Engine internals may differ from what this lesson describes.

Checkpoint

Check: What engines add to standard C++

  1. Question 1 of 2 Which jobs does an engine's description of its classes drive?

    Choose every answer that is right.

    Show the answer: Question 1 of 2

    Answer:

    • Scripting and network replication
    • Saving and loading
    • The editor's property panels

    Called reflection, the description drives the editor's property panels, saving and loading, scripting, network replication and, in Unreal Engine, garbage collection. The compiler compiles the class without it.

  2. Question 2 of 2 A team's build machines run only the C++ compiler, with no other program before it. Which ways of building a class's description can they use as they are?

    Choose every answer that is right.

    Show the answer: Question 2 of 2

    Answer:

    • C++26 annotations, on a compiler that implements reflection
    • A list written in code, such as REFLECT macros or a _bind_methods function

    Of the three paths into the compiler, only the markers need a program of their own first: the Unreal Header Tool writes the generated code that the compiler then builds. A list in code and C++26 annotations go straight to the compiler.

Unreal generates the registration code, Godot writes it by hand

C++ in Unreal: macros that a tool reads

Epic’s documentation calls the step a preprocessing of the headers, done by the UnrealHeaderTool, or UHT. A class derived from UObject carries UCLASS() above it and GENERATED_BODY() inside it, and its header ends its includes with the generated header, such as MyObject.generated.h. A function or property without such a marker is missing from the class’s description, and the UObject system built on that description provides garbage collection, serialization, editor integration, run-time type information and network replication. C++ in Unreal follows other rules too: objects are never created with new, the coding standard says to avoid standard containers and strings except in interop code, and each module’s build rules switch exception handling and run-time type information on or off.

Godot: classes registered in _bind_methods

godot-cpp is the Godot project’s own C++ binding for its GDExtension system. Unlike a C++ module, which is compiled into the engine, an extension built with it is a library of its own, so changing it never means rebuilding Godot. A class starts with GDCLASS(GDExample, Sprite2D), and its static _bind_methods function, which Godot calls once, registers what scripts and the editor may use: ClassDB::bind_method(D_METHOD("set_speed", "p_speed"), &GDExample::set_speed) for a method and ADD_PROPERTY for a property backed by a getter and a setter. GDREGISTER_CLASS then makes the class known, and Object::cast_to<T> converts between classes the way dynamic_cast would.

Not affiliated

Based on public information about the Godot project. MySmartCoPilot is not affiliated with the Godot project, and the Godot project’s real engine internals may differ from what this lesson describes.

Step through one class’s way into each engine, Unreal’s at build time and Godot’s when the extension loads:

Interactive · step by step One class's way into the engine, in Unreal and in Godot Step through Unreal's build first, from the markers to the UObject system, then Godot's registration at run time.
One class's way into the engine, in Unreal and in Godot, step 8 of 8. Showing The class's header, Unreal Header Tool, C++ compiler, MyObject.generated.h (registers marked members), The class's description (marked members only), The UObject system, _bind_methods() (called once by Godot), ClassDB (GDExample and set_speed). In focus: _bind_methods(), ClassDB.readswritesincludedregistersThe class's headerUCLASS, UPROPERTYUnreal Header Toolbefore the compilerC++ compilerthe normal buildMyObject.generated.hregisters marked membersThe class's descriptionmarked members onlyThe UObject systemreads the description_bind_methods()called once by GodotClassDBGDExample and set_speedset_speed

The drawing shows the whole scene; its 8 steps are listed below.

Every step as text
  1. Markers in the header. A class derived from UObject carries UCLASS() above it and GENERATED_BODY() inside it, and marks the members the engine should know with UPROPERTY and UFUNCTION.
  2. The header tool runs first. Before the compiler runs, the Unreal Header Tool, UHT, reads the headers. Epic's documentation calls this step a preprocessing of the headers.
  3. Generated code. UHT writes the generated header, such as MyObject.generated.h, which the class's header includes last of all its includes. It holds the code that registers the class and its marked members.
  4. Compiled together. The compiler then builds the class and the generated code together, in the normal build.
  5. Only marked members. Only marked members make it into the class's description. A member without UPROPERTY is not in it, so the engine cannot see it.
  6. What the description runs. The UObject system built on that description provides garbage collection, serialization, editor integration, run-time type information and network replication.
  7. Godot: no header tool. godot-cpp has no tool that reads headers. A class starts with GDCLASS(GDExample, Sprite2D), GDREGISTER_CLASS makes it known, and Godot calls its static _bind_methods() once.
  8. Methods and properties. Inside it, ClassDB::bind_method(D_METHOD("set_speed", "p_speed"), &GDExample::set_speed) registers a method, and ADD_PROPERTY a property backed by a getter and a setter. Scripts and the editor can then use them.

Checkpoint

Check: How Unreal and Godot register classes

  1. Question 1 of 2 An Unreal class derived from UObject has a member with no UPROPERTY marker. What does the engine's description of the class say about it?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Nothing, because only the members marked with the macros are listed

    Members without a marker are missing from the class's description, and everything the UObject system does, from saving to garbage collection, works from that description.

  2. Question 2 of 2 Why does changing a godot-cpp extension never mean rebuilding Godot?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: The extension is a library of its own, loaded by Godot through GDExtension

    godot-cpp is the official C++ binding for GDExtension. A C++ module is compiled into the engine, but an extension is a library of its own, so it can change without touching the engine.

A registry you can run shows what a table cannot check

Custom engines often start with a few macros that turn a list of members into a table. This registry is a small, working example:

A property registry built with macros

registry.hpp

// An engine-style property registry built with macros. A class lists its properties once, between REFLECT_BEGIN and
// REFLECT_END, and the macros turn the list into a table: each property's name, kind, flags and where it lives in an
// object. Generic code reads the table to show, edit and save objects of any class it has never seen.
#pragma once

#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <string_view>
#include <vector>

enum class Kind { Int, Float, Text };
enum Flags : unsigned { Editable = 1, Saved = 2 };

struct Property {
    const char* name;
    Kind kind;
    unsigned flags;
    void* (*address)(void* object);  // the member's address inside an object of the class
};

struct ClassInfo {
    const char* name;
    std::vector<Property> properties;
};

template <typename T>
constexpr Kind kind_of();
template <>
constexpr Kind kind_of<int>() { return Kind::Int; }
template <>
constexpr Kind kind_of<float>() { return Kind::Float; }
template <>
constexpr Kind kind_of<std::string>() { return Kind::Text; }

// REFLECT_BEGIN opens a function that builds the class's table once; each REFLECT_PROPERTY adds one row.
#define REFLECT_BEGIN(Class)                          \
    inline const ClassInfo& class_info_of(const Class*) { \
        using Self = Class;                           \
        static const ClassInfo info{#Class, {
#define REFLECT_PROPERTY(member, flags)                                     \
    Property{#member, kind_of<decltype(Self::member)>(), flags,              \
             [](void* object) -> void* { return &static_cast<Self*>(object)->member; }},
#define REFLECT_END() \
    }};               \
    return info;      \
    }

template <typename T>
const ClassInfo& class_info() {
    return class_info_of(static_cast<const T*>(nullptr));
}

// ---- generic code: it knows the table, not the classes ------------------------------------------------------------

inline std::string text_of(const Property& p, void* object) {
    void* at = p.address(object);
    switch (p.kind) {
        case Kind::Int: return std::to_string(*static_cast<int*>(at));
        case Kind::Float: {
            char buffer[32];
            std::snprintf(buffer, sizeof buffer, "%g", double(*static_cast<float*>(at)));
            return buffer;
        }
        case Kind::Text: return *static_cast<std::string*>(at);
    }
    return {};
}

inline void set_from_text(const Property& p, void* object, const std::string& text) {
    void* at = p.address(object);
    switch (p.kind) {
        case Kind::Int: *static_cast<int*>(at) = int(std::strtol(text.c_str(), nullptr, 10)); break;
        case Kind::Float: *static_cast<float*>(at) = std::strtof(text.c_str(), nullptr); break;
        case Kind::Text: *static_cast<std::string*>(at) = text; break;
    }
}

// An editor's "details panel": every editable property with its value.
template <typename T>
void details_panel(T& object) {
    const ClassInfo& info = class_info<T>();
    std::printf("[%s]\n", info.name);
    for (const Property& p : info.properties)
        if (p.flags & Editable) std::printf("  %-8s = %s\n", p.name, text_of(p, &object).c_str());
}

// Sets a property by its name, as an editor or a console command would. False when the class has no such property.
template <typename T>
bool set(T& object, std::string_view name, const std::string& text) {
    for (const Property& p : class_info<T>().properties)
        if (name == p.name && (p.flags & Editable)) {
            set_from_text(p, &object, text);
            return true;
        }
    return false;
}

// Saves every Saved property as name=value lines, and loads them back.
template <typename T>
std::string save(T& object) {
    std::string out;
    for (const Property& p : class_info<T>().properties)
        if (p.flags & Saved) out += std::string(p.name) + "=" + text_of(p, &object) + "\n";
    return out;
}

template <typename T>
void load(T& object, std::string_view text) {
    while (!text.empty()) {
        const std::string_view line = text.substr(0, text.find('\n'));
        text.remove_prefix(std::min(text.size(), line.size() + 1));
        const auto eq = line.find('=');
        for (const Property& p : class_info<T>().properties)
            if ((p.flags & Saved) && line.substr(0, eq) == p.name) set_from_text(p, &object, std::string(line.substr(eq + 1)));
    }
}

registry_macros.cpp

// Two classes registered with the macros of registry.hpp, then shown, edited and saved by code that never names them.
#include "registry.hpp"

struct Player {
    std::string name = "Asha";
    int health = 100;
    float speed = 4.5f;
    int frames_alive = 0;  // not in the list below: no panel, no save file and no script will ever see it
};

REFLECT_BEGIN(Player)
REFLECT_PROPERTY(name, Editable | Saved)
REFLECT_PROPERTY(health, Editable | Saved)
REFLECT_PROPERTY(speed, Editable | Saved)
REFLECT_END()

struct Lamp {
    float brightness = 0.8f;
    int colour = 0xffd27f;
};

REFLECT_BEGIN(Lamp)
REFLECT_PROPERTY(brightness, Editable | Saved)
REFLECT_PROPERTY(colour, Saved)
REFLECT_END()

int main() {
    Player player;
    Lamp lamp;
    details_panel(player);
    details_panel(lamp);

    set(player, "health", "75");
    set(player, "name", "Ravi");
    std::printf("set armour: %s\n", set(player, "armour", "5") ? "done" : "Player has no such property");
    std::printf("set colour: %s\n", set(lamp, "colour", "0") ? "done" : "colour is not editable");
    std::printf("saved Player:\n%s", save(player).c_str());

    Player copy;
    copy.name = "nobody";
    load(copy, save(player));
    std::printf("loaded copy: name %s, health %d\n", copy.name.c_str(), copy.health);
}
Shown, edited and saved by code that never names the class Python · registry_session.py
from session import run

# registry_macros.cpp with each compiler; the last line compares the two programs' output.
run("""
clang++ -std=c++23 -Wall -Wextra -fsanitize=address,undefined registry_macros.cpp -o registry && ./registry
g++-16 -std=c++23 -Wall -Wextra -fsanitize=address,undefined registry_macros.cpp -o registry-gcc
./registry > clang.txt && ./registry-gcc > gcc.txt && cmp clang.txt gcc.txt && echo "GCC 16 printed the same"
""")

Output

$ clang++ -std=c++23 -Wall -Wextra -fsanitize=address,undefined registry_macros.cpp -o registry && ./registry
[Player]
  name     = Asha
  health   = 100
  speed    = 4.5
[Lamp]
  brightness = 0.8
set armour: Player has no such property
set colour: colour is not editable
saved Player:
name=Ravi
health=75
speed=4.5
loaded copy: name Ravi, health 75
$ g++-16 -std=c++23 -Wall -Wextra -fsanitize=address,undefined registry_macros.cpp -o registry-gcc
$ ./registry > clang.txt && ./registry-gcc > gcc.txt && cmp clang.txt gcc.txt && echo "GCC 16 printed the same"
GCC 16 printed the same

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 registry_session.py

Step through what the generic code did with the table, row by row:

Interactive · step by step Generic code that reads a table, and the member no row names Step through registry_macros.cpp. Every function below the table knows only the table, never the Player class.
Generic code that reads a table, and the member no row names, step 6 of 6. Showing struct Player, Player's table (name, health, speed), details_panel() (Asha, 100, 4.5), set() by name (colour: not editable), save() and load() (name, health, speed), frames_alive. In focus: frames_alive, Player's table.REFLECT linesstruct Playerfour membersPlayer's tablename, health, speeddetails_panel()Asha, 100, 4.5set() by namecolour: not editablesave() and load()name, health, speedframes_alivea member of Playerin no row of the table

The drawing shows the whole scene; its 6 steps are listed below.

Every step as text
  1. Three rows, built once. REFLECT_BEGIN(Player), three REFLECT_PROPERTY lines and REFLECT_END() build Player's table the first time it is asked for. Each row holds a member's name, kind, flags and how to find it in an object.
  2. A panel from the table. details_panel(player) walks the rows and prints every Editable one, name = Asha, health = 100 and speed = 4.5, without ever naming the Player class.
  3. Set by name. set(player, "health", "75") finds the row named health and writes 75, and set(player, "name", "Ravi") writes the name. set(player, "armour", "5") finds no row and returns false.
  4. A row that is not editable. Lamp's colour row has the Saved flag but not Editable, so set(lamp, "colour", "0") is refused too. The flags of a row decide what generic code may do with it.
  5. Saved and loaded. save(player) writes name=Ravi, health=75 and speed=4.5, one line per Saved row, and load() copied all three into a second player through that text.
  6. The member no row names. frames_alive is a member of Player, but no row names it, so no panel, save file or script saw it, and nothing warned. The compiler checks each listed member, never the ones left out.

The functions details_panel, set, save and load know only the table, yet they showed both classes, changed a player’s health by name, refused a property that does not exist and one that is not editable, and copied a player through its save file. frames_alive never appeared anywhere, because nothing listed it. That is the weakness of every table kept apart from its class: the compiler checks that each listed member exists and has a type the table knows, but nothing checks that every member is listed.

Key idea

Generic code sees only what the table lists, so a table kept apart from its class loses a member without a word.

reflection: A program's ability to inspect its own declarations while it is being compiled, for example to list the enumerators of an enumeration with their names. C++26 adds it (P2996R13). More in “C++ enums: enum class and std::to_underlying” removes the list. The compiler can enumerate a class’s members, with their names, types and annotations such as [[=editable]], while it compiles, so the table follows the class by itself.

Version note

GCC 16 implements C++26 reflection with -freflection; Clang does not yet.

Checkpoint

Check: A property table kept beside its class

  1. Question 1 of 2 In the registry's run, set(player, "armour", "5") returned false. Why?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Player's table has no row named armour, and set() finds properties only through the table

    Generic code knows only the table. A name that is in no row cannot be shown, set or saved, which is how the run printed "Player has no such property".

  2. Question 2 of 2 Player has a member frames_alive, yet it appeared in no panel and no save file, and nothing warned. Why?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: No REFLECT_PROPERTY line lists it, and nothing compares the list with the class's members

    That is the weakness of every table kept apart from its class: the compiler checks that each listed member exists and has a type the table knows, but nothing checks that every member is listed.

Practise: two ways to build the table

Compare the macro registry above with one that C++26 reflection builds, on four criteria: extra build steps, keeping the table in step with the class, the compilers that build it, and what happens when a member is missing from the table. The questions below take up these criteria and the other ideas above, and start with the one you know least.

Practice that adapts to you

Practise this lesson

10 questions about 5 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 10 What does the word reflection mean in a game engine?

    Choose one answer.

    Show the answer: Question 1 of 10

    Answer: A description of the program's own classes, their properties and functions, that the engine's code can read

    Reflection is a description of each class that code can read: its name, its properties with their types and flags, its functions. Engines build it themselves, and C++26 lets the compiler build one.

  2. Question 2 of 10 An Unreal project wants to use a library that throws exceptions, in one module only. Where is that decided?

    Choose one answer.

    Show the answer: Question 2 of 10

    Answer: In that module's build rules, which switch exception handling and run-time type information on or off

    Engines decide module by module whether exceptions and run-time type information may be used, and Unreal's module build rules switch both on or off.

  3. Question 3 of 10 Which header does an Unreal class's header include last of all its includes?

    Choose one answer.

    Show the answer: Question 3 of 10

    Answer: The generated header, such as MyObject.generated.h

    A class derived from UObject carries UCLASS() above it and GENERATED_BODY() inside it, and its header ends its includes with the generated header, which the header tool wrote before the compiler ran.

  4. Question 4 of 10 When does the Unreal Header Tool run?

    Choose one answer.

    Show the answer: Question 4 of 10

    Answer: Before the compiler, reading the headers and writing the registration code

    The Unreal Header Tool reads the marked headers before the compiler runs and writes generated C++, which the compiler then builds with the class.

  5. Question 5 of 10 A godot-cpp class GDExample has get_speed() and set_speed(). What does its _bind_methods need so that the editor can show and change speed?

    Choose one answer.

    Show the answer: Question 5 of 10

    Answer: bind_method for the getter and the setter, then ADD_PROPERTY for a property backed by them

    _bind_methods, which Godot calls once, registers what scripts and the editor may use: ClassDB::bind_method(D_METHOD("set_speed", "p_speed"), &GDExample::set_speed) for each method, and ADD_PROPERTY for a property backed by a getter and a setter.

  6. Question 6 of 10 A teammate writes Unreal gameplay code with std::vector and std::string throughout. What does Epic's coding standard say about that?

    Choose one answer.

    Show the answer: Question 6 of 10

    Answer: To avoid the standard containers and strings, except in code that talks to other libraries

    C++ in Unreal follows rules of its own: objects are never created with new, the coding standard says to avoid standard containers and strings except in interop code, and each module decides about exceptions and RTTI.

  7. Question 7 of 10 A team using Godot wants to change its C++ gameplay code many times a day without ever rebuilding the engine. Which arrangement gives them that?

    Choose one answer.

    Show the answer: Question 7 of 10

    Answer: A GDExtension library built with godot-cpp

    godot-cpp builds extensions for Godot's GDExtension system. Unlike a module, which is compiled into the engine, an extension is a library of its own, so changing it never means rebuilding Godot.

  8. Question 8 of 10 Which of the lesson's compilers can build a registry that uses C++26 reflection and annotations?

    Choose one answer.

    Show the answer: Question 8 of 10

    Answer: GCC 16, with -freflection

    GCC 16 implements reflection with -freflection, and Clang does not yet. The macro registry, by contrast, builds with any compiler.

  9. Question 9 of 10 A teammate adds a member to a class registered with the macro list, and forgets its REFLECT_PROPERTY line. What happens?

    Choose one answer.

    Show the answer: Question 9 of 10

    Answer: It compiles without a warning, and the member is missing from panels and save files

    The list is separate from the class, and nothing compares the two, so the table silently lacks the member, as it lacked frames_alive in the run.

  10. Question 10 of 10 You add a Projectile class with members speed and damage to the macro registry, and want the editor panel to show both. What do you write?

    Choose one answer.

    Show the answer: Question 10 of 10

    Answer: A REFLECT_BEGIN(Projectile) block with one REFLECT_PROPERTY line, flagged Editable, for each of the two members

    Generic code works on any class that has a table. A new class needs its REFLECT block, a line per member with the flags it should have, and nothing in details_panel, set, save or load changes.

The property table two ways: a forgotten line, then C++26 reflection Pro: opens with a Pro, Premium or Ultimate pass

A table kept apart from its class compiles happily when the two fall out of step. This part reproduces that bug as lost save data, generates the table from markers instead, builds it with C++26 reflection on GCC 16, and compares the three ways on four criteria, with measured compile times.

In this part:

  • A forgotten line, reproduced as lost save data
  • Generating the table from markers, and where a generator fails
  • The same registry with C++26 reflection on GCC 16
  • Four criteria, compared

2 runnable examples (Python, drift_macros.cpp, generated_main.cpp, header_tool.py, player_marked.hpp, registry_reflection.cpp) · about 6 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Garbage collection, RTTI and exceptions the engine way, reproduced Pro: opens with a Pro, Premium or Ultimate pass

Engines change three rules of everyday C++: who frees objects, how a program asks for an object’s type, and how errors travel. This part reproduces the bug a tracing collector punishes, the errors a build without RTTI and exceptions gives on both compilers, and the replacements engines use.

In this part:

  • A pointer the collector cannot see, reproduced
  • Who frees objects: four models, with their trade-offs
  • Builds without RTTI and exceptions, on both compilers
  • Cast and error values instead

1 diagram · 2 runnable examples (Python, engine_cast.cpp, gc.cpp, no_rtti.cpp) · about 6 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Interview questions

  1. Fresher to mid · Warm-up

    What is reflection in a game engine, and what does the engine use it for?

    Reflection is a description of the program's own classes that code can read: each class's name, its properties with their names, types and flags, and its functions. Standard C++ had none at run time, so engines build it themselves. Unreal Engine generates it from UCLASS, UPROPERTY and UFUNCTION markers with the Unreal Header Tool; godot-cpp registers it in each class's _bind_methods; custom engines keep a table made with macros or a generator. The engine then writes generic code once: the editor's property panel, saving and loading, binding to scripts, network replication and, in Unreal, finding the object pointers the garbage collector must follow. C++26 adds reflection to the language, so the compiler can build such a table from the class itself.

  2. Mid to senior

    After a release, players say the coins they earn are gone whenever they reload a save. The coins member was added to a class whose properties are registered with a hand-written macro list. Explain the failure, and how you would make it impossible.

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  3. Mid to senior

    In an engine with a tracing garbage collector, a character's spare weapon sometimes reports nonsense damage, and now and then the game crashes. The spare is held by a plain pointer member of the character. Diagnose the bug and fix it.

    Model answer, Pro. First step and follow-ups

  4. Senior to staff

    Your engine is built without exceptions and RTTI. A teammate wants both switched on so that a new library can throw and use dynamic_cast. What do you check, what would it cost, and what do engines use instead?

    Model answer, Pro. First step and follow-ups

Key takeaways

  • Engines add reflection to C++: a description of each class that drives the editor, saving, scripting, networking and, in Unreal, garbage collection.
  • C++ in Unreal marks classes and members with UCLASS, UPROPERTY and UFUNCTION, and the Unreal Header Tool generates the registration code before compiling.
  • Godot’s godot-cpp registers classes with GDCLASS and _bind_methods, as an extension library that never needs an engine rebuild.
  • A table kept apart from its class can fall out of step without any error; C++26 reflection, in GCC 16 with -freflection, builds it from the class itself.

References

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.