#!/usr/bin/env dub
/+ dub.sdl:
name "property_tree_reflect_descent"
targetPath "build"
dflags "-preview=in" "-preview=dip1000"
buildType "checked" {
buildOptions "optimize" "inline" "debugInfo"
}
+/
/**
* What a compile-time descent over `T` actually yields, and where it stops.
*
* Backs [../concepts.md](../concepts.md) § _descent decision_ and
* [../sparkles-baseline.md](../sparkles-baseline.md) § _Recursion_: every
* surveyed library decides "leaf or subtree?" at **runtime** — from a converter
* ([../winforms-propertygrid.md](../winforms-propertygrid.md)), a
* `ReflectRef` discriminant ([../bevy-inspector-egui.md](../bevy-inspector-egui.md)),
* or a `PropertyInfo.type` ([../godot-inspector.md](../godot-inspector.md)).
* In D the same decision is a `static if` over `isAggregateType`, and that moves
* two failure modes from run time to compile time:
*
* 1. **A recursive type diverges at compile time, not at expand time.** Godot
* lets a reader unfold a self-referencing resource for as long as they keep
* clicking; the equivalent D descent never finishes compiling. The visited
* *type* set below is not an optimization — without it this program does
* not build: both ldc2 2.111 and dmd 2.112 stop with
* `Error: template instance ... recursive expansion exceeded allowed
* nesting limit` after 500 levels. That is a stronger guarantee than any
* surveyed library offers, and a harder constraint.
* 2. **The whole tree is a value, not a walk.** `describe!T` runs in CTFE and
* returns a flat, pre-ordered row array, so the row count of a type is
* knowable before a frame is drawn.
*
* Run: `dub run --single reflect-descent.d`
*/
module (module) property_tree_reflect_descentWhat a compile-time descent over T actually yields, and where it stops.
Backs ../concepts.md § descent decision_ and
../sparkles-baseline.md § Recursion_: every
surveyed library decides "leaf or subtree?" at runtime — from a converter
(../winforms-propertygrid.md), a
ReflectRef discriminant (../bevy-inspector-egui.md),
or a PropertyInfo.type (../godot-inspector.md).
In D the same decision is a static if over isAggregateType, and that moves
two failure modes from run time to compile time:
A recursive type diverges at compile time, not at expand time. Godot
lets a reader unfold a self-referencing resource for as long as they keep
clicking; the equivalent D descent never finishes compiling. The visited
type set below is not an optimization — without it this program does
not build: both ldc2 2.111 and dmd 2.112 stop with
Error
template instance ... recursive expansion exceeded allowed
nesting limit` after 500 levels. That is a stronger guarantee than any
surveyed library offers, and a harder constraint.
The whole tree is a value, not a walk. describe!T runs in CTFE and
returns a flat, pre-ordered row array, so the row count of a type is
knowable before a frame is drawn.
Run
dub run --single reflect-descent.d
property_tree_reflect_descent;
import (package) stdstd.(module) std.stdioCategory Symbols File handles _popen File isFileHandle openNetwork stderr stdin stdout Reading chunks lines readf readfln readln Writing toFile write writef writefln writeln Misc KeepTerminator LockType StdioException
Standard I/O functions that extend core.stdc.stdio. core.stdc.stdio
is publically imported when importing std.stdio.
There are three layers of I/O:
The lowest layer is the operating system layer. The two main schemes are Windows and Posix.
C's stdio.h which unifies the two operating system schemes.
std.stdio, this module, unifies the various stdio.h implementations into
a high level package for D programs.
Source
std/stdio.d
stdio : (alias template) property_tree_reflect_descent.writefln = std.stdio.writefln(alias fmt, A...)(A args) if (isSomeString!(typeof(fmt)))Equivalent to writef(fmt, args, '\n').
writefln, (alias template) property_tree_reflect_descent.writeln = std.stdio.writeln(T...)(T args)Equivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln;
import (package) stdstd.(module) std.traitsTemplates which extract information about types and symbols at compile time.
Category Templates Symbol Name traits fullyQualifiedName mangledName moduleName packageName Function traits isFunction arity functionAttributes hasFunctionAttributes functionLinkage FunctionTypeOf isSafe isUnsafe isFinal ParameterDefaults ParameterIdentifierTuple ParameterStorageClassTuple Parameters ReturnType SetFunctionAttributes variadicFunctionStyle Aggregate Type traits BaseClassesTuple BaseTypeTuple classInstanceAlignment EnumMembers FieldNameTuple Fields hasAliasing hasElaborateAssign hasElaborateCopyConstructor hasElaborateDestructor hasElaborateMove hasIndirections hasMember hasStaticMember hasNested hasUnsharedAliasing InterfacesTuple isInnerClass isNested MemberFunctionsTuple RepresentationTypeTuple TemplateArgsOf TemplateOf TransitiveBaseTypeTuple Type Conversion CommonType AllImplicitConversionTargets ImplicitConversionTargets CopyTypeQualifiers CopyConstness isAssignable isCovariantWith isImplicitlyConvertible isQualifierConvertible Type Constructors InoutOf ConstOf SharedOf SharedInoutOf SharedConstOf SharedConstInoutOf ImmutableOf QualifierOf Categories of types allSameType ifTestable isType isAggregateType isArray isAssociativeArray isAutodecodableString isBasicType isBoolean isBuiltinType isCopyable isDynamicArray isEqualityComparable isFloatingPoint isIntegral isNarrowString isConvertibleToString isNumeric isOrderingComparable isPointer isScalarType isSigned isSIMDVector isSomeChar isSomeString isStaticArray isUnsigned Type behaviours isAbstractClass isAbstractFunction isCallable isDelegate isExpressions isFinalClass isFinalFunction isFunctionPointer isInstanceOf isIterable isMutable isSomeFunction isTypeTuple General Types ForeachType KeyType Largest mostNegative OriginalType PointerTarget Signed Unconst Unshared Unqual Unsigned ValueType Promoted Misc lvalueOf rvalueOf Select select User-Defined Attributes hasUDA getUDAs getSymbolsByUDA
Source
std/traits.d
traits : (alias template) property_tree_reflect_descent.FieldNameTuple = std.traits.FieldNameTuple(T)Get as an expression tuple the names of the fields of a struct, class, or
union. This consists of the fields that take up memory space, excluding the
hidden fields like the virtual function table pointer or a context pointer
for nested types.
Inherited fields (for classes) are not included.
If T isn't a struct, class, interface or union, an
expression tuple with an empty string is returned.
FieldNameTuple, (alias template) property_tree_reflect_descent.Fields = std.traits.Fields(T)Get as a tuple the types of the fields of a struct, class, or union.
This consists of the fields that take up memory space,
excluding the hidden fields like the virtual function
table pointer or a context pointer for nested types.
If T isn't a struct, class, interface or union returns a tuple
with one element T.
Fields, (alias template) property_tree_reflect_descent.isAggregateType = std.traits.isAggregateType(T)Detect whether type T is an aggregate type.
isAggregateType, (alias template) property_tree_reflect_descent.isPointer = std.traits.isPointer(T)Detect whether type T is a pointer.
isPointer,
(alias template) property_tree_reflect_descent.PointerTarget = std.traits.PointerTarget(T : T*)Returns the target type of a pointer.
PointerTarget;
@safe:
/// One presented row of a reflected type: what a renderer needs before values.
struct (struct) property_tree_reflect_descent.FieldRowOne presented row of a reflected type: what a renderer needs before values.
FieldRow
{
(alias) object.string = stringstring (field) string property_tree_reflect_descent.FieldRow.pathdotted path from the root
path; /// dotted path from the root
(alias) object.string = stringstring (field) string property_tree_reflect_descent.FieldRow.typethe field's static type
type; /// the field's static type
(alias) object.size_t = ulongsize_t (field) ulong property_tree_reflect_descent.FieldRow.depthnesting level; 0 is a direct field of the root
depth; /// nesting level; 0 is a direct field of the root
bool (field) bool property_tree_reflect_descent.FieldRow.expandablean aggregate we descended into
expandable; /// an aggregate we descended into
bool (field) bool property_tree_reflect_descent.FieldRow.cutdescent stopped here: the type is already on the path
cut; /// descent stopped here: the type is already on the path
}
/// The types a leaf editor exists for. Everything else is a candidate subtree.
private enum bool (constant) bool property_tree_reflect_descent.isLeafType!(property_tree_reflect_descent.Vec2) = falseThe types a leaf editor exists for. Everything else is a candidate subtree.
isLeafType(T) = is((alias) T = property_tree_reflect_descent.Vec2T == string) || !(template instance) std.traits.isAggregateType!(property_tree_reflect_descent.Vec2)isAggregateType!(alias) T = property_tree_reflect_descent.Vec2T;
/**
Flattens `T` into pre-ordered rows at compile time.
`Seen` is the set of aggregate types already open on the current path. It is
what makes the function total: a type that contains itself (directly or through
a pointer) is cut with `cut = true` instead of re-entering.
*/
(struct) property_tree_reflect_descent.FieldRowOne presented row of a reflected type: what a renderer needs before values.
FieldRow[] property_tree_reflect_descent.FieldRow[] property_tree_reflect_descent.describe!(property_tree_reflect_descent.Vec2, 2LU, "parent.position.", property_tree_reflect_descent.Node, property_tree_reflect_descent.Vec2)() pure nothrow @safeFlattens T into pre-ordered rows at compile time.
Seen is the set of aggregate types already open on the current path. It is
what makes the function total: a type that contains itself (directly or through
a pointer) is cut with cut = true instead of re-entering.
describe(T, size_t depth = 0, string prefix = "", Seen...)() pure nothrow
{
(struct) property_tree_reflect_descent.FieldRowOne presented row of a reflected type: what a renderer needs before values.
FieldRow[] (local variable) property_tree_reflect_descent.FieldRow[] rowsrows;
static foreach (i, name; (constant) string property_tree_reflect_descent.Vec2.fun!(x).NameOf = "x"FieldNameTuple!T)
{{
alias (alias) property_tree_reflect_descent.describe!(property_tree_reflect_descent.Vec2, 2LU, "parent.position.", property_tree_reflect_descent.Node, property_tree_reflect_descent.Vec2).F = floatF = (alias) std.traits.Fields!(property_tree_reflect_descent.Vec2) = (float, float)Get as a tuple the types of the fields of a struct, class, or union.
This consists of the fields that take up memory space,
excluding the hidden fields like the virtual function
table pointer or a context pointer for nested types.
If T isn't a struct, class, interface or union returns a tuple
with one element T.
Examples
import std.meta : AliasSeq;
struct S { int x; float y; }
static assert(is(Fields!S == AliasSeq!(int, float)));
Fields!(alias) T = property_tree_reflect_descent.Vec2T[(constant) ulong property_tree_reflect_descent.describe!(property_tree_reflect_descent.Vec2, 2LU, "parent.position.", property_tree_reflect_descent.Node, property_tree_reflect_descent.Vec2).i = 0LUi];
// A pointer is presented as the type it points at — that is what makes
// `Node*` a cycle rather than an opaque address.
static if ((template instance) std.traits.isPointer!floatisPointer!(alias) property_tree_reflect_descent.describe!(property_tree_reflect_descent.Vec2, 2LU, "parent.position.", property_tree_reflect_descent.Node, property_tree_reflect_descent.Vec2).F = floatF && (template instance) isAggregateType!(PointerTarget!F)isAggregateType!((alias) T = property_tree_reflect_descent.NodePointerTarget!(unresolved type) FF))
alias (alias) property_tree_reflect_descent.describe!(property_tree_reflect_descent.Node, 0LU, "").Target = property_tree_reflect_descent.NodeTarget = (alias) T = property_tree_reflect_descent.NodePointerTarget!(alias) property_tree_reflect_descent.describe!(property_tree_reflect_descent.Node, 0LU, "").F = property_tree_reflect_descent.Node*F;
else
alias (alias) property_tree_reflect_descent.describe!(property_tree_reflect_descent.Vec2, 2LU, "parent.position.", property_tree_reflect_descent.Node, property_tree_reflect_descent.Vec2).Target = floatTarget = (alias) property_tree_reflect_descent.describe!(property_tree_reflect_descent.Vec2, 2LU, "parent.position.", property_tree_reflect_descent.Node, property_tree_reflect_descent.Vec2).F = floatF;
enum (constant) string property_tree_reflect_descent.describe!(property_tree_reflect_descent.Vec2, 2LU, "parent.position.", property_tree_reflect_descent.Node, property_tree_reflect_descent.Vec2).path = "parent.position.x"path = (constant) string property_tree_reflect_descent.prefix = "parent.position."prefix ~ (constant) string property_tree_reflect_descent.describe!(property_tree_reflect_descent.Node, 0LU, "").name = "parent"name;
enum bool (constant) bool property_tree_reflect_descent.describe!(property_tree_reflect_descent.Vec2, 2LU, "parent.position.", property_tree_reflect_descent.Node, property_tree_reflect_descent.Vec2).leaf = trueleaf = (template instance) property_tree_reflect_descent.isLeafType!floatisLeafType!(alias) property_tree_reflect_descent.describe!(property_tree_reflect_descent.Vec2, 2LU, "parent.position.", property_tree_reflect_descent.Node, property_tree_reflect_descent.Vec2).Target = floatTarget;
enum bool (constant) bool property_tree_reflect_descent.describe!(property_tree_reflect_descent.Vec2, 2LU, "parent.position.", property_tree_reflect_descent.Node, property_tree_reflect_descent.Vec2).seen = falseseen = (template instance) property_tree_reflect_descent.anyIs!(float, property_tree_reflect_descent.Node, property_tree_reflect_descent.Vec2)anyIs!((alias) property_tree_reflect_descent.describe!(property_tree_reflect_descent.Vec2, 2LU, "parent.position.", property_tree_reflect_descent.Node, property_tree_reflect_descent.Vec2).Target = floatTarget, SeenSeen);
(local variable) property_tree_reflect_descent.FieldRow[] rowsrows ~= (struct) property_tree_reflect_descent.FieldRowOne presented row of a reflected type: what a renderer needs before values.
FieldRow((constant) string property_tree_reflect_descent.describe!(property_tree_reflect_descent.Vec2, 2LU, "parent.position.", property_tree_reflect_descent.Node, property_tree_reflect_descent.Vec2).path = "parent.position.x"path, (struct) property_tree_reflect_descent.Vec2F.(constant) string float.stringof = "float"stringof, (constant) ulong property_tree_reflect_descent.depth = 2LUdepth, !(constant) bool property_tree_reflect_descent.describe!(property_tree_reflect_descent.Vec2, 2LU, "parent.position.", property_tree_reflect_descent.Node, property_tree_reflect_descent.Vec2).leaf = trueleaf && !(constant) bool property_tree_reflect_descent.describe!(property_tree_reflect_descent.Node, 0LU, "").seen = falseseen, !(constant) bool property_tree_reflect_descent.describe!(property_tree_reflect_descent.Vec2, 2LU, "parent.position.", property_tree_reflect_descent.Node, property_tree_reflect_descent.Vec2).leaf = trueleaf && (constant) bool property_tree_reflect_descent.describe!(property_tree_reflect_descent.Node, 0LU, "").seen = falseseen);
static if (!leaf && !seen)
(local variable) property_tree_reflect_descent.FieldRow[] rowsrows ~= property_tree_reflect_descent.FieldRow[] property_tree_reflect_descent.describe!(property_tree_reflect_descent.Vec2, 1LU, "position.", property_tree_reflect_descent.Vec2)() pure nothrow @safeFlattens T into pre-ordered rows at compile time.
Seen is the set of aggregate types already open on the current path. It is
what makes the function total: a type that contains itself (directly or through
a pointer) is cut with cut = true instead of re-entering.
describe!((alias) property_tree_reflect_descent.describe!(property_tree_reflect_descent.Node, 0LU, "").Target = property_tree_reflect_descent.Vec2Target, (constant) ulong property_tree_reflect_descent.depth = 1LUdepth + 1, (constant) string property_tree_reflect_descent.describe!(property_tree_reflect_descent.Node, 1LU, "parent.", property_tree_reflect_descent.Node).path = "parent.position"path ~ ".", SeenSeen, (alias) property_tree_reflect_descent.describe!(property_tree_reflect_descent.Node, 1LU, "parent.", property_tree_reflect_descent.Node).Target = property_tree_reflect_descent.Vec2Target)();
}}
return (local variable) property_tree_reflect_descent.FieldRow[] rowsrows;
}
private enum bool (constant) bool property_tree_reflect_descent.anyIs!(float, property_tree_reflect_descent.Node, property_tree_reflect_descent.Vec2) = falseanyIs(T, Seen...) = ()
{
bool (local variable) bool foundfound;
static foreach (S; Seen)
(local variable) bool foundfound = (local variable) bool foundfound || is((alias) T = floatT == S);
return (local variable) bool foundfound;
}();
// ---------------------------------------------------------------------------
// A subject with the three shapes that matter: a plain leaf, a nested
// aggregate, and a type that reaches itself.
// ---------------------------------------------------------------------------
struct (struct) property_tree_reflect_descent.Vec2Vec2
{
float (field) float property_tree_reflect_descent.Vec2.xx = 0, (field) float property_tree_reflect_descent.Vec2.yy = 0;
}
struct (struct) property_tree_reflect_descent.MaterialMaterial
{
(alias) object.string = stringstring (field) string property_tree_reflect_descent.Material.namename;
(struct) property_tree_reflect_descent.Vec2Vec2 (field) property_tree_reflect_descent.Vec2 property_tree_reflect_descent.Material.offsetoffset;
bool (field) bool property_tree_reflect_descent.Material.twoSidedtwoSided;
}
struct (struct) property_tree_reflect_descent.NodeNode
{
(alias) object.string = stringstring (field) string property_tree_reflect_descent.Node.labellabel;
(struct) property_tree_reflect_descent.Vec2Vec2 (field) property_tree_reflect_descent.Vec2 property_tree_reflect_descent.Node.positionposition;
(struct) property_tree_reflect_descent.MaterialMaterial (field) property_tree_reflect_descent.Material property_tree_reflect_descent.Node.materialmaterial;
(struct) property_tree_reflect_descent.NodeNode* (field) property_tree_reflect_descent.Node* property_tree_reflect_descent.Node.parentparent; // the cycle
}
void void D main() @safemain()
{
// The whole tree is computed at compile time — this is a manifest constant,
// not a walk performed while painting.
enum (constant) property_tree_reflect_descent.FieldRow[] property_tree_reflect_descent.main.rows = [FieldRow("label", "string", 0LU, false, false), FieldRow("position", "Vec2", 0LU, true, false), FieldRow("position.x", "float", 1LU, false, false), FieldRow("position.y", "float", 1LU, false, false), FieldRow("material", "Material", 0LU, true, false), FieldRow("material.name", "string", 1LU, false, false), FieldRow("material.offset", "Vec2", 1LU, true, false), FieldRow("material.offset.x", "float", 2LU, false, false), FieldRow("material.offset.y", "float", 2LU, false, false), FieldRow("material.twoSided", "bool", 1LU, false, false), FieldRow("parent", "Node*", 0LU, true, false), FieldRow("parent.label", "string", 1LU, false, false), FieldRow("parent.position", "Vec2", 1LU, true, false), FieldRow("parent.position.x", "float", 2LU, false, false), FieldRow("parent.position.y", "float", 2LU, false, false), FieldRow("parent.material", "Material", 1LU, true, false), FieldRow("parent.material.name", "string", 2LU, false, false), FieldRow("parent.material.offset", "Vec2", 2LU, true, false), FieldRow("parent.material.offset.x", "float", 3LU, false, false), FieldRow("parent.material.offset.y", "float", 3LU, false, false), FieldRow("parent.material.twoSided", "bool", 2LU, false, false), FieldRow("parent.parent", "Node*", 1LU, false, true)]rows = property_tree_reflect_descent.FieldRow[] property_tree_reflect_descent.describe!(property_tree_reflect_descent.Node, 0LU, "")() pure nothrow @safeFlattens T into pre-ordered rows at compile time.
Seen is the set of aggregate types already open on the current path. It is
what makes the function total: a type that contains itself (directly or through
a pointer) is cut with cut = true instead of re-entering.
describe!(struct) property_tree_reflect_descent.NodeNode();
void std.stdio.writefln!(char, ulong)(in char[] fmt, ulong __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln("%s rows for Node, all known at compile time", (constant) property_tree_reflect_descent.FieldRow[] property_tree_reflect_descent.main.rows = [FieldRow("label", "string", 0LU, false, false), FieldRow("position", "Vec2", 0LU, true, false), FieldRow("position.x", "float", 1LU, false, false), FieldRow("position.y", "float", 1LU, false, false), FieldRow("material", "Material", 0LU, true, false), FieldRow("material.name", "string", 1LU, false, false), FieldRow("material.offset", "Vec2", 1LU, true, false), FieldRow("material.offset.x", "float", 2LU, false, false), FieldRow("material.offset.y", "float", 2LU, false, false), FieldRow("material.twoSided", "bool", 1LU, false, false), FieldRow("parent", "Node*", 0LU, true, false), FieldRow("parent.label", "string", 1LU, false, false), FieldRow("parent.position", "Vec2", 1LU, true, false), FieldRow("parent.position.x", "float", 2LU, false, false), FieldRow("parent.position.y", "float", 2LU, false, false), FieldRow("parent.material", "Material", 1LU, true, false), FieldRow("parent.material.name", "string", 2LU, false, false), FieldRow("parent.material.offset", "Vec2", 2LU, true, false), FieldRow("parent.material.offset.x", "float", 3LU, false, false), FieldRow("parent.material.offset.y", "float", 3LU, false, false), FieldRow("parent.material.twoSided", "bool", 2LU, false, false), FieldRow("parent.parent", "Node*", 1LU, false, true)]rows.(field) ulong [FieldRow("label", "string", 0LU, false, false), FieldRow("position", "Vec2", 0LU, true, false), FieldRow("position.x", "float", 1LU, false, false), FieldRow("position.y", "float", 1LU, false, false), FieldRow("material", "Material", 0LU, true, false), FieldRow("material.name", "string", 1LU, false, false), FieldRow("material.offset", "Vec2", 1LU, true, false), FieldRow("material.offset.x", "float", 2LU, false, false), FieldRow("material.offset.y", "float", 2LU, false, false), FieldRow("material.twoSided", "bool", 1LU, false, false), FieldRow("parent", "Node*", 0LU, true, false), FieldRow("parent.label", "string", 1LU, false, false), FieldRow("parent.position", "Vec2", 1LU, true, false), FieldRow("parent.position.x", "float", 2LU, false, false), FieldRow("parent.position.y", "float", 2LU, false, false), FieldRow("parent.material", "Material", 1LU, true, false), FieldRow("parent.material.name", "string", 2LU, false, false), FieldRow("parent.material.offset", "Vec2", 2LU, true, false), FieldRow("parent.material.offset.x", "float", 3LU, false, false), FieldRow("parent.material.offset.y", "float", 3LU, false, false), FieldRow("parent.material.twoSided", "bool", 2LU, false, false), FieldRow("parent.parent", "Node*", 1LU, false, true)].lengthlength);
void std.stdio.writeln!()() @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln();
foreach ((parameter) property_tree_reflect_descent.FieldRow rr; (constant) property_tree_reflect_descent.FieldRow[] property_tree_reflect_descent.main.rows = [FieldRow("label", "string", 0LU, false, false), FieldRow("position", "Vec2", 0LU, true, false), FieldRow("position.x", "float", 1LU, false, false), FieldRow("position.y", "float", 1LU, false, false), FieldRow("material", "Material", 0LU, true, false), FieldRow("material.name", "string", 1LU, false, false), FieldRow("material.offset", "Vec2", 1LU, true, false), FieldRow("material.offset.x", "float", 2LU, false, false), FieldRow("material.offset.y", "float", 2LU, false, false), FieldRow("material.twoSided", "bool", 1LU, false, false), FieldRow("parent", "Node*", 0LU, true, false), FieldRow("parent.label", "string", 1LU, false, false), FieldRow("parent.position", "Vec2", 1LU, true, false), FieldRow("parent.position.x", "float", 2LU, false, false), FieldRow("parent.position.y", "float", 2LU, false, false), FieldRow("parent.material", "Material", 1LU, true, false), FieldRow("parent.material.name", "string", 2LU, false, false), FieldRow("parent.material.offset", "Vec2", 2LU, true, false), FieldRow("parent.material.offset.x", "float", 3LU, false, false), FieldRow("parent.material.offset.y", "float", 3LU, false, false), FieldRow("parent.material.twoSided", "bool", 2LU, false, false), FieldRow("parent.parent", "Node*", 1LU, false, true)]rows)
{
char[] (local variable) char[] indentindent;
foreach ((local variable) ulong __; 0 .. (local variable) property_tree_reflect_descent.FieldRow rr.(field) ulong property_tree_reflect_descent.FieldRow.depthnesting level; 0 is a direct field of the root
depth)
(local variable) char[] indentindent ~= " ";
const (local variable) const(string) markmark = (local variable) property_tree_reflect_descent.FieldRow rr.(field) bool property_tree_reflect_descent.FieldRow.cutdescent stopped here: the type is already on the path
cut ? " [cut: type already on path]"
: (local variable) property_tree_reflect_descent.FieldRow rr.(field) bool property_tree_reflect_descent.FieldRow.expandablean aggregate we descended into
expandable ? " [subtree]" : "";
void std.stdio.writefln!(char, char[], string, string, string)(in char[] fmt, char[] __param_1, string __param_2, string __param_3, string __param_4) @safeEquivalent to writef(fmt, args, '\n').
writefln("%s%s : %s%s", (local variable) char[] indentindent, (local variable) property_tree_reflect_descent.FieldRow rr.(field) string property_tree_reflect_descent.FieldRow.pathdotted path from the root
path, (local variable) property_tree_reflect_descent.FieldRow rr.(field) string property_tree_reflect_descent.FieldRow.typethe field's static type
type, (local variable) const(string) markmark);
}
void std.stdio.writeln!()() @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln();
// The counts are compile-time facts too, which is what makes a static row
// budget possible at all.
(alias) object.size_t = ulongsize_t (local variable) ulong leavesleaves, (local variable) ulong subtreessubtrees, (local variable) ulong cutscuts;
foreach ((parameter) property_tree_reflect_descent.FieldRow rr; (constant) property_tree_reflect_descent.FieldRow[] property_tree_reflect_descent.main.rows = [FieldRow("label", "string", 0LU, false, false), FieldRow("position", "Vec2", 0LU, true, false), FieldRow("position.x", "float", 1LU, false, false), FieldRow("position.y", "float", 1LU, false, false), FieldRow("material", "Material", 0LU, true, false), FieldRow("material.name", "string", 1LU, false, false), FieldRow("material.offset", "Vec2", 1LU, true, false), FieldRow("material.offset.x", "float", 2LU, false, false), FieldRow("material.offset.y", "float", 2LU, false, false), FieldRow("material.twoSided", "bool", 1LU, false, false), FieldRow("parent", "Node*", 0LU, true, false), FieldRow("parent.label", "string", 1LU, false, false), FieldRow("parent.position", "Vec2", 1LU, true, false), FieldRow("parent.position.x", "float", 2LU, false, false), FieldRow("parent.position.y", "float", 2LU, false, false), FieldRow("parent.material", "Material", 1LU, true, false), FieldRow("parent.material.name", "string", 2LU, false, false), FieldRow("parent.material.offset", "Vec2", 2LU, true, false), FieldRow("parent.material.offset.x", "float", 3LU, false, false), FieldRow("parent.material.offset.y", "float", 3LU, false, false), FieldRow("parent.material.twoSided", "bool", 2LU, false, false), FieldRow("parent.parent", "Node*", 1LU, false, true)]rows)
{
if ((local variable) property_tree_reflect_descent.FieldRow rr.(field) bool property_tree_reflect_descent.FieldRow.cutdescent stopped here: the type is already on the path
cut)
(local variable) ulong cutscuts++;
else if ((local variable) property_tree_reflect_descent.FieldRow rr.(field) bool property_tree_reflect_descent.FieldRow.expandablean aggregate we descended into
expandable)
(local variable) ulong subtreessubtrees++;
else
(local variable) ulong leavesleaves++;
}
void std.stdio.writefln!(char, ulong, ulong, ulong)(in char[] fmt, ulong __param_1, ulong __param_2, ulong __param_3) @safeEquivalent to writef(fmt, args, '\n').
writefln("leaves=%s subtrees=%s cuts=%s", (local variable) ulong leavesleaves, (local variable) ulong subtreessubtrees, (local variable) ulong cutscuts);
}