Skip to content

Reflection

C3 allows both compile time and runtime reflection.

During compile time the type information may be directly used as compile time constants, the same data is then available dynamically at runtime.

During compile time there are a number of compile time fields that may be accessed directly.

It is possible to access properties on the type itself:

  • alignof
  • associated
  • elements
  • extnameof
  • inf
  • inner
  • kindof
  • len
  • max
  • membersof
  • methodsof
  • min
  • nan
  • nameof
  • names
  • paramsof
  • parentof
  • qnameof
  • returns
  • sizeof
  • typeid
  • values

Returns the alignment in bytes needed for the type.

struct Foo @align(8)
{
int a;
}
uint a = Foo.alignof; // 8

Only available for enums. Returns an array containing the types of associated values if any.

enum Foo : int (double d, String s)
{
BAR = { 1.0, "normal" },
BAZ = { 2.0, "exceptional" }
}
String s = Foo.associated[0].nameof; // "double"

Only available for floating point types

Returns a representation of floating point “infinity”.

This returns a typeid to an “inner” type. What this means is different for each type:

  • Array -> the array base type.
  • Bitstruct -> underlying base type.
  • Distinct -> the underlying type.
  • Enum -> underlying enum base type.
  • Pointer -> the type being pointed to.
  • Vector -> the vector base type.

It is not defined for other types.

Returns the underlying TypeKind as defined in std::core::types.

TypeKind kind = int.kindof; // TypeKind.SIGNED_INT

Returns the length of the array.

usz len = int[4].len; // 4

Returns the maximum value of the type (only valid for integer and float types).

ushort max_ushort = ushort.max; // 65535

Only available for bitstruct, struct and union types.

Returns a compile time list containing the fields in a bitstruct, struct or union. The elements have the compile time only type of member_ref.

Note: As the list is an “untyped” list, you are limited to iterating and accessing it at compile time.

struct Baz
{
int x;
Foo* z;
}
String x = Baz.membersof[1].nameof; // "z"

A member_ref has properties alignof, kindof, membersof, nameof, offsetof, sizeof and typeid.

This property returns the methods associated with a type as a constant array of strings.

Methods are generally registered after types are registered, which means that the use of “methodsof” may return inconsistent results depending on where in the resolution cycle it is invoked. It is always safe to use inside a function.

Returns the minimum value of the type (only valid for integer and float types).

ichar min_ichar = ichar.min; // -128

Returns the name of the type.

Returns a slice containing the names of an enum.

enum FooEnum
{
BAR,
BAZ
}
String[] x = FooEnum.names; // ["BAR", "BAZ"]

Only available for function pointer types. Returns a ReflectParam struct for all function pointer parameters.

alias TestFunc = fn int(int x, double f);
String s = TestFunc.paramsof[1].name; // "f"
typeid t = TestFunc.paramsof[1].type; // double.typeid

Only available for bitstruct and struct types. Returns the typeid of the parent type.

struct Foo
{
int a;
}
struct Bar
{
inline Foo f;
}
String x = Bar.parentof.nameof; // "Foo"

Only available for function types. Returns the typeid of the return type.

alias TestFunc = fn int(int, double);
String s = TestFunc.returns.nameof; // "int"

Returns the size in bytes for the given type, like C sizeof.

usz x = Foo.sizeof;

Returns the typeid for the given type. aliass will return the typeid of the underlying type. The typeid size is the same as that of an iptr.

typeid x = Foo.typeid;

Returns a slice containing the values of an enum.

enum FooEnum
{
BAR,
BAZ
}
String x = FooEnum.values[1].nameof; // "BAR"

There are several built-in functions to inspect the code during compile time.

  • $alignof
  • $defined
  • $eval
  • $evaltype
  • $extnameof
  • $nameof
  • $offsetof
  • $qnameof
  • $sizeof
  • $stringify
  • $typeof

Returns the alignment in bytes needed for the type or member.

module test::bar;
struct Foo
{
int x;
char[] y;
}
int g = 123;
$alignof(Foo.x); // => returns 4
$alignof(Foo.y); // => returns 8 on 64 bit
$alignof(Foo); // => returns 8 on 64 bit
$alignof(g); // => returns 4

