Compile-Time Reflection

29.1 __traits

struct User
{
    string name;
    int age;
    @("primary") int id;
    void greet() {}
    static void create() {}
}

void inspect(T)()
{
    foreach (member; __traits(allMembers, T))
        writeln(member);
    static foreach (field; __traits(allMembers, T))
    {{
        static if (is(typeof(__traits(getMember, T.init, field))) && !isCallable!(__traits(getMember, T, field)))
            writeln(field, ": ", typeof(__traits(getMember, T.init, field)).stringof);
    }}
}

writeln(__traits(hasMember, User, "name"));
writeln(__traits(isPOD, User));
writeln(__traits(identifier, User.age));
writeln(__traits(getAttributes, User.id));            // UDAs
writeln(__traits(compiles, User.init.greet()));
writeln(__traits(isStaticFunction, User.create));
writeln(__traits(getProtection, User.name));
writeln(__traits(getOverloads, User, "greet").length);
writeln(__traits(derivedMembers, User));
writeln(__traits(getLinkage, User.greet));
writeln(__traits(fullyQualifiedName, User));          // 2.106+

29.2 std.traits and std.meta

import std.traits;
alias F = Fields!User;                      // (string, int, int)
enum names = FieldNameTuple!User;           // ("name", "age", "id")
alias R = ReturnType!add;
alias P = Parameters!add;
enum pn = ParameterIdentifierTuple!add;
static assert(isCallable!add);
static assert(isAggregateType!User);
static assert(isSomeString!string);
static assert(isDynamicArray!(int[]));
static assert(isAssociativeArray!(int[string]));
static assert(hasUDA!(User.id, "primary"));
alias withUDA = getSymbolsByUDA!(User, "primary");
alias Base = BaseClassesTuple!MyClass;
alias Unq = Unqual!(const int);             // int
enum fqn = fullyQualifiedName!User;
static assert(hasMember!(User, "age"));
static assert(isImplicitlyConvertible!(int, long));

29.3 User-Defined Attributes (UDAs)

Real-world uses: serialization (vibe.data.json, asdf, mir-ion), ORMs, CLI parsers (argsd, darg), test runners, dependency injection.

struct Column { string name; }
struct Ignore {}

struct Product
{
    @Column("product_id") int id;
    @Column("title") string name;
    @Ignore double cache;
}

string toSql(T)()
{
    string cols;
    static foreach (field; FieldNameTuple!T)
    {{
        alias member = __traits(getMember, T, field);
        static if (!hasUDA!(member, Ignore))
        {
            static if (hasUDA!(member, Column))
                cols ~= getUDAs!(member, Column)[0].name ~ ", ";
            else
                cols ~= field ~ ", ";
        }
    }}
    return "SELECT " ~ cols[0 .. $ - 2] ~ " FROM " ~ T.stringof;
}
enum sql = toSql!Product();      // computed at compile time