Skip to content

Design note: Replacing the preprocessor with reflection and generation

Herb Sutter edited this page Nov 25, 2022 · 1 revision

Q: How will we handle cross-platform code without the preprocessor? For example, #if WIN32 > use this, and #if LINUX > use that?

A: By embracing compile-time reflection and source code generation that is part of the language, not a preprocessor outside the language.

My aim for Cpp2 is to embrace reflection and generation, the latter meaning generative programming equivalent to (disciplined) source code generation.

Today, we might write something like this using preprocessor directives (note we can't generally replace this with if constexpr in today's C++ because that's not allowed at global scope for example):

// Strawman Cpp1 code today in the preprocessor outside the language

// maybe #define WIN32
#define WinVersion /*some value*/

#if defined(WIN32) || WinVersion > 0
    use this code
#else
    use that code
#endif

The idea in Cpp2 is to replace the above outside-the-language with inside-the-language code, something like the following (disclaimer: very-strawman syntax here, not yet implemented! -- the idea is that consteval { } is similar to a block that is C++20 consteval, and -> { } is similar to a P1717 -> { } block -- note the co-evolution here as Cpp2 is following these C++20 and P1717 ISO C++ evolution features and proposals, but C++20 consteval and P1717 also originally came from Cpp2 prototyping work):

// Strawman Cpp2 equivalent inside the language (not yet implemented)

WIN32 := /*some value*/; // assume globals become implicitly constexpr
WinVersion := /*some value*/ ;

consteval { // run this block of code at compile time
    if WIN32 != "" || WinVersion > 0 {
        -> { use this code }
    } else {
        -> { use that code }
    }
}

I agree that it's a conceptually small step to move the logic from a separate preprocessor step that runs before the language, to a very early compile-time phase of the language -- but it's an important step, because it moves it across that all-important line from "outside the language" to "inside the language."

Combined with compile-time reflection, this is even more powerful because it lets us introspect Cpp2 code and generate new code, which is the foundation of metaclass functions (also still to be implemented). For much more about those, see README.md > 2017: Reflection, generation, and metaclasses which was the first "large" feature of Cpp2 submitted as an ISO C++ evolution proposal.