erased-descent.dhover×214all
#!/usr/bin/env dub
/+ dub.sdl:
    name "property_tree_erased_descent"
    targetPath "build"
    dflags "-preview=in" "-preview=dip1000"
    buildType "checked" {
        buildOptions "optimize" "inline" "debugInfo"
    }
+/
/**
 * The escape from the compile-time recursion limit, and what it costs.
 *
 * [`reflect-descent.d`](./reflect-descent.d) shows that a descent written as one
 * CTFE walk over `T` **fails to build** on a recursive type — the visited-type
 * set is mandatory, and it cuts a type the *second* time it appears anywhere on
 * a path.
 *
 * The Tier-2 pass found that the field does not have this problem, and why.
 * Rust's [derive-macro inspectors](../derive-macro-inspectors.md) generate one
 * impl per type whose child walk crosses a `&mut dyn` boundary, so recursion is
 * a **runtime call**; [DevTools](../devtools-object-inspector.md) fetches each
 * level on expansion, so a cyclic graph is finite work per click; and
 * [`react-jsonschema-form`](../react-jsonschema-form.md) renders a detected
 * `$ref` cycle as an **Expand placeholder** — one level per press, with the cut
 * visible to the reader.
 *
 * This program is those three answers in D:
 *
 *   1. `rowsOf!T` is still compile-time — it reflects fields, labels and leaf-ness
 *      from the type — but the child walk is **erased behind a delegate**, so the
 *      template instantiates once per type rather than once per path, and a
 *      self-referential type compiles.
 *   2. Descent is driven by a **budget**, not by the type: rows are produced to a
 *      requested depth and anything deeper becomes a `cut` row.
 *   3. A `cut` row carries the delegate that would continue, so "expand" is one
 *      more bounded call — the rjsf affordance, in cells.
 *
 * The cost is stated honestly at the end: the erasure is a virtual call and an
 * allocation per open node, which is exactly what the CTFE walk avoided.
 *
 * Run: `dub run --single erased-descent.d`
 */
module 
(module) property_tree_erased_descent

The escape from the compile-time recursion limit, and what it costs.

reflect-descent.d shows that a descent written as one CTFE walk over T fails to build on a recursive type — the visited-type set is mandatory, and it cuts a type the second time it appears anywhere on a path.

The Tier-2 pass found that the field does not have this problem, and why. Rust's derive-macro inspectors generate one impl per type whose child walk crosses a &mut dyn boundary, so recursion is a runtime call; DevTools fetches each level on expansion, so a cyclic graph is finite work per click; and react-jsonschema-form renders a detected $ref cycle as an Expand placeholder — one level per press, with the cut visible to the reader.

This program is those three answers in D:

  1. rowsOf!T is still compile-time — it reflects fields, labels and leaf-ness from the type — but the child walk is erased behind a delegate, so the template instantiates once per type rather than once per path, and a self-referential type compiles.

  2. Descent is driven by a budget, not by the type: rows are produced to a requested depth and anything deeper becomes a cut row.

  3. A cut row carries the delegate that would continue, so "expand" is one more bounded call — the rjsf affordance, in cells.

The cost is stated honestly at the end: the erasure is a virtual call and an allocation per open node, which is exactly what the CTFE walk avoided.

Run

dub run --single erased-descent.d

property_tree_erased_descent
;
import
(package) std
std
.
(module) std.stdio
Category 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:

  1. The lowest layer is the operating system layer. The two main schemes are Windows and Posix.

  2. C's stdio.h which unifies the two operating system schemes.

  3. std.stdio, this module, unifies the various stdio.h implementations into a high level package for D programs.

Source

std/stdio.d

@copyrightCopyright The D Language Foundation 2007-.@licenseBoost License 1.0.@authorsWalter Bright, Andrei Alexandrescu, Alex Rønne Petersen
stdio
:
(alias template) property_tree_erased_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_erased_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);
    }
}
@paramargs the items to write to stdout@throwsIn case of an I/O error, throws an StdioException.
writeln
;
import
(package) std
std
.
(module) std.traits

Templates 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

