· via Hacker News – Front Page (hnrss.org)
Compile-time reflection compared: C++ std::meta vs Zig comptime vs C3 macros
A blog post shared to Hacker News pits C++'s upcoming std::meta reflection against Zig's comptime and C3's macros across enum-to-string, struct introspection and attribute-driven validation.

A personal blog post that reached the Hacker News front page puts the compile-time reflection facilities of C++, Zig and C3 side by side, solving the same three problems in each language: converting enum values to strings, printing struct fields, and validating configuration through data attached to type members.
Three languages, one compile-time idea
According to the post, all three languages place reflection at compile time rather than run time, so a program can reason about types, enumerators and struct members without any runtime cost. The mechanisms differ sharply. C++ is gaining reflection through a std::meta library — the examples call functions such as std::meta::enumerators_of and std::meta::identifier_of, then use the [: ... :] splice to turn reflection results back into ordinary code. Zig leans on its comptime model, where @typeInfo exposes a type's layout to generic functions. C3, which the author describes as a newer language that aims to stay readable, fast and minimal while feeling familiar to C and C++ programmers, ships without garbage collection, exceptions, RAII or a heavyweight runtime and supports the C ABI out of the box. It prefixes every compile-time variable and control-flow construct with $, a deliberate choice so a reader can see at a glance which code runs during compilation. Its macros, positioned as a replacement for the C preprocessor, add compile-time evaluation built on constant folding.
Enum-to-string conversion
In C++ the author writes one generic function that loops over the enumerators of any enum type via std::meta::enumerators_of and returns std::meta::identifier_of for the match. Zig gets no equivalent in the post: the only approach the author found workable is a hand-written method containing a switch, attached individually to each enum, and they openly invite corrections while noting they are not a deep Zig expert. C3 handles the task with a macro that iterates the enum's values and reads each one's description. The post also highlights C3 conveniences here: printing an enum value outputs its source-level name rather than its integer, the .ordinal property or a cast retrieves the underlying value, and individual enumerators can carry associated values of arbitrary types that code can read directly.
Struct introspection
All three languages can walk a struct's fields generically. The C++ version iterates std::meta::nonstatic_data_members_of and reaches each member through the splice obj.[:member:]. Zig's version uses inline for across the struct fields that @typeInfo reports and the @field builtin to read values by name. C3's macro loops over the type's members and dereferences them dynamically. The author also demonstrates a C3 contract — an optional precondition, here requiring the input to be a struct — which is checked at compile time when possible and at runtime otherwise.
Field-level attributes and validation
The sharpest divergence appears when attaching data to struct members. The C++ example places annotations such as [[=Range{1, 65535}]] on Config fields, and a validating function template reads them back through std::meta annotations queries and extracts the bounds, so a static_assert over an out-of-range configuration simply fails to compile. Zig, per the author, has no attributes or any substitute mechanism for this at all. C3 defines an attribute with attrdef, tags fields with @Range, and then offers two paths: a compile-time validating macro for compile-time values and a near-identical macro that performs the same checks at runtime for ordinary values.
Why it matters
Reflection is one of the most consequential capabilities heading toward C++, and abstract proposals are difficult to judge. By working identical small problems in three languages, the post offers a tactile preview of how std::meta will feel in practice — generic and powerful, but syntactically dense — set against Zig's comptime, which is flexible yet currently lacks an answer for generic enum printing and per-field metadata, and C3's explicitly marked macro system, which reads clearly and supports attributes natively but belongs to a far younger ecosystem. The comparison reflects one author's perspective, with an explicit caveat about their own Zig experience, but as a snapshot of where compile-time metaprogramming is heading across these languages it clearly resonated with the Hacker News audience.
- #cpp
- #zig
- #c3
- #reflection
- #compile-time