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