@copyrightCopyright The D Language Foundation 2005 - 2009.@licenseBoost License 1.0.@authorsWalter Bright, Tomasz Stachowiak (isExpressions), Andrei Alexandrescu, Shin Fujishiro, Robert Clipsham, David Nadlinger, Kenji Hara, Shoichi Kato
traits
:
(alias template) property_tree_erased_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.

@history
  • Returned AliasSeq!"" for interfaces prior to 2.097

FieldNameTuple
,
(alias template) property_tree_erased_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.

@history
  • Returned AliasSeq!(Interface) for interfaces prior to 2.097

Fields
,
(alias template) property_tree_erased_descent.isAggregateType = std.traits.isAggregateType(T)

Detect whether type T is an aggregate type.

isAggregateType
,
(alias template) property_tree_erased_descent.isPointer = std.traits.isPointer(T)

Detect whether type T is a pointer.

isPointer
,
(alias template) property_tree_erased_descent.PointerTarget = std.traits.PointerTarget(T : T*)

Returns the target type of a pointer.

PointerTarget
;
@safe: /// One presented row. `expand` is non-null exactly when the row was cut. struct
(struct) property_tree_erased_descent.Row

One presented row. expand is non-null exactly when the row was cut.

Row
{
(alias) object.string = string
string
(field) string property_tree_erased_descent.Row.path
path
;
(alias) object.string = string
string
(field) string property_tree_erased_descent.Row.type
type
;
(alias) object.size_t = ulong
size_t
(field) ulong property_tree_erased_descent.Row.depth
depth
;
bool
(field) bool property_tree_erased_descent.Row.isLeaf
isLeaf
;
(alias) property_tree_erased_descent.Rows = property_tree_erased_descent.Row[]
Rows
delegate() @safe
(field) property_tree_erased_descent.Row[] delegate() @safe property_tree_erased_descent.Row.expand

null unless this row is a cut

expand
; /// null unless this row is a cut
} alias
(alias) property_tree_erased_descent.Rows = property_tree_erased_descent.Row[]
Rows
= Row[];
/// The erasure boundary: a node knows how to produce its own child rows and /// nothing about who asked. This is the `&mut dyn EguiProbe` of the Rust family. alias
(alias) property_tree_erased_descent.ChildSource = property_tree_erased_descent.Row[] delegate(ulong budget, string prefix, ulong depth) @safe

The erasure boundary: a node knows how to produce its own child rows and nothing about who asked. This is the &mut dyn EguiProbe of the Rust family.

ChildSource
= Rows delegate(
(alias) object.size_t = ulong
size_t
(parameter) ulong budget
budget
,
(alias) object.string = string
string
(parameter) string prefix
prefix
,
(alias) object.size_t = ulong
size_t
(parameter) ulong depth
depth
) @safe;
private enum bool
(constant) bool property_tree_erased_descent.isLeafType!float = true
isLeafType
(T) = is(
(alias) T = float
T
== string) || !
(template instance) std.traits.isAggregateType!float
isAggregateType
!
(alias) T = float
T
;
/// Presents `value` as rows, descending at most `budget` levels. /// /// The recursion below is a **runtime** call through `ChildSource`, so this /// template is instantiated once per type — not once per path — and a type that /// contains itself is ordinary rather than fatal.
(alias) property_tree_erased_descent.Rows = property_tree_erased_descent.Row[]
Rows
property_tree_erased_descent.Row[] property_tree_erased_descent.rowsOf!(property_tree_erased_descent.Material)(ref property_tree_erased_descent.Material value, ulong budget, string prefix = "", ulong depth = 0LU) @safe

Presents value as rows, descending at most budget levels.

The recursion below is a runtime call through ChildSource, so this template is instantiated once per type — not once per path — and a type that contains itself is ordinary rather than fatal.

