FieldSymbols

Evaluates to an AliasSeq of the symbols for the member variables of an aggregate type (i.e. a struct, class, interface, or union).

These are fields which take up memory space within an instance of the type (i.e. not enums / manifest constants, since they don't take up memory space, and not static member variables, since they don't take up memory space within an instance).

Hidden fields (like the virtual function table pointer or the context pointer for nested types) are not included.

For classes, only the direct member variables are included and not those of any base classes.

For interfaces, the result of FieldSymbols is always empty, because interfaces cannot have member variables. However, because interfaces are aggregate types, they work with FieldSymbols for consistency so that code that's written to work on aggregate types doesn't have to worry about whether it's dealing with an interface.

In most cases, FieldSymbols!T has the same result as T.tupleof. The difference is that for nested structs with a context pointer, T.tupleof includes the context pointer, whereas FieldSymbols!T does not. For non-nested structs, and for classes, interfaces, and unions, FieldSymbols!T and T.tupleof are the same.

So, for most cases, T.tupleof is sufficient and avoids instantiating an additional template, but FieldSymbols is provided so that the code that needs to avoid including context pointers in the list of fields can do so without the programmer having to figure how to do that correctly. It also provides a template that's equivalent to what FieldNames and FieldTypes do in terms of which fields it gives (the difference of course then being whether you get the symbols, names, or types for the fields), whereas the behavior for tupleof is subtly different.

template FieldSymbols (
T
) if (
isAggregateType!T
) {}

Examples

1 import phobos.sys.meta : AliasSeq;
2 
3 struct S
4 {
5     int x;
6     float y;
7 }
8 static assert(__traits(isSame, FieldSymbols!S, AliasSeq!(S.x, S.y)));
9 
10 // FieldSymbols!S and S.tupleof are the same, because S is not nested.
11 static assert(__traits(isSame, FieldSymbols!S, S.tupleof));
12 
13 // Note that type qualifiers _should_ be passed on to the result, but due
14 // to https://issues.dlang.org/show_bug.cgi?id=24516, they aren't.
15 // FieldTypes does not have this problem, because it aliases the types
16 // rather than the symbols, so if you need the types from the symbols, you
17 // should use either FieldTypes or tupleof until the compiler bug has been
18 // fixed (and if you use tupleof, you need to avoid aliasing the result
19 // before getting the types from it).
20 static assert(is(typeof(FieldSymbols!S[0]) == int));
21 
22 // These currently fail when they shouldn't:
23 //static assert(is(typeof(FieldSymbols!(const S)[0]) == const int));
24 //static assert(is(typeof(FieldSymbols!(shared S)[0]) == shared int));
25 
26 class C
27 {
28     // static variables are not included.
29     static int var;
30 
31     // Manifest constants are not included.
32     enum lang = "dlang";
33 
34     // Functions are not included, even if they're @property functions.
35     @property int foo() { return 42; }
36 
37     string s;
38     int i;
39     int[] arr;
40 }
41 static assert(__traits(isSame, FieldSymbols!C, AliasSeq!(C.s, C.i, C.arr)));
42 
43 // FieldSymbols!C and C.tupleof have the same symbols, because they are
44 // always the same for classes.
45 static assert(__traits(isSame, FieldSymbols!C, C.tupleof));
46 
47 // Only direct member variables are included. Member variables from any base
48 // classes are not.
49 class D : C
50 {
51     real r;
52 }
53 static assert(__traits(isSame, FieldSymbols!D, AliasSeq!(D.r)));
54 static assert(__traits(isSame, FieldSymbols!D, D.tupleof));
55 
56 // FieldSymbols will always be empty for an interface, since it's not legal
57 // for interfaces to have member variables.
58 interface I
59 {
60 }
61 static assert(FieldSymbols!I.length == 0);
62 static assert(I.tupleof.length == 0);
63 
64 union U
65 {
66     int i;
67     double d;
68     long l;
69     S s;
70 }
71 static assert(__traits(isSame, FieldSymbols!U, AliasSeq!(U.i, U.d, U.l, U.s)));
72 
73 // FieldSymbols!C and C.tupleof have the same symbols, because they are
74 // always the same for unions.
75 static assert(__traits(isSame, FieldSymbols!U, U.tupleof));
76 
77 // FieldSymbols only operates on aggregate types.
78 static assert(!__traits(compiles, FieldSymbols!int));
79 static assert(!__traits(compiles, FieldSymbols!(S*)));
80 static assert(!__traits(compiles, FieldSymbols!(C[])));

Some examples with nested types.

import phobos.sys.meta : AliasSeq;

int outside;

struct S
{
    long l;
    string s;

    void foo() { outside = 2; }
}
static assert(__traits(isNested, S));
static assert(__traits(isSame, FieldSymbols!S, AliasSeq!(S.l, S.s)));

// FieldSymbols!S and S.tupleof are not the same, because S is nested, and
// the context pointer to the outer scope is included in S.tupleof, whereas
// it is excluded from FieldSymbols!S.
static assert(__traits(isSame, S.tupleof[0 .. $ - 1], AliasSeq!(S.l, S.s)));
static assert(S.tupleof[$ - 1].stringof == "this");

class C
{
    bool b;
    int* ptr;

    void foo() { outside = 7; }
}
static assert(__traits(isNested, C));
static assert(__traits(isSame, FieldSymbols!C, AliasSeq!(C.b, C.ptr)));

// FieldSymbols!C and C.tupleof have the same symbols, because they are
// always the same for classes. No context pointer is provided as part of
// tupleof for nested classes.
static assert(__traits(isSame, FieldSymbols!C, C.tupleof));

// __traits(isNested, ...) is never true for interfaces or unions, since
// they cannot have a context pointer to an outer scope. So, tupleof and
// FieldSymbols will always be the same for interfaces and unions.

See Also

Meta