Returns true when the expression(s) inside are defined and all sub expressions are valid.

$defined(Foo); // => true
$defined(Foo.x); // => true
$defined(Foo.baz); // => false
Foo foo = {};
// Check if a method exists:
$if $defined(foo.call):
// Check what the method accepts:
$switch :
$case $defined(foo.call(1)) :
foo.call(1);
$default :
// do nothing
$endswitch
$endif
// Other way to write that:
$if $defined(foo.call, foo.call(1)):
foo.call(1);
$endif

The full list of what $defined can check:

  • *<expr> - checks if <expr> can be dereferenced, <expr> must already be valid
  • <expr>[<index>] - checks if indexing is valid, <expr> and <index> must already be valid, and when possible to check at compile-time if <index> is out of bounds this will return false
  • <expr>[<index>] = <value> - same as above, but also checks if <value> can be assigned, <expr>, <index> and <value> must already be valid
  • <expr>.<ident1>.<ident2> - check if .<ident2> is valid, <expr>.<ident1> must already be valid (“ident” is short for “identifier”)
  • ident, #ident, @ident, IDENT, $$IDENT, $ident - check if identifier exists
  • Type - check if the type exists
  • &<expr> - check if you can take the address of <expr>, <expr> must already be valid
  • &&<expr> - check if you can take the temp address of <expr>, <expr> must already be valid
  • $eval(<expr>) - check if the $eval evaluates to something valid, <expr> must already be valid
  • <expr>(<arg0>, ...) - check that the arguments are valid for the <expr> macro/function, <expr> and all args must already be valid
  • <expr>!! and <expr>! - check that <expr> is an optional, <expr> must already be valid
  • <expr>? - check that <expr> is a fault, <expr> must already be valid
  • <expr1> binary_operator <expr2> - check if the binary_operator (+, -, …) is defined between the two expressions, both expressions must already be valid
  • (<Type>)<expr> - check if <expr> can be casted to <Type>, both <Type> and <expr> must already be valid

If for example <expr> is not defined when trying (<Type>)<expr> this will result in a compile-time error.

Converts a compile time string with the corresponding variable:

int a = 123; // => a is now 123
$eval("a") = 222; // => a is now 222
$eval("mymodule::fooFunc")(a); // => same as mymodule::fooFunc(a)

$eval is limited to a single, optionally path prefixed, identifier. Consequently methods cannot be evaluated directly:

struct Foo { ... }
fn int Foo.test(Foo* f) { ... }
fn void test()
{
void* test1 = &$eval("test"); // Works
void* test2 = &Foo.$eval("test"); // Works
// void* test3 = &$eval("Foo.test"); // Error
}

Similar to $eval but for types:

$evaltype("float") f = 12.0f;

Returns the external name of a type, variable or function. The external name is the one used by the linker.

fn void testfn(int x) { }
String a = $extnameof(g); // => "test.bar.g";
String b = $extnameof(testfn); // => "test.bar.testfn"

Returns the name of a function or variable as a string without module prefixes.

fn void test() { }
int g = 1;
String a = $nameof(g); // => "g"
String b = $nameof(test); // => "test"

Returns the offset of a member in a struct.

Foo z;
$offsetof(z.y); // => returns 8 on 64 bit, 4 on 32 bit

Returns the same as $nameof, but with the full module name prepended.

module abc;
fn void test() { }
int g = 1;
String a = $qnameof(g); // => "abc::g"
String b = $qnameof(test); // => "abc::test"

This is used on a value to determine the allocation size needed. $sizeof(a) is equivalent to doing $typeof(a).sizeof. Note that this is only used on values and not on types.

$typeof(a)* x = allocate_bytes($sizeof(a));
*x = a;

Returns the expression as a string. It has a special behaviour for macro expression parameters, where $stringify(#foo) will return the expression contained in #foo rather than simply return “#foo”

Returns the type of an expression or variable as a type itself.

Foo f;
$typeof(f) x = f;