rowsOf
(T)(ref
(alias) T = property_tree_erased_descent.Material
T
(parameter) property_tree_erased_descent.Material value
value
,
(alias) object.size_t = ulong
size_t
(parameter) ulong budget
budget
,
(alias) object.string = string
string
(parameter) string prefix
prefix
= "",
(alias) object.size_t = ulong
size_t
(parameter) ulong depth
depth
= 0)
{
(alias) property_tree_erased_descent.Rows = property_tree_erased_descent.Row[]
Rows
(local variable) property_tree_erased_descent.Row[] rows
rows
;
static foreach (i, name;
(constant) string property_tree_erased_descent.Material.fun!(name).NameOf = "name"
FieldNameTuple
!T)
{{ alias
(alias) property_tree_erased_descent.rowsOf!(property_tree_erased_descent.Material).F = string
F
=
(alias) std.traits.Fields!(property_tree_erased_descent.Material) = (string, property_tree_erased_descent.Vec2)

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)));
@history
  • Returned AliasSeq!(Interface) for interfaces prior to 2.097

Fields
!
(alias) T = property_tree_erased_descent.Material
T
[
(constant) ulong property_tree_erased_descent.rowsOf!(property_tree_erased_descent.Material).i = 0LU
i
];
static if (
(template instance) std.traits.isPointer!string
isPointer
!
(alias) property_tree_erased_descent.rowsOf!(property_tree_erased_descent.Material).F = string
F
&&
(template instance) isAggregateType!(PointerTarget!F)
isAggregateType
!(
(alias) T = property_tree_erased_descent.Node
PointerTarget
!
(unresolved type) F
F
))
alias
(alias) property_tree_erased_descent.rowsOf!(property_tree_erased_descent.Node).Target = property_tree_erased_descent.Node
Target
=
(alias) T = property_tree_erased_descent.Node
PointerTarget
!
(alias) property_tree_erased_descent.rowsOf!(property_tree_erased_descent.Node).F = property_tree_erased_descent.Node*
F
;
else alias
(alias) property_tree_erased_descent.rowsOf!(property_tree_erased_descent.Material).Target = string
Target
=
(alias) property_tree_erased_descent.rowsOf!(property_tree_erased_descent.Material).F = string
F
;
const
(local variable) const(string) path
path
=
(parameter) string prefix
prefix
~
(constant) string property_tree_erased_descent.rowsOf!(property_tree_erased_descent.Material).name = "name"
name
;
enum
(constant) bool property_tree_erased_descent.rowsOf!(property_tree_erased_descent.Material).leaf = true
leaf
=
(template instance) property_tree_erased_descent.isLeafType!string
isLeafType
!
(alias) property_tree_erased_descent.rowsOf!(property_tree_erased_descent.Material).Target = string
Target
;
static if (leaf) {
(local variable) property_tree_erased_descent.Row[] rows
rows
~=
(struct) property_tree_erased_descent.Row

One presented row. expand is non-null exactly when the row was cut.

Row
(
(local variable) const(string) path
path
,
(darray) string
F
.
(constant) string string.stringof = "string"
stringof
,
(parameter) ulong depth
depth
, true, null);
} else { // The child walk, erased. Capturing it as a delegate is what stops // the template from re-entering itself at compile time.
(alias) property_tree_erased_descent.ChildSource = property_tree_erased_descent.Row[] delegate(ulong budget, string prefix, ulong depth) @safe

The erasure boundary: a node knows how to produce its own child rows and nothing about who asked. This is the &mut dyn EguiProbe of the Rust family.

ChildSource
(local variable) property_tree_erased_descent.Row[] delegate(ulong budget, string prefix, ulong depth) @safe source
source
= (
(alias) object.size_t = ulong
size_t
(parameter) ulong b
b
,
(alias) object.string = string
string
(parameter) string p
p
,
(alias) object.size_t = ulong
size_t
(parameter) ulong d
d
) @safe {
static if (
(template instance) std.traits.isPointer!(property_tree_erased_descent.Vec2)
isPointer
!
(alias) property_tree_erased_descent.rowsOf!(property_tree_erased_descent.Material).F = property_tree_erased_descent.Vec2
F
)
{ auto
(local variable) property_tree_erased_descent.Node* target
target
= __traits(getMember, value, name);
if (
(local variable) property_tree_erased_descent.Node* target
target
is null)
return
(darray) property_tree_erased_descent.Row[]
Rows
.
(constant) property_tree_erased_descent.Row[] property_tree_erased_descent.Row[].init = null
init
;
return
property_tree_erased_descent.Row[] property_tree_erased_descent.rowsOf!(property_tree_erased_descent.Node)(ref property_tree_erased_descent.Node value, ulong budget, string prefix = "", ulong depth = 0LU) @safe

Presents value as rows, descending at most budget levels.

The recursion below is a runtime call through ChildSource, so this template is instantiated once per type — not once per path — and a type that contains itself is ordinary rather than fatal.

rowsOf
!
(alias) property_tree_erased_descent.rowsOf!(property_tree_erased_descent.Node).Target = property_tree_erased_descent.Node
Target
(*
(local variable) property_tree_erased_descent.Node* target
target
,
(parameter) ulong b
b
,
(parameter) string p
p
,
(parameter) ulong d
d
);
} else return
property_tree_erased_descent.Row[] property_tree_erased_descent.rowsOf!(property_tree_erased_descent.Vec2)(ref property_tree_erased_descent.Vec2 value, ulong budget, string prefix = "", ulong depth = 0LU) pure nothrow @safe

Presents value as rows, descending at most budget levels.

The recursion below is a runtime call through ChildSource, so this template is instantiated once per type — not once per path — and a type that contains itself is ordinary rather than fatal.

rowsOf
!
(alias) property_tree_erased_descent.rowsOf!(property_tree_erased_descent.Material).Target = property_tree_erased_descent.Vec2
Target
(__traits(getMember, value, name),
(parameter) ulong b
b
,
(parameter) string p
p
,
(parameter) ulong d
d
);
}; if (
(parameter) ulong budget
budget
== 0)
{ // The rjsf answer: a cut is a row with an affordance, not a hole.
(local variable) property_tree_erased_descent.Row[] rows
rows
~=
(struct) property_tree_erased_descent.Row

One presented row. expand is non-null exactly when the row was cut.

Row
(
(local variable) const(string) path
path
,
(struct) property_tree_erased_descent.Vec2
F
.
(constant) string property_tree_erased_descent.Vec2.stringof = "Vec2"
stringof
,
(parameter) ulong depth
depth
, false,
() @safe =>
(local variable) property_tree_erased_descent.Row[] delegate(ulong budget, string prefix, ulong depth) @safe source
source
(1,
(local variable) const(string) path
path
~ ".",
(parameter) ulong depth
depth
+ 1));
} else {
(local variable) property_tree_erased_descent.Row[] rows
rows
~=
(struct) property_tree_erased_descent.Row

One presented row. expand is non-null exactly when the row was cut.

Row
(
(local variable) const(string) path
path
,
(struct) property_tree_erased_descent.Vec2
F
.
(constant) string property_tree_erased_descent.Vec2.stringof = "Vec2"
stringof
,
(parameter) ulong depth
depth
, false, null);
(local variable) property_tree_erased_descent.Row[] rows
rows
~=
(local variable) property_tree_erased_descent.Row[] delegate(ulong budget, string prefix, ulong depth) @safe source
source
(
(parameter) ulong budget
budget
- 1,
(local variable) const(string) path
path
~ ".",
(parameter) ulong depth
depth
+ 1);
} } }} return
(local variable) property_tree_erased_descent.Row[] rows
rows
;
} // --------------------------------------------------------------------------- // The same subject as reflect-descent.d, including the type that reaches itself. // --------------------------------------------------------------------------- struct
(struct) property_tree_erased_descent.Vec2
Vec2
{ float
(field) float property_tree_erased_descent.Vec2.x
x
= 0,
(field) float property_tree_erased_descent.Vec2.y
y
= 0;
} struct
(struct) property_tree_erased_descent.Material
Material
{
(alias) object.string = string
string
(field) string property_tree_erased_descent.Material.name
name
;
(struct) property_tree_erased_descent.Vec2
Vec2
(field) property_tree_erased_descent.Vec2 property_tree_erased_descent.Material.offset
offset
;
} struct
(struct) property_tree_erased_descent.Node
Node
{
(alias) object.string = string
string
(field) string property_tree_erased_descent.Node.label
label
;
(struct) property_tree_erased_descent.Vec2
Vec2
(field) property_tree_erased_descent.Vec2 property_tree_erased_descent.Node.position
position
;
(struct) property_tree_erased_descent.Material
Material
(field) property_tree_erased_descent.Material property_tree_erased_descent.Node.material
material
;
(struct) property_tree_erased_descent.Node
Node
*
(field) property_tree_erased_descent.Node* property_tree_erased_descent.Node.parent
parent
;
} private void
void property_tree_erased_descent.print(in property_tree_erased_descent.Row[] rows) @safe
print
(in
(alias) property_tree_erased_descent.Rows = property_tree_erased_descent.Row[]
Rows
(parameter) const(property_tree_erased_descent.Row[]) rows
rows
)
{ foreach (
(parameter) const(property_tree_erased_descent.Row) r
r
;
(parameter) const(property_tree_erased_descent.Row[]) rows
rows
)
{ char[]
(local variable) char[] indent
indent
;
foreach (
(local variable) ulong _
_
; 0 ..
(local variable) const(property_tree_erased_descent.Row) r
r
.
(field) ulong property_tree_erased_descent.Row.depth
depth
)
(local variable) char[] indent
indent
~= " ";
const
(local variable) const(string) mark
mark
=
(local variable) const(property_tree_erased_descent.Row) r
r
.
(field) property_tree_erased_descent.Row[] delegate() @safe property_tree_erased_descent.Row.expand

null unless this row is a cut

expand
!is null ? " ← cut (expandable)"
:
(local variable) const(property_tree_erased_descent.Row) r
r
.
(field) bool property_tree_erased_descent.Row.isLeaf
isLeaf
? "" : " [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) @safe

Equivalent to writef(fmt, args, '\n').

writefln
("%s%s : %s%s",
(local variable) char[] indent
indent
,
(local variable) const(property_tree_erased_descent.Row) r
r
.
(field) string property_tree_erased_descent.Row.path
path
,
(local variable) const(property_tree_erased_descent.Row) r
r
.
(field) string property_tree_erased_descent.Row.type
type
,
(local variable) const(string) mark
mark
);
} } void
void D main() @safe
main
()
{ // A genuinely cyclic value: the node is its own parent.
(struct) property_tree_erased_descent.Node
Node
(local variable) property_tree_erased_descent.Node root
root
=
(struct) property_tree_erased_descent.Node
Node
("root");
() @trusted {
(local variable) property_tree_erased_descent.Node root
root
.
(field) property_tree_erased_descent.Node* property_tree_erased_descent.Node.parent
parent
= &
(local variable) property_tree_erased_descent.Node root
root
; }();
void std.stdio.writeln!string(string __param_0) @safe

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);
    }
}
@paramargs the items to write to stdout@throwsIn case of an I/O error, throws an StdioException.
writeln
("budget = 1 — one level, everything deeper is a cut row");
auto
(local variable) property_tree_erased_descent.Row[] shallow
shallow
=
property_tree_erased_descent.Row[] property_tree_erased_descent.rowsOf!(property_tree_erased_descent.Node)(ref property_tree_erased_descent.Node value, ulong budget, string prefix = "", ulong depth = 0LU) @safe

Presents value as rows, descending at most budget levels.

The recursion below is a runtime call through ChildSource, so this template is instantiated once per type — not once per path — and a type that contains itself is ordinary rather than fatal.

rowsOf
(
(local variable) property_tree_erased_descent.Node root
root
, 1);
void property_tree_erased_descent.print(in property_tree_erased_descent.Row[] rows) @safe
print
(
(local variable) property_tree_erased_descent.Row[] shallow
shallow
);
void std.stdio.writeln!()() @safe

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);
    }
}
@paramargs the items to write to stdout@throwsIn case of an I/O error, throws an StdioException.
writeln
();
void std.stdio.writefln!(char, ulong, ulong)(in char[] fmt, ulong __param_1, ulong __param_2) @safe

Equivalent to writef(fmt, args, '\n').

writefln
("%s rows, %s of them cuts",
(local variable) property_tree_erased_descent.Row[] shallow
shallow
.
(field) ulong property_tree_erased_descent.Row[].length
length
,
ulong property_tree_erased_descent.countCuts(in property_tree_erased_descent.Row[] rows) pure nothrow @nogc @safe
countCuts
(
(local variable) property_tree_erased_descent.Row[] shallow
shallow
));
void std.stdio.writeln!()() @safe

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);
    }
}
@paramargs the items to write to stdout@throwsIn case of an I/O error, throws an StdioException.
writeln
();
void std.stdio.writeln!string(string __param_0) @safe

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);
    }
}
@paramargs the items to write to stdout@throwsIn case of an I/O error, throws an StdioException.
writeln
("expanding the first cut — one more bounded level, on demand");
foreach (
(parameter) property_tree_erased_descent.Row r
r
;
(local variable) property_tree_erased_descent.Row[] shallow
shallow
)
{ if (
(local variable) property_tree_erased_descent.Row r
r
.
(field) property_tree_erased_descent.Row[] delegate() @safe property_tree_erased_descent.Row.expand

null unless this row is a cut

expand
!is null)
{
void property_tree_erased_descent.print(in property_tree_erased_descent.Row[] rows) @safe
print
(
(local variable) property_tree_erased_descent.Row r
r
.
(field) property_tree_erased_descent.Row[] delegate() @safe property_tree_erased_descent.Row.expand

null unless this row is a cut

expand
());
break; } }
void std.stdio.writeln!()() @safe

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);
    }
}
@paramargs the items to write to stdout@throwsIn case of an I/O error, throws an StdioException.
writeln
();
// The same value at three budgets: the row count is a function of the // budget, not of the (infinite) value graph. foreach (
(local variable) int budget
budget
; 0 .. 4)
{ auto
(local variable) property_tree_erased_descent.Row[] rows
rows
=
property_tree_erased_descent.Row[] property_tree_erased_descent.rowsOf!(property_tree_erased_descent.Node)(ref property_tree_erased_descent.Node value, ulong budget, string prefix = "", ulong depth = 0LU) @safe

Presents value as rows, descending at most budget levels.

The recursion below is a runtime call through ChildSource, so this template is instantiated once per type — not once per path — and a type that contains itself is ordinary rather than fatal.

rowsOf
(
(local variable) property_tree_erased_descent.Node root
root
,
(local variable) int budget
budget
);
void std.stdio.writefln!(char, int, ulong, ulong)(in char[] fmt, int __param_1, ulong __param_2, ulong __param_3) @safe

Equivalent to writef(fmt, args, '\n').

writefln
("budget=%s → %s rows (%s cuts)",
(local variable) int budget
budget
,
(local variable) property_tree_erased_descent.Row[] rows
rows
.
(field) ulong property_tree_erased_descent.Row[].length
length
,
ulong property_tree_erased_descent.countCuts(in property_tree_erased_descent.Row[] rows) pure nothrow @nogc @safe
countCuts
(
(local variable) property_tree_erased_descent.Row[] rows
rows
));
}
void std.stdio.writeln!()() @safe

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);
    }
}
@paramargs the items to write to stdout@throwsIn case of an I/O error, throws an StdioException.
writeln
();
void std.stdio.writeln!string(string __param_0) @safe

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);
    }
}
@paramargs the items to write to stdout@throwsIn case of an I/O error, throws an StdioException.
writeln
("cost: one delegate per open node, one virtual call per descent —");
void std.stdio.writeln!string(string __param_0) @safe

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);
    }
}
@paramargs the items to write to stdout@throwsIn case of an I/O error, throws an StdioException.
writeln
("what the compile-time walk avoided, in exchange for terminating.");
} private
(alias) object.size_t = ulong
size_t
ulong property_tree_erased_descent.countCuts(in property_tree_erased_descent.Row[] rows) pure nothrow @nogc @safe
countCuts
(in
(alias) property_tree_erased_descent.Rows = property_tree_erased_descent.Row[]
Rows
(parameter) const(property_tree_erased_descent.Row[]) rows
rows
) pure nothrow @nogc
{
(alias) object.size_t = ulong
size_t
(local variable) ulong n
n
;
foreach (
(parameter) const(property_tree_erased_descent.Row) r
r
;
(parameter) const(property_tree_erased_descent.Row[]) rows
rows
)
if (
(local variable) const(property_tree_erased_descent.Row) r
r
.
(field) property_tree_erased_descent.Row[] delegate() @safe property_tree_erased_descent.Row.expand

null unless this row is a cut

expand
!is null)
(local variable) ulong n
n
++;
return
(local variable) ulong n
n
;
}