edit-commands.dhover×553all
#!/usr/bin/env dub
/+ dub.sdl:
    name "property_tree_edit_commands"
    targetPath "build"
    dflags "-preview=in" "-preview=dip1000"
    buildType "checked" {
        buildOptions "optimize" "inline" "debugInfo"
    }
+/
/**
 * The mutation contract (fork D5), with read-write as the default
 * and read-only as a policy (fork D6, as decided).
 *
 * Under test:
 *   C12. An edit is a VALUE — `(path, newValue, phase)` — not a callback.
 *        Applying one returns its INVERSE, so undo/redo is a host-owned stack
 *        of the same value type and the component contains no transaction
 *        machinery. (The corpus rule "undo belongs to the host", made cheap.)
 *   C13. `@readOnly` and a component-level policy both refuse the write at the
 *        SAME place — inside the generated dispatch — so no view can bypass it.
 *   C14. `phase` separates a drag's previews from its commit (Unreal's
 *        `SetValue` flags). Previews mutate; only a commit yields an undo
 *        entry. This is the distinction Godot's `changing` flag does NOT make.
 *   C15. The D-specific wrinkle: `SumType.opAssign` is `@system` when another
 *        member has indirections, so a variant switch needs one `@trusted`
 *        seam — and the edit vocabulary needs a `variant` case, because a
 *        switch is not an assignment of any leaf type.
 *
 * Run: `dub run --single edit-commands.d`
 */
module 
(module) property_tree_edit_commands

The mutation contract (fork D5), with read-write as the default and read-only as a policy (fork D6, as decided).

Under test: C12. An edit is a VALUE — (path, newValue, phase) — not a callback. Applying one returns its INVERSE, so undo/redo is a host-owned stack of the same value type and the component contains no transaction machinery. (The corpus rule "undo belongs to the host", made cheap.) C13. @readOnly and a component-level policy both refuse the write at the SAME place — inside the generated dispatch — so no view can bypass it. C14. phase separates a drag's previews from its commit (Unreal's SetValue flags). Previews mutate; only a commit yields an undo entry. This is the distinction Godot's changing flag does NOT make. C15. The D-specific wrinkle: SumType.opAssign is @system when another member has indirections, so a variant switch needs one @trusted seam — and the edit vocabulary needs a variant case, because a switch is not an assignment of any leaf type.

Run

dub run --single edit-commands.d

property_tree_edit_commands
;
import
(package) std
std
.
(module) std.conv

A one-stop shop for converting values from one type to another.

Category Functions
Generic asOriginalType castFrom parse to toChars bitCast
Strings text wtext dtext writeText writeWText writeDText hexString
Numeric octal roundTo signed unsigned
Exceptions ConvException ConvOverflowException

Source

std/conv.d

@copyrightCopyright The D Language Foundation 2007-.@licenseBoost License 1.0.@authorsWalter Bright, Andrei Alexandrescu, Shin Fujishiro, Adam D. Ruppe, Kenji Hara
conv
:
(alias template) property_tree_edit_commands.text = std.conv.text(T...)(T args) if (T.length > 0)

Convenience functions for converting one or more arguments of any type into text (the three character widths).

text
,
(alias template) property_tree_edit_commands.to = std.conv.to(T)

The to template converts a value from one type to another. The source type is deduced and the target type must be specified, for example the expression to`!int(42.0)` converts the number 42 from `double` to `int`. The conversion is "safe", i.e., it checks for overflow; to!int(4.2e10) would throw the ConvOverflowException exception. Overflow checks are only inserted when necessary, e.g., ``to!double(42) does not do any checking because any int fits in a double.

Conversions from string to numeric types differ from the C equivalents atoi() and atol() by checking for overflow and not allowing whitespace.

For conversion of strings to signed types, the grammar recognized is: Integer: Sign UnsignedInteger UnsignedInteger Sign: + -

For conversion to unsigned types, the grammar recognized is: UnsignedInteger: DecimalDigit DecimalDigit UnsignedInteger

to
;
import
(package) std
std
.
(module) std.sumtype

SumType is a generic discriminated union implementation that uses design-by-introspection to generate safe and efficient code. Its features include:

  • Pattern matching.

  • Support for self-referential types.

  • Full attribute correctness (pure, @safe, @nogc, and nothrow are inferred whenever possible).

  • A type-safe and memory-safe API compatible with DIP 1000 (scope).

  • No dependency on runtime type information (TypeInfo).

  • Compatibility with BetterC.

List of examples

Source

std/sumtype.d

Examples

Basic usage

import std.math.operations : isClose;

struct Fahrenheit { double value; }
struct Celsius { double value; }
struct Kelvin { double value; }

alias Temperature = SumType!(Fahrenheit, Celsius, Kelvin);

// Construct from any of the member types.
Temperature t1 = Fahrenheit(98.6);
Temperature t2 = Celsius(100);
Temperature t3 = Kelvin(273);

// Use pattern matching to access the value.
Fahrenheit toFahrenheit(Temperature t)
{
    return Fahrenheit(
        t.match!(
            (Fahrenheit f) => f.value,
            (Celsius c) => c.value * 9.0/5 + 32,
            (Kelvin k) => k.value * 9.0/5 - 459.4
        )
    );
}

assert(toFahrenheit(t1).value.isClose(98.6));
assert(toFahrenheit(t2).value.isClose(212));
assert(toFahrenheit(t3).value.isClose(32));

// Use ref to modify the value in place.
void freeze(ref Temperature t)
{
    t.match!(
        (ref Fahrenheit f) => f.value = 32,
        (ref Celsius c) => c.value = 0,
        (ref Kelvin k) => k.value = 273
    );
}

freeze(t1);
assert(toFahrenheit(t1).value.isClose(32));

// Use a catch-all handler to give a default result.
bool isFahrenheit(Temperature t)
{
    return t.match!(
        (Fahrenheit f) => true,
        _ => false
    );
}

assert(isFahrenheit(t1));
assert(!isFahrenheit(t2));
assert(!isFahrenheit(t3));

Matching with an overload set

Instead of writing match handlers inline as lambdas, you can write them as overloads of a function. An alias can be used to create an additional overload for the SumType itself.

For example, with this overload set:

string handle(int n) { return "got an int"; }
string handle(string s) { return "got a string"; }
string handle(double d) { return "got a double"; }
alias handle = match!handle;

Usage would look like this:

alias ExampleSumType = SumType!(int, string, double);

ExampleSumType a = 123;
ExampleSumType b = "hello";
ExampleSumType c = 3.14;

assert(a.handle == "got an int");
assert(b.handle == "got a string");
assert(c.handle == "got a double");

Recursive SumTypes

This example makes use of the special placeholder type This to define a recursive data type: an abstract syntax tree for representing simple arithmetic expressions.

import std.functional : partial;
import std.traits : EnumMembers;
import std.typecons : Tuple;

enum Op : string
{
    Plus  = "+",
    Minus = "-",
    Times = "*",
    Div   = "/"
}

// An expression is either
//  - a number,
//  - a variable, or
//  - a binary operation combining two sub-expressions.
alias Expr = SumType!(
    double,
    string,
    Tuple!(Op, "op", This*, "lhs", This*, "rhs")
);

// Shorthand for Tuple!(Op, "op", Expr*, "lhs", Expr*, "rhs"),
// the Tuple type above with Expr substituted for This.
alias BinOp = Expr.Types[2];

// Factory function for number expressions
Expr* num(double value)
{
    return new Expr(value);
}

// Factory function for variable expressions
Expr* var(string name)
{
    return new Expr(name);
}

// Factory function for binary operation expressions
Expr* binOp(Op op, Expr* lhs, Expr* rhs)
{
    return new Expr(BinOp(op, lhs, rhs));
}

// Convenience wrappers for creating BinOp expressions
alias sum  = partial!(binOp, Op.Plus);
alias diff = partial!(binOp, Op.Minus);
alias prod = partial!(binOp, Op.Times);
alias quot = partial!(binOp, Op.Div);

// Evaluate expr, looking up variables in env
double eval(Expr expr, double[string] env)
{
    return expr.match!(
        (double num) => num,
        (string var) => env[var],
        (BinOp bop)
        {
            double lhs = eval(*bop.lhs, env);
            double rhs = eval(*bop.rhs, env);
            final switch (bop.op)
            {
                static foreach (op; EnumMembers!Op)
                {
                    case op:
                        return mixin("lhs" ~ op ~ "rhs");
                }
            }
        }
    );
}

// Return a "pretty-printed" representation of expr
string pprint(Expr expr)
{
    import std.format : format;

    return expr.match!(
        (double num) => "%g".format(num),
        (string var) => var,
        (BinOp bop) => "(%s %s %s)".format(
            pprint(*bop.lhs),
            cast(string) bop.op,
            pprint(*bop.rhs)
        )
    );
}

Expr* myExpr = sum(var("a"), prod(num(2), var("b")));
double[string] myEnv = ["a":3, "b":4, "c":7];

assert(eval(*myExpr, myEnv) == 11);
assert(pprint(*myExpr) == "(a + (2 * b))");
@licenseBoost License 1.0@authorsPaul Backus
sumtype
:
(alias template) property_tree_edit_commands.match = std.sumtype.match(handlers...)

Calls a type-appropriate function with the value held in a SumType.

For each possible type the SumType can hold, the given handlers are checked, in order, to see whether they accept a single argument of that type. The first one that does is chosen as the match for that type. (Note that the first match may not always be the most exact match. See "Avoiding unintentional matches" for one common pitfall.)

Every type must have a matching handler, and every handler must match at least one type. This is enforced at compile time.

Handlers may be functions, delegates, or objects with opCall overloads. If a function with more than one overload is given as a handler, all of the overloads are considered as potential matches.

Templated handlers are also accepted, and will match any type for which they can be implicitly instantiated. (Remember that a function literal without an explicit argument type is considered a template.)

If multiple SumTypes are passed to match, their values are passed to the handlers as separate arguments, and matching is done for each possible combination of value types. See "Multiple dispatch" for an example.

@returnsThe value returned from the handler that matches the currently-held type.@seevisit
match
,
(alias struct) property_tree_edit_commands.SumType = std.sumtype.SumType(Types...) if (is(NoDuplicates!Types == Types) && (Types.length > 0))

A tagged union that can hold a single value from any of a specified set of types.

The value in a SumType can be operated on using pattern matching.

To avoid ambiguity, duplicate types are not allowed (but see the "basic usage" example for a workaround).

The special type This can be used as a placeholder to create self-referential types, just like with Algebraic. See the "Recursive SumTypes" example for usage.

A SumType is initialized by default to hold the .init value of its first member type, just like a regular union. The version identifier SumTypeNoDefaultCtor can be used to disable this behavior.

@seeAlgebraic
SumType
;
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_edit_commands.hasUDA = std.traits.hasUDA(alias symbol, alias attribute)

Determine if a symbol has a given user-defined attribute.

@seegetUDAs
hasUDA
,
(alias template) property_tree_edit_commands.isAggregateType = std.traits.isAggregateType(T)

Detect whether type T is an aggregate type.

isAggregateType
,
(alias template) property_tree_edit_commands.isArray = std.traits.isArray(T)

Detect whether type T is an array (static or dynamic; for associative arrays see isAssociativeArray).

isArray
,
(alias template) property_tree_edit_commands.isSomeString = std.traits.isSomeString(T)

Detect whether T is one of the built-in string types.

The built-in string types are Char[], where Char is any of char, wchar or dchar, with or without qualifiers.

Static arrays of characters (like char[80]) are not considered built-in string types.

isSomeString
;
@safe: enum
(enum) property_tree_edit_commands.readOnly
readOnly
;
// ── the edit vocabulary (C12) ──────────────────────────────────────────────── enum
(enum) property_tree_edit_commands.Phase
Phase
: ubyte {
(enum value) property_tree_edit_commands.Phase.preview = cast(ubyte)0u
preview
,
(enum value) property_tree_edit_commands.Phase.commit = 1
commit
}
/// Every leaf the component can produce an edit for, as one value type. struct
(struct) property_tree_edit_commands.EditValue

Every leaf the component can produce an edit for, as one value type.

EditValue
{ enum
(enum) property_tree_edit_commands.EditValue.Kind
Kind
: ubyte {
(enum value) property_tree_edit_commands.EditValue.Kind.none = cast(ubyte)0u
none
,
(enum value) property_tree_edit_commands.EditValue.Kind.boolean = 1
boolean
,
(enum value) property_tree_edit_commands.EditValue.Kind.integral = 2
integral
,
(enum value) property_tree_edit_commands.EditValue.Kind.floating = 3
floating
,
(enum value) property_tree_edit_commands.EditValue.Kind.text = 4
text
,
(enum value) property_tree_edit_commands.EditValue.Kind.variant = 5
variant
}
(enum) property_tree_edit_commands.EditValue.Kind
Kind
(field) property_tree_edit_commands.EditValue.Kind property_tree_edit_commands.EditValue.kind
kind
;
bool
(field) bool property_tree_edit_commands.EditValue.b
b
; long
(field) long property_tree_edit_commands.EditValue.i
i
; double
(field) double property_tree_edit_commands.EditValue.f
f
;
(alias) object.string = string
string
(field) string property_tree_edit_commands.EditValue.s
s
;
static
(struct) property_tree_edit_commands.EditValue

Every leaf the component can produce an edit for, as one value type.

EditValue
property_tree_edit_commands.EditValue property_tree_edit_commands.EditValue.of(bool v) @safe
of
(bool
(parameter) bool v
v
) =>
(struct) property_tree_edit_commands.EditValue

Every leaf the component can produce an edit for, as one value type.

EditValue
(
(enum) property_tree_edit_commands.EditValue.Kind
Kind
.
(enum value) property_tree_edit_commands.EditValue.Kind.boolean = 1
boolean
,
(parameter) bool v
v
);
static
(struct) property_tree_edit_commands.EditValue

Every leaf the component can produce an edit for, as one value type.

EditValue
property_tree_edit_commands.EditValue property_tree_edit_commands.EditValue.of(long v) @safe
of
(long
(parameter) long v
v
) =>
(struct) property_tree_edit_commands.EditValue

Every leaf the component can produce an edit for, as one value type.

EditValue
(
(enum) property_tree_edit_commands.EditValue.Kind
Kind
.
(enum value) property_tree_edit_commands.EditValue.Kind.integral = 2
integral
, false,
(parameter) long v
v
);
static
(struct) property_tree_edit_commands.EditValue

Every leaf the component can produce an edit for, as one value type.

EditValue
property_tree_edit_commands.EditValue property_tree_edit_commands.EditValue.of(double v) @safe
of
(double
(parameter) double v
v
) =>
(struct) property_tree_edit_commands.EditValue

Every leaf the component can produce an edit for, as one value type.

EditValue
(
(enum) property_tree_edit_commands.EditValue.Kind
Kind
.
(enum value) property_tree_edit_commands.EditValue.Kind.floating = 3
floating
, false, 0,
(parameter) double v
v
);
static
(struct) property_tree_edit_commands.EditValue

Every leaf the component can produce an edit for, as one value type.

EditValue
property_tree_edit_commands.EditValue property_tree_edit_commands.EditValue.of(string v) @safe
of
(
(alias) object.string = string
string
(parameter) string v
v
) =>
(struct) property_tree_edit_commands.EditValue

Every leaf the component can produce an edit for, as one value type.

EditValue
(
(enum) property_tree_edit_commands.EditValue.Kind
Kind
.
(enum value) property_tree_edit_commands.EditValue.Kind.text = 4
text
, false, 0, 0,
(parameter) string v
v
);
static
(struct) property_tree_edit_commands.EditValue

Every leaf the component can produce an edit for, as one value type.

EditValue
property_tree_edit_commands.EditValue property_tree_edit_commands.EditValue.variantOf(string name) @safe
variantOf
(
(alias) object.string = string
string
(parameter) string name
name
)
=>
(struct) property_tree_edit_commands.EditValue

Every leaf the component can produce an edit for, as one value type.

EditValue
(
(enum) property_tree_edit_commands.EditValue.Kind
Kind
.
(enum value) property_tree_edit_commands.EditValue.Kind.variant = 5
variant
, false, 0, 0,
(parameter) string name
name
);
(alias) object.string = string
string
string property_tree_edit_commands.EditValue.toString() const pure @safe
toString
() const pure
{ final switch (
(field) property_tree_edit_commands.EditValue.Kind property_tree_edit_commands.EditValue.kind
kind
)
{ case
(enum) property_tree_edit_commands.EditValue.Kind
Kind
.
(enum value) property_tree_edit_commands.EditValue.Kind.none = cast(ubyte)0u
none
: return "∅";
case
(enum) property_tree_edit_commands.EditValue.Kind
Kind
.
(enum value) property_tree_edit_commands.EditValue.Kind.boolean = 1
boolean
: return
(field) bool property_tree_edit_commands.EditValue.b
b
? "true" : "false";
case
(enum) property_tree_edit_commands.EditValue.Kind
Kind
.
(enum value) property_tree_edit_commands.EditValue.Kind.integral = 2
integral
: return
(field) long property_tree_edit_commands.EditValue.i
i
.
string std.conv.to!string.to!(const(long))(const(long) __param_0) pure nothrow @safe

The to template converts a value from one type to another. The source type is deduced and the target type must be specified, for example the expression to`!int(42.0)` converts the number 42 from `double` to `int`. The conversion is "safe", i.e., it checks for overflow; to!int(4.2e10) would throw the ConvOverflowException exception. Overflow checks are only inserted when necessary, e.g., ``to!double(42) does not do any checking because any int fits in a double.

Conversions from string to numeric types differ from the C equivalents atoi() and atol() by checking for overflow and not allowing whitespace.

For conversion of strings to signed types, the grammar recognized is: Integer: Sign UnsignedInteger UnsignedInteger Sign: + -

For conversion to unsigned types, the grammar recognized is: UnsignedInteger: DecimalDigit DecimalDigit UnsignedInteger

Examples

Converting a value to its own type (useful mostly for generic code) simply returns its argument.

int a = 42;
int b = to!int(a);
double c = to!double(3.14); // c is double with value 3.14

Converting among numeric types is a safe way to cast them around.

Conversions from floating-point types to integral types allow loss of precision (the fractional part of a floating-point number). The conversion is truncating towards zero, the same way a cast would truncate. (To round a floating point value when casting to an integral, use roundTo.)

import std.exception : assertThrown;

int a = 420;
assert(to!long(a) == a);
assertThrown!ConvOverflowException(to!byte(a));

assert(to!int(4.2e6) == 4200000);
assertThrown!ConvOverflowException(to!uint(-3.14));
assert(to!uint(3.14) == 3);
assert(to!uint(3.99) == 3);
assert(to!int(-3.99) == -3);

When converting strings to numeric types, note that D hexadecimal and binary literals are not handled. Neither the prefixes that indicate the base, nor the horizontal bar used to separate groups of digits are recognized. This also applies to the suffixes that indicate the type.

To work around this, you can specify a radix for conversions involving numbers.

auto str = to!string(42, 16);
assert(str == "2A");
auto i = to!int(str, 16);
assert(i == 42);

Conversions from integral types to floating-point types always succeed, but might lose accuracy. The largest integers with a predecessor representable in floating-point format are 2^24-1 for float, 2^53-1 for double, and 2^64-1 for real (when real is 80-bit, e.g. on Intel machines).

// 2^24 - 1, largest proper integer representable as float
int a = 16_777_215;
assert(to!int(to!float(a)) == a);
assert(to!int(to!float(-a)) == -a);

Conversion from string types to char types enforces the input to consist of a single code point, and said code point must fit in the target type. Otherwise, ConvException is thrown.

import std.exception : assertThrown;

assert(to!char("a") == 'a');
assertThrown(to!char("ñ")); // 'ñ' does not fit into a char
assert(to!wchar("ñ") == 'ñ');
assertThrown(to!wchar("😃")); // '😃' does not fit into a wchar
assert(to!dchar("😃") == '😃');

// Using wstring or dstring as source type does not affect the result
assert(to!char("a"w) == 'a');
assert(to!char("a"d) == 'a');

// Two code points cannot be converted to a single one
assertThrown(to!char("ab"));

Converting an array to another array type works by converting each element in turn. Associative arrays can be converted to associative arrays as long as keys and values can in turn be converted.

import std.string : split;

int[] a = [1, 2, 3];
auto b = to!(float[])(a);
assert(b == [1.0f, 2, 3]);
string str = "1 2 3 4 5 6";
auto numbers = to!(double[])(split(str));
assert(numbers == [1.0, 2, 3, 4, 5, 6]);
int[string] c;
c["a"] = 1;
c["b"] = 2;
auto d = to!(double[wstring])(c);
assert(d["a"w] == 1 && d["b"w] == 2);

Conversions operate transitively, meaning that they work on arrays and associative arrays of any complexity.

This conversion works because to`!short` applies to an `int`, to!wstring applies to a string, to`!string` applies to a `double`, and to!(double[]) applies to an int[]. The conversion might throw an exception because ``to!short might fail the range check.

int[string][double[int[]]] a;
auto b = to!(short[wstring][string[double[]]])(a);

Object-to-object conversions by dynamic casting throw exception when the source is non-null and the target is null.

import std.exception : assertThrown;
// Testing object conversions
class A {}
class B : A {}
class C : A {}
A a1 = new A, a2 = new B, a3 = new C;
assert(to!B(a2) is a2);
assert(to!C(a3) is a3);
assertThrown!ConvException(to!B(a3));

Stringize conversion from all types is supported.

  • String to string conversion works for any two string types having (char, wchar, dchar) character widths and any combination of qualifiers (mutable, const, or immutable).

  • Converts array (other than strings) to string. Each element is converted by calling ``to!T.

  • Associative array to string conversion. Each element is converted by calling ``to!T.

  • Object to string conversion calls toString against the object or returns "null" if the object is null.

  • Struct to string conversion calls toString against the struct if it is defined.

  • For structs that do not define toString, the conversion to string produces the list of fields.

  • Enumerated types are converted to strings as their symbolic names.

  • Boolean values are converted to "true" or "false".

  • char, wchar, dchar to a string type.

  • Unsigned or signed integers to strings.

    special case

    : Convert integral value to string in radix radix. radix must be a value from 2 to 36. value is treated as a signed value only if radix is 10. The characters A through Z are used to represent values 10 through 36 and their case is determined by the letterCase parameter.

  • All floating point types to all string types.

  • Pointer to string conversions convert the pointer to a size_t value. If pointer is char*, treat it as C-style strings. In that case, this function is @system.

See formatValue on how toString should be defined.

// Conversion representing dynamic/static array with string
long[] a = [ 1, 3, 5 ];
assert(to!string(a) == "[1, 3, 5]");

// Conversion representing associative array with string
int[string] associativeArray = ["0":1, "1":2];
assert(to!string(associativeArray) == `["0":1, "1":2]` ||
       to!string(associativeArray) == `["1":2, "0":1]`);

// char* to string conversion
assert(to!string(cast(char*) null) == "");
assert(to!string("foo\0".ptr) == "foo");

// Conversion reinterpreting void array to string
auto w = "abcx"w;
const(void)[] b = w;
assert(b.length == 8);

auto c = to!(wchar[])(b);
assert(c == "abcx");

Strings can be converted to enum types. The enum member with the same name as the input string is returned. The comparison is case-sensitive.

A ConvException is thrown if the enum does not have the specified member.

import std.exception : assertThrown;

enum E { a, b, c }
assert(to!E("a") == E.a);
assert(to!E("b") == E.b);
assertThrown!ConvException(to!E("A"));
to
!
(alias) object.string = string
string
;
case
(enum) property_tree_edit_commands.EditValue.Kind
Kind
.
(enum value) property_tree_edit_commands.EditValue.Kind.floating = 3
floating
: return
(field) double property_tree_edit_commands.EditValue.f
f
.
string std.conv.to!string.to!(const(double))(const(double) __param_0) pure @safe

The to template converts a value from one type to another. The source type is deduced and the target type must be specified, for example the expression to`!int(42.0)` converts the number 42 from `double` to `int`. The conversion is "safe", i.e., it checks for overflow; to!int(4.2e10) would throw the ConvOverflowException exception. Overflow checks are only inserted when necessary, e.g., ``to!double(42) does not do any checking because any int fits in a double.

Conversions from string to numeric types differ from the C equivalents atoi() and atol() by checking for overflow and not allowing whitespace.

For conversion of strings to signed types, the grammar recognized is: Integer: Sign UnsignedInteger UnsignedInteger Sign: + -

For conversion to unsigned types, the grammar recognized is: UnsignedInteger: DecimalDigit DecimalDigit UnsignedInteger

Examples

Converting a value to its own type (useful mostly for generic code) simply returns its argument.

int a = 42;
int b = to!int(a);
double c = to!double(3.14); // c is double with value 3.14

Converting among numeric types is a safe way to cast them around.

Conversions from floating-point types to integral types allow loss of precision (the fractional part of a floating-point number). The conversion is truncating towards zero, the same way a cast would truncate. (To round a floating point value when casting to an integral, use roundTo.)

import std.exception : assertThrown;

int a = 420;
assert(to!long(a) == a);
assertThrown!ConvOverflowException(to!byte(a));

assert(to!int(4.2e6) == 4200000);
assertThrown!ConvOverflowException(to!uint(-3.14));
assert(to!uint(3.14) == 3);
assert(to!uint(3.99) == 3);
assert(to!int(-3.99) == -3);

When converting strings to numeric types, note that D hexadecimal and binary literals are not handled. Neither the prefixes that indicate the base, nor the horizontal bar used to separate groups of digits are recognized. This also applies to the suffixes that indicate the type.

To work around this, you can specify a radix for conversions involving numbers.

auto str = to!string(42, 16);
assert(str == "2A");
auto i = to!int(str, 16);
assert(i == 42);

Conversions from integral types to floating-point types always succeed, but might lose accuracy. The largest integers with a predecessor representable in floating-point format are 2^24-1 for float, 2^53-1 for double, and 2^64-1 for real (when real is 80-bit, e.g. on Intel machines).

// 2^24 - 1, largest proper integer representable as float
int a = 16_777_215;
assert(to!int(to!float(a)) == a);
assert(to!int(to!float(-a)) == -a);

Conversion from string types to char types enforces the input to consist of a single code point, and said code point must fit in the target type. Otherwise, ConvException is thrown.

import std.exception : assertThrown;

assert(to!char("a") == 'a');
assertThrown(to!char("ñ")); // 'ñ' does not fit into a char
assert(to!wchar("ñ") == 'ñ');
assertThrown(to!wchar("😃")); // '😃' does not fit into a wchar
assert(to!dchar("😃") == '😃');

// Using wstring or dstring as source type does not affect the result
assert(to!char("a"w) == 'a');
assert(to!char("a"d) == 'a');

// Two code points cannot be converted to a single one
assertThrown(to!char("ab"));

Converting an array to another array type works by converting each element in turn. Associative arrays can be converted to associative arrays as long as keys and values can in turn be converted.

import std.string : split;

int[] a = [1, 2, 3];
auto b = to!(float[])(a);
assert(b == [1.0f, 2, 3]);
string str = "1 2 3 4 5 6";
auto numbers = to!(double[])(split(str));
assert(numbers == [1.0, 2, 3, 4, 5, 6]);
int[string] c;
c["a"] = 1;
c["b"] = 2;
auto d = to!(double[wstring])(c);
assert(d["a"w] == 1 && d["b"w] == 2);

Conversions operate transitively, meaning that they work on arrays and associative arrays of any complexity.

This conversion works because to`!short` applies to an `int`, to!wstring applies to a string, to`!string` applies to a `double`, and to!(double[]) applies to an int[]. The conversion might throw an exception because ``to!short might fail the range check.

int[string][double[int[]]] a;
auto b = to!(short[wstring][string[double[]]])(a);

Object-to-object conversions by dynamic casting throw exception when the source is non-null and the target is null.

import std.exception : assertThrown;
// Testing object conversions
class A {}
class B : A {}
class C : A {}
A a1 = new A, a2 = new B, a3 = new C;
assert(to!B(a2) is a2);
assert(to!C(a3) is a3);
assertThrown!ConvException(to!B(a3));

Stringize conversion from all types is supported.

  • String to string conversion works for any two string types having (char, wchar, dchar) character widths and any combination of qualifiers (mutable, const, or immutable).

  • Converts array (other than strings) to string. Each element is converted by calling ``to!T.

  • Associative array to string conversion. Each element is converted by calling ``to!T.

  • Object to string conversion calls toString against the object or returns "null" if the object is null.

  • Struct to string conversion calls toString against the struct if it is defined.

  • For structs that do not define toString, the conversion to string produces the list of fields.

  • Enumerated types are converted to strings as their symbolic names.

  • Boolean values are converted to "true" or "false".

  • char, wchar, dchar to a string type.

  • Unsigned or signed integers to strings.

    special case

    : Convert integral value to string in radix radix. radix must be a value from 2 to 36. value is treated as a signed value only if radix is 10. The characters A through Z are used to represent values 10 through 36 and their case is determined by the letterCase parameter.

  • All floating point types to all string types.

  • Pointer to string conversions convert the pointer to a size_t value. If pointer is char*, treat it as C-style strings. In that case, this function is @system.

See formatValue on how toString should be defined.

// Conversion representing dynamic/static array with string
long[] a = [ 1, 3, 5 ];
assert(to!string(a) == "[1, 3, 5]");

// Conversion representing associative array with string
int[string] associativeArray = ["0":1, "1":2];
assert(to!string(associativeArray) == `["0":1, "1":2]` ||
       to!string(associativeArray) == `["1":2, "0":1]`);

// char* to string conversion
assert(to!string(cast(char*) null) == "");
assert(to!string("foo\0".ptr) == "foo");

// Conversion reinterpreting void array to string
auto w = "abcx"w;
const(void)[] b = w;
assert(b.length == 8);

auto c = to!(wchar[])(b);
assert(c == "abcx");

Strings can be converted to enum types. The enum member with the same name as the input string is returned. The comparison is case-sensitive.

A ConvException is thrown if the enum does not have the specified member.

import std.exception : assertThrown;

enum E { a, b, c }
assert(to!E("a") == E.a);
assert(to!E("b") == E.b);
assertThrown!ConvException(to!E("A"));
to
!
(alias) object.string = string
string
;
case
(enum) property_tree_edit_commands.EditValue.Kind
Kind
.
(enum value) property_tree_edit_commands.EditValue.Kind.text = 4
text
: return `"` ~
(field) string property_tree_edit_commands.EditValue.s
s
~ `"`;
case
(enum) property_tree_edit_commands.EditValue.Kind
Kind
.
(enum value) property_tree_edit_commands.EditValue.Kind.variant = 5
variant
: return "=" ~
(field) string property_tree_edit_commands.EditValue.s
s
;
} } } /// The whole mutation API surface: one value. struct
(struct) property_tree_edit_commands.Edit

The whole mutation API surface: one value.

Edit
{
(alias) object.string = string
string
(field) string property_tree_edit_commands.Edit.path
path
;
(struct) property_tree_edit_commands.EditValue

Every leaf the component can produce an edit for, as one value type.

EditValue
(field) property_tree_edit_commands.EditValue property_tree_edit_commands.Edit.value
value
;
(enum) property_tree_edit_commands.Phase
Phase
(field) property_tree_edit_commands.Phase property_tree_edit_commands.Edit.phase
phase
=
(enum) property_tree_edit_commands.Phase
Phase
.
(enum value) property_tree_edit_commands.Phase.commit = 1
commit
;
} /// Why a write was refused — the view renders this, it is not an exception. enum
(enum) property_tree_edit_commands.Refusal

Why a write was refused — the view renders this, it is not an exception.

Refusal
: ubyte {
(enum value) property_tree_edit_commands.Refusal.none = cast(ubyte)0u
none
,
(enum value) property_tree_edit_commands.Refusal.noSuchPath = 1
noSuchPath
,
(enum value) property_tree_edit_commands.Refusal.readOnlyField = 2
readOnlyField
,
(enum value) property_tree_edit_commands.Refusal.readOnlyPolicy = 3
readOnlyPolicy
,
(enum value) property_tree_edit_commands.Refusal.typeMismatch = 4
typeMismatch
}
struct
(struct) property_tree_edit_commands.Applied
Applied
{
(enum) property_tree_edit_commands.Refusal

Why a write was refused — the view renders this, it is not an exception.

Refusal
(field) property_tree_edit_commands.Refusal property_tree_edit_commands.Applied.refusal
refusal
;
(struct) property_tree_edit_commands.Edit

The whole mutation API surface: one value.

Edit
(field) property_tree_edit_commands.Edit property_tree_edit_commands.Applied.inverse

valid when refusal == none && phase == commit

inverse
; /// valid when refusal == none && phase == commit
bool
bool property_tree_edit_commands.Applied.ok() const pure nothrow @nogc @safe
ok
() const pure nothrow @nogc =>
(field) property_tree_edit_commands.Refusal property_tree_edit_commands.Applied.refusal
refusal
==
(enum) property_tree_edit_commands.Refusal

Why a write was refused — the view renders this, it is not an exception.

Refusal
.
(enum value) property_tree_edit_commands.Refusal.none = cast(ubyte)0u
none
;
} // ── applying an edit ───────────────────────────────────────────────────────── struct
(struct) property_tree_edit_commands.Policy
Policy
{ bool
(field) bool property_tree_edit_commands.Policy.readOnly
readOnly
; }
/// Assigns `v` from an `EditValue` when the types line up; reports the old /// value as an `EditValue` so the caller can build the inverse. private bool
bool property_tree_edit_commands.assignLeaf!double(ref double v, in property_tree_edit_commands.EditValue e, out property_tree_edit_commands.EditValue old) @safe

Assigns v from an EditValue when the types line up; reports the old value as an EditValue so the caller can build the inverse.

assignLeaf
(V)(ref
(alias) V = double
V
(parameter) double v
v
, in
(struct) property_tree_edit_commands.EditValue

Every leaf the component can produce an edit for, as one value type.

EditValue
(parameter) const(property_tree_edit_commands.EditValue) e
e
, out
(struct) property_tree_edit_commands.EditValue

Every leaf the component can produce an edit for, as one value type.

EditValue
(parameter) property_tree_edit_commands.EditValue old
old
)
{ static if (is(
(alias) V = double
V
== bool))
{ if (
(parameter) const(property_tree_edit_commands.EditValue) e
e
.
(field) property_tree_edit_commands.EditValue.Kind property_tree_edit_commands.EditValue.kind
kind
!=
(struct) property_tree_edit_commands.EditValue

Every leaf the component can produce an edit for, as one value type.

EditValue
.
(enum) property_tree_edit_commands.EditValue.Kind
Kind
.
(enum value) property_tree_edit_commands.EditValue.Kind.boolean = 1
boolean
) return false;
(parameter) property_tree_edit_commands.EditValue old
old
=
(struct) property_tree_edit_commands.EditValue

Every leaf the component can produce an edit for, as one value type.

EditValue
.
property_tree_edit_commands.EditValue property_tree_edit_commands.EditValue.of(bool v) @safe
of
(
(parameter) bool v
v
);
(parameter) bool v
v
=
(parameter) const(property_tree_edit_commands.EditValue) e
e
.
(field) bool property_tree_edit_commands.EditValue.b
b
; return true;
} else static if (is(
(alias) V = double
V
== enum))
{ if (
(parameter) const(property_tree_edit_commands.EditValue) e
e
.
(field) property_tree_edit_commands.EditValue.Kind property_tree_edit_commands.EditValue.kind
kind
!=
(struct) property_tree_edit_commands.EditValue

Every leaf the component can produce an edit for, as one value type.

EditValue
.
(enum) property_tree_edit_commands.EditValue.Kind
Kind
.
(enum value) property_tree_edit_commands.EditValue.Kind.text = 4
text
) return false;
(parameter) property_tree_edit_commands.EditValue old
old
=
(struct) property_tree_edit_commands.EditValue

Every leaf the component can produce an edit for, as one value type.

EditValue
.
property_tree_edit_commands.EditValue property_tree_edit_commands.EditValue.of(string v) @safe
of
(
(parameter) property_tree_edit_commands.Cap v
v
.
string std.conv.to!string.to!(property_tree_edit_commands.Cap)(property_tree_edit_commands.Cap __param_0) pure @safe

The to template converts a value from one type to another. The source type is deduced and the target type must be specified, for example the expression to`!int(42.0)` converts the number 42 from `double` to `int`. The conversion is "safe", i.e., it checks for overflow; to!int(4.2e10) would throw the ConvOverflowException exception. Overflow checks are only inserted when necessary, e.g., ``to!double(42) does not do any checking because any int fits in a double.

Conversions from string to numeric types differ from the C equivalents atoi() and atol() by checking for overflow and not allowing whitespace.

For conversion of strings to signed types, the grammar recognized is: Integer: Sign UnsignedInteger UnsignedInteger Sign: + -

For conversion to unsigned types, the grammar recognized is: UnsignedInteger: DecimalDigit DecimalDigit UnsignedInteger

Examples

Converting a value to its own type (useful mostly for generic code) simply returns its argument.

int a = 42;
int b = to!int(a);
double c = to!double(3.14); // c is double with value 3.14

Converting among numeric types is a safe way to cast them around.

Conversions from floating-point types to integral types allow loss of precision (the fractional part of a floating-point number). The conversion is truncating towards zero, the same way a cast would truncate. (To round a floating point value when casting to an integral, use roundTo.)

import std.exception : assertThrown;

int a = 420;
assert(to!long(a) == a);
assertThrown!ConvOverflowException(to!byte(a));

assert(to!int(4.2e6) == 4200000);
assertThrown!ConvOverflowException(to!uint(-3.14));
assert(to!uint(3.14) == 3);
assert(to!uint(3.99) == 3);
assert(to!int(-3.99) == -3);

When converting strings to numeric types, note that D hexadecimal and binary literals are not handled. Neither the prefixes that indicate the base, nor the horizontal bar used to separate groups of digits are recognized. This also applies to the suffixes that indicate the type.

To work around this, you can specify a radix for conversions involving numbers.

auto str = to!string(42, 16);
assert(str == "2A");
auto i = to!int(str, 16);
assert(i == 42);

Conversions from integral types to floating-point types always succeed, but might lose accuracy. The largest integers with a predecessor representable in floating-point format are 2^24-1 for float, 2^53-1 for double, and 2^64-1 for real (when real is 80-bit, e.g. on Intel machines).

// 2^24 - 1, largest proper integer representable as float
int a = 16_777_215;
assert(to!int(to!float(a)) == a);
assert(to!int(to!float(-a)) == -a);

Conversion from string types to char types enforces the input to consist of a single code point, and said code point must fit in the target type. Otherwise, ConvException is thrown.

import std.exception : assertThrown;

assert(to!char("a") == 'a');
assertThrown(to!char("ñ")); // 'ñ' does not fit into a char
assert(to!wchar("ñ") == 'ñ');
assertThrown(to!wchar("😃")); // '😃' does not fit into a wchar
assert(to!dchar("😃") == '😃');

// Using wstring or dstring as source type does not affect the result
assert(to!char("a"w) == 'a');
assert(to!char("a"d) == 'a');

// Two code points cannot be converted to a single one
assertThrown(to!char("ab"));

Converting an array to another array type works by converting each element in turn. Associative arrays can be converted to associative arrays as long as keys and values can in turn be converted.

import std.string : split;

int[] a = [1, 2, 3];
auto b = to!(float[])(a);
assert(b == [1.0f, 2, 3]);
string str = "1 2 3 4 5 6";
auto numbers = to!(double[])(split(str));
assert(numbers == [1.0, 2, 3, 4, 5, 6]);
int[string] c;
c["a"] = 1;
c["b"] = 2;
auto d = to!(double[wstring])(c);
assert(d["a"w] == 1 && d["b"w] == 2);

Conversions operate transitively, meaning that they work on arrays and associative arrays of any complexity.

This conversion works because to`!short` applies to an `int`, to!wstring applies to a string, to`!string` applies to a `double`, and to!(double[]) applies to an int[]. The conversion might throw an exception because ``to!short might fail the range check.

int[string][double[int[]]] a;
auto b = to!(short[wstring][string[double[]]])(a);

Object-to-object conversions by dynamic casting throw exception when the source is non-null and the target is null.

import std.exception : assertThrown;
// Testing object conversions
class A {}
class B : A {}
class C : A {}
A a1 = new A, a2 = new B, a3 = new C;
assert(to!B(a2) is a2);
assert(to!C(a3) is a3);
assertThrown!ConvException(to!B(a3));

Stringize conversion from all types is supported.

  • String to string conversion works for any two string types having (char, wchar, dchar) character widths and any combination of qualifiers (mutable, const, or immutable).

  • Converts array (other than strings) to string. Each element is converted by calling ``to!T.

  • Associative array to string conversion. Each element is converted by calling ``to!T.

  • Object to string conversion calls toString against the object or returns "null" if the object is null.

  • Struct to string conversion calls toString against the struct if it is defined.

  • For structs that do not define toString, the conversion to string produces the list of fields.

  • Enumerated types are converted to strings as their symbolic names.

  • Boolean values are converted to "true" or "false".

  • char, wchar, dchar to a string type.

  • Unsigned or signed integers to strings.

    special case

    : Convert integral value to string in radix radix. radix must be a value from 2 to 36. value is treated as a signed value only if radix is 10. The characters A through Z are used to represent values 10 through 36 and their case is determined by the letterCase parameter.

  • All floating point types to all string types.

  • Pointer to string conversions convert the pointer to a size_t value. If pointer is char*, treat it as C-style strings. In that case, this function is @system.

See formatValue on how toString should be defined.

// Conversion representing dynamic/static array with string
long[] a = [ 1, 3, 5 ];
assert(to!string(a) == "[1, 3, 5]");

// Conversion representing associative array with string
int[string] associativeArray = ["0":1, "1":2];
assert(to!string(associativeArray) == `["0":1, "1":2]` ||
       to!string(associativeArray) == `["1":2, "0":1]`);

// char* to string conversion
assert(to!string(cast(char*) null) == "");
assert(to!string("foo\0".ptr) == "foo");

// Conversion reinterpreting void array to string
auto w = "abcx"w;
const(void)[] b = w;
assert(b.length == 8);

auto c = to!(wchar[])(b);
assert(c == "abcx");

Strings can be converted to enum types. The enum member with the same name as the input string is returned. The comparison is case-sensitive.

A ConvException is thrown if the enum does not have the specified member.

import std.exception : assertThrown;

enum E { a, b, c }
assert(to!E("a") == E.a);
assert(to!E("b") == E.b);
assertThrown!ConvException(to!E("A"));
to
!
(alias) object.string = string
string
);
switch (
(parameter) const(property_tree_edit_commands.EditValue) e
e
.
(field) string property_tree_edit_commands.EditValue.s
s
)
{ static foreach (m; __traits(allMembers, V)) { case
(constant) string property_tree_edit_commands.assignLeaf!(property_tree_edit_commands.Cap).m = "butt"
m
:
(parameter) property_tree_edit_commands.Cap v
v
= __traits(getMember, V, m); return true;
} default: return false; } } else static if (__traits(isIntegral,
(unresolved type) V
V
))
{ if (
(parameter) const(property_tree_edit_commands.EditValue) e
e
.
(field) property_tree_edit_commands.EditValue.Kind property_tree_edit_commands.EditValue.kind
kind
!=
(struct) property_tree_edit_commands.EditValue

Every leaf the component can produce an edit for, as one value type.

EditValue
.
(enum) property_tree_edit_commands.EditValue.Kind
Kind
.
(enum value) property_tree_edit_commands.EditValue.Kind.integral = 2
integral
) return false;
(parameter) property_tree_edit_commands.EditValue old
old
=
(struct) property_tree_edit_commands.EditValue

Every leaf the component can produce an edit for, as one value type.

EditValue
.
property_tree_edit_commands.EditValue property_tree_edit_commands.EditValue.of(long v) @safe
of
(cast(long)
(parameter) int v
v
);
(parameter) int v
v
= cast(
(alias) V = int
V
)
(parameter) const(property_tree_edit_commands.EditValue) e
e
.
(field) long property_tree_edit_commands.EditValue.i
i
; return true;
} else static if (__traits(isFloating,
(unresolved type) V
V
))
{ if (
(parameter) const(property_tree_edit_commands.EditValue) e
e
.
(field) property_tree_edit_commands.EditValue.Kind property_tree_edit_commands.EditValue.kind
kind
!=
(struct) property_tree_edit_commands.EditValue

Every leaf the component can produce an edit for, as one value type.

EditValue
.
(enum) property_tree_edit_commands.EditValue.Kind
Kind
.
(enum value) property_tree_edit_commands.EditValue.Kind.floating = 3
floating
) return false;
(parameter) property_tree_edit_commands.EditValue old
old
=
(struct) property_tree_edit_commands.EditValue

Every leaf the component can produce an edit for, as one value type.

EditValue
.
property_tree_edit_commands.EditValue property_tree_edit_commands.EditValue.of(double v) @safe
of
(cast(double)
(parameter) double v
v
);
(parameter) double v
v
= cast(
(alias) V = double
V
)
(parameter) const(property_tree_edit_commands.EditValue) e
e
.
(field) double property_tree_edit_commands.EditValue.f
f
; return true;
} else static if (
(template instance) std.traits.isSomeString!string
isSomeString
!
(alias) V = string
V
)
{ if (
(parameter) const(property_tree_edit_commands.EditValue) e
e
.
(field) property_tree_edit_commands.EditValue.Kind property_tree_edit_commands.EditValue.kind
kind
!=
(struct) property_tree_edit_commands.EditValue

Every leaf the component can produce an edit for, as one value type.

EditValue
.
(enum) property_tree_edit_commands.EditValue.Kind
Kind
.
(enum value) property_tree_edit_commands.EditValue.Kind.text = 4
text
) return false;
// dip1000 earns its keep: an `in Edit` is scope, so its text cannot // be stored into the subject without a copy. The compiler says so.
(parameter) property_tree_edit_commands.EditValue old
old
=
(struct) property_tree_edit_commands.EditValue

Every leaf the component can produce an edit for, as one value type.

EditValue
.
property_tree_edit_commands.EditValue property_tree_edit_commands.EditValue.of(string v) @safe
of
(
(parameter) string v
v
.
string object.idup!(immutable(char))(string a) pure nothrow @property @safe

Provide the .idup array property, which creates an immutable duplicate.

idup
);
(parameter) string v
v
=
(parameter) const(property_tree_edit_commands.EditValue) e
e
.
(field) string property_tree_edit_commands.EditValue.s
s
.
string object.idup!(immutable(char))(string a) pure nothrow @property @safe

Provide the .idup array property, which creates an immutable duplicate.

idup
; return true;
} else return false; } /// The generated dispatch: one walk, parameterised by TYPE only ([`open-set-descent.d`](./open-set-descent.d)). /// `@readOnly` is consulted HERE (C13) — a view cannot route around it. private
(enum) property_tree_edit_commands.Refusal

Why a write was refused — the view renders this, it is not an exception.

Refusal
property_tree_edit_commands.Refusal property_tree_edit_commands.applyAt!(property_tree_edit_commands.Cap)(ref property_tree_edit_commands.Cap subject, in property_tree_edit_commands.Seg[] segs, ulong at_, in property_tree_edit_commands.Edit e, in property_tree_edit_commands.Policy pol, out property_tree_edit_commands.EditValue old) pure nothrow @nogc @safe

The generated dispatch: one walk, parameterised by TYPE only (open-set-descent.d). @readOnly is consulted HERE (C13) — a view cannot route around it.

applyAt
(T)(ref
(alias) T = property_tree_edit_commands.Cap
T
(parameter) property_tree_edit_commands.Cap subject
subject
, in
(struct) property_tree_edit_commands.Seg
Seg
[]
(parameter) const(property_tree_edit_commands.Seg[]) segs
segs
,
(alias) object.size_t = ulong
size_t
(parameter) ulong at_
at_
,
in
(struct) property_tree_edit_commands.Edit

The whole mutation API surface: one value.

Edit
(parameter) const(property_tree_edit_commands.Edit) e
e
, in
(struct) property_tree_edit_commands.Policy
Policy
(parameter) const(property_tree_edit_commands.Policy) pol
pol
, out
(struct) property_tree_edit_commands.EditValue

Every leaf the component can produce an edit for, as one value type.

EditValue
(parameter) property_tree_edit_commands.EditValue old
old
)
{ static if (is(
(alias) T = property_tree_edit_commands.Cap
T
== U*, U))
{ if (subject is null) return Refusal.noSuchPath; return applyAt(*subject, segs, at_, e, pol, old); } else static if (
(template instance) std.traits.isAggregateType!(property_tree_edit_commands.Cap)
isAggregateType
!
(alias) T = property_tree_edit_commands.Cap
T
&& !
(template instance) isSomeString!T
isSomeString
!
(alias) T = property_tree_edit_commands.Stroke
T
)
{ if (
(parameter) ulong at_
at_
>=
(parameter) const(property_tree_edit_commands.Seg[]) segs
segs
.
(field) ulong const(property_tree_edit_commands.Seg[]).length
length
||
(parameter) const(property_tree_edit_commands.Seg[]) segs
segs
[
(parameter) ulong at_
at_
].
(field) bool property_tree_edit_commands.Seg.isIndex
isIndex
) return
(enum) property_tree_edit_commands.Refusal

Why a write was refused — the view renders this, it is not an exception.

Refusal
.
(enum value) property_tree_edit_commands.Refusal.noSuchPath = 1
noSuchPath
;
switch (
(parameter) const(property_tree_edit_commands.Seg[]) segs
segs
[
(parameter) ulong at_
at_
].
(field) string property_tree_edit_commands.Seg.name
name
)
{ static foreach (name; __traits(allMembers, T)) {{ alias
(alias field) property_tree_edit_commands.applyAt!(property_tree_edit_commands.Stroke).F = double property_tree_edit_commands.Stroke.width
F
= __traits(getMember, T, name);
static if (__traits(compiles, typeof(
(field) double property_tree_edit_commands.Stroke.width
F
))
&& !is(typeof(
(field) double property_tree_edit_commands.Stroke.width
F
) == function))
{ case
(constant) string property_tree_edit_commands.applyAt!(property_tree_edit_commands.Stroke).name = "width"
name
:
static if (
(template instance) property_tree_edit_commands.Stroke.hasUDA!(width, property_tree_edit_commands.readOnly)
hasUDA
!(
(field) double property_tree_edit_commands.Stroke.width
F
,
(enum) property_tree_edit_commands.readOnly
readOnly
))
return
(enum) property_tree_edit_commands.Refusal

Why a write was refused — the view renders this, it is not an exception.

Refusal
.
(enum value) property_tree_edit_commands.Refusal.readOnlyField = 2
readOnlyField
;
else { if (
(parameter) ulong at_
at_
+ 1 ==
(parameter) const(property_tree_edit_commands.Seg[]) segs
segs
.
(field) ulong const(property_tree_edit_commands.Seg[]).length
length
)
return
bool property_tree_edit_commands.assignLeaf!double(ref double v, in property_tree_edit_commands.EditValue e, out property_tree_edit_commands.EditValue old) @safe

Assigns v from an EditValue when the types line up; reports the old value as an EditValue so the caller can build the inverse.

assignLeaf
(__traits(getMember, subject, name),
(parameter) const(property_tree_edit_commands.Edit) e
e
.
(field) property_tree_edit_commands.EditValue property_tree_edit_commands.Edit.value
value
,
(parameter) property_tree_edit_commands.EditValue old
old
)
?
(enum) property_tree_edit_commands.Refusal

Why a write was refused — the view renders this, it is not an exception.

Refusal
.
(enum value) property_tree_edit_commands.Refusal.none = cast(ubyte)0u
none
:
(enum) property_tree_edit_commands.Refusal

Why a write was refused — the view renders this, it is not an exception.

Refusal
.
(enum value) property_tree_edit_commands.Refusal.typeMismatch = 4
typeMismatch
;
return
property_tree_edit_commands.Refusal property_tree_edit_commands.applyAt!double(ref double subject, in property_tree_edit_commands.Seg[] segs, ulong at_, in property_tree_edit_commands.Edit e, in property_tree_edit_commands.Policy pol, out property_tree_edit_commands.EditValue old) pure nothrow @nogc @safe

The generated dispatch: one walk, parameterised by TYPE only (open-set-descent.d). @readOnly is consulted HERE (C13) — a view cannot route around it.

applyAt
(__traits(getMember, subject, name),
(parameter) const(property_tree_edit_commands.Seg[]) segs
segs
,
(parameter) ulong at_
at_
+ 1,
(parameter) const(property_tree_edit_commands.Edit) e
e
,
(parameter) const(property_tree_edit_commands.Policy) pol
pol
,
(parameter) property_tree_edit_commands.EditValue old
old
);
} } }} default: return
(enum) property_tree_edit_commands.Refusal

Why a write was refused — the view renders this, it is not an exception.

Refusal
.
(enum value) property_tree_edit_commands.Refusal.noSuchPath = 1
noSuchPath
;
} } else static if (
(template instance) std.traits.isArray!(property_tree_edit_commands.Cap)
isArray
!
(alias) T = property_tree_edit_commands.Cap
T
&& !
(template instance) isSomeString!T
isSomeString
!
(alias) T = string
T
)
{ if (at_ >= segs.length || !segs[at_].isIndex || segs[at_].index >= subject.length) return Refusal.noSuchPath; if (at_ + 1 == segs.length) return assignLeaf(subject[segs[at_].index], e.value, old) ? Refusal.none : Refusal.typeMismatch; return applyAt(subject[segs[at_].index], segs, at_ + 1, e, pol, old); } else return
(enum) property_tree_edit_commands.Refusal

Why a write was refused — the view renders this, it is not an exception.

Refusal
.
(enum value) property_tree_edit_commands.Refusal.noSuchPath = 1
noSuchPath
;
} /// The public verb. Read-only policy is refused before the walk (C13).
(struct) property_tree_edit_commands.Applied
Applied
property_tree_edit_commands.Applied property_tree_edit_commands.apply!(property_tree_edit_commands.Layer)(ref property_tree_edit_commands.Layer subject, in property_tree_edit_commands.Edit e, in property_tree_edit_commands.Policy pol = Policy(false)) @safe

The public verb. Read-only policy is refused before the walk (C13).

apply
(T)(ref
(alias) T = property_tree_edit_commands.Layer
T
(parameter) property_tree_edit_commands.Layer subject
subject
, in
(struct) property_tree_edit_commands.Edit

The whole mutation API surface: one value.

Edit
(parameter) const(property_tree_edit_commands.Edit) e
e
, in
(struct) property_tree_edit_commands.Policy
Policy
(parameter) const(property_tree_edit_commands.Policy) pol
pol
=
(struct) property_tree_edit_commands.Policy
Policy
.
(constant) property_tree_edit_commands.Policy property_tree_edit_commands.Policy.init = Policy(false)
init
)
{ if (
(parameter) const(property_tree_edit_commands.Policy) pol
pol
.
(field) bool property_tree_edit_commands.Policy.readOnly
readOnly
) return
(struct) property_tree_edit_commands.Applied
Applied
(
(enum) property_tree_edit_commands.Refusal

Why a write was refused — the view renders this, it is not an exception.

Refusal
.
(enum value) property_tree_edit_commands.Refusal.readOnlyPolicy = 3
readOnlyPolicy
);
(struct) property_tree_edit_commands.EditValue

Every leaf the component can produce an edit for, as one value type.

EditValue
(local variable) property_tree_edit_commands.EditValue old
old
;
const
(local variable) const(property_tree_edit_commands.Refusal) r
r
=
property_tree_edit_commands.Refusal property_tree_edit_commands.applyAt!(property_tree_edit_commands.Layer)(ref property_tree_edit_commands.Layer subject, in property_tree_edit_commands.Seg[] segs, ulong at_, in property_tree_edit_commands.Edit e, in property_tree_edit_commands.Policy pol, out property_tree_edit_commands.EditValue old) @safe

The generated dispatch: one walk, parameterised by TYPE only (open-set-descent.d). @readOnly is consulted HERE (C13) — a view cannot route around it.

applyAt
(
(parameter) property_tree_edit_commands.Layer subject
subject
,
property_tree_edit_commands.Seg[] property_tree_edit_commands.segments(scope const(char)[] path) pure @safe
segments
(
(parameter) const(property_tree_edit_commands.Edit) e
e
.
(field) string property_tree_edit_commands.Edit.path
path
), 0,
(parameter) const(property_tree_edit_commands.Edit) e
e
,
(parameter) const(property_tree_edit_commands.Policy) pol
pol
,
(local variable) property_tree_edit_commands.EditValue old
old
);
if (
(local variable) const(property_tree_edit_commands.Refusal) r
r
!=
(enum) property_tree_edit_commands.Refusal

Why a write was refused — the view renders this, it is not an exception.

Refusal
.
(enum value) property_tree_edit_commands.Refusal.none = cast(ubyte)0u
none
) return
(struct) property_tree_edit_commands.Applied
Applied
(
(local variable) const(property_tree_edit_commands.Refusal) r
r
);
// C12: the inverse is the same value type, built from what was there. return
(struct) property_tree_edit_commands.Applied
Applied
(
(enum) property_tree_edit_commands.Refusal

Why a write was refused — the view renders this, it is not an exception.

Refusal
.
(enum value) property_tree_edit_commands.Refusal.none = cast(ubyte)0u
none
,
(struct) property_tree_edit_commands.Edit

The whole mutation API surface: one value.

Edit
(
(parameter) const(property_tree_edit_commands.Edit) e
e
.
(field) string property_tree_edit_commands.Edit.path
path
,
(local variable) property_tree_edit_commands.EditValue old
old
,
(enum) property_tree_edit_commands.Phase
Phase
.
(enum value) property_tree_edit_commands.Phase.commit = 1
commit
));
} // ── path segments ([`path-addressing.d`](./path-addressing.d), verbatim) ──────────────────────────────────────── struct
(struct) property_tree_edit_commands.Seg
Seg
{
(alias) object.string = string
string
(field) string property_tree_edit_commands.Seg.name
name
;
(alias) object.size_t = ulong
size_t
(field) ulong property_tree_edit_commands.Seg.index
index
; bool
(field) bool property_tree_edit_commands.Seg.isIndex
isIndex
; }
(struct) property_tree_edit_commands.Seg
Seg
[]
property_tree_edit_commands.Seg[] property_tree_edit_commands.segments(scope const(char)[] path) pure @safe
segments
(scope const(char)[]
(parameter) const(char)[] path
path
) pure
{
(struct) property_tree_edit_commands.Seg
Seg
[]
(local variable) property_tree_edit_commands.Seg[] segs
segs
;
(alias) object.size_t = ulong
size_t
(local variable) ulong i
i
;
while (
(local variable) ulong i
i
<
(parameter) const(char)[] path
path
.
(field) ulong const(char)[].length
length
)
{ if (
(parameter) const(char)[] path
path
[
(local variable) ulong i
i
] == '.') {
(local variable) ulong i
i
++; continue; }
if (
(parameter) const(char)[] path
path
[
(local variable) ulong i
i
] == '[')
{
(alias) object.size_t = ulong
size_t
(local variable) ulong j
j
= ++
(local variable) ulong i
i
;
while (
(local variable) ulong j
j
<
(parameter) const(char)[] path
path
.
(field) ulong const(char)[].length
length
&&
(parameter) const(char)[] path
path
[
(local variable) ulong j
j
] != ']')
(local variable) ulong j
j
++;
(local variable) property_tree_edit_commands.Seg[] segs
segs
~=
(struct) property_tree_edit_commands.Seg
Seg
(null,
ulong std.conv.to!ulong.to!(const(char)[])(const(char)[] __param_0) pure @safe

The to template converts a value from one type to another. The source type is deduced and the target type must be specified, for example the expression to`!int(42.0)` converts the number 42 from `double` to `int`. The conversion is "safe", i.e., it checks for overflow; to!int(4.2e10) would throw the ConvOverflowException exception. Overflow checks are only inserted when necessary, e.g., ``to!double(42) does not do any checking because any int fits in a double.

Conversions from string to numeric types differ from the C equivalents atoi() and atol() by checking for overflow and not allowing whitespace.

For conversion of strings to signed types, the grammar recognized is: Integer: Sign UnsignedInteger UnsignedInteger Sign: + -

For conversion to unsigned types, the grammar recognized is: UnsignedInteger: DecimalDigit DecimalDigit UnsignedInteger

Examples

Converting a value to its own type (useful mostly for generic code) simply returns its argument.

int a = 42;
int b = to!int(a);
double c = to!double(3.14); // c is double with value 3.14

Converting among numeric types is a safe way to cast them around.

Conversions from floating-point types to integral types allow loss of precision (the fractional part of a floating-point number). The conversion is truncating towards zero, the same way a cast would truncate. (To round a floating point value when casting to an integral, use roundTo.)

import std.exception : assertThrown;

int a = 420;
assert(to!long(a) == a);
assertThrown!ConvOverflowException(to!byte(a));

assert(to!int(4.2e6) == 4200000);
assertThrown!ConvOverflowException(to!uint(-3.14));
assert(to!uint(3.14) == 3);
assert(to!uint(3.99) == 3);
assert(to!int(-3.99) == -3);

When converting strings to numeric types, note that D hexadecimal and binary literals are not handled. Neither the prefixes that indicate the base, nor the horizontal bar used to separate groups of digits are recognized. This also applies to the suffixes that indicate the type.

To work around this, you can specify a radix for conversions involving numbers.

auto str = to!string(42, 16);
assert(str == "2A");
auto i = to!int(str, 16);
assert(i == 42);

Conversions from integral types to floating-point types always succeed, but might lose accuracy. The largest integers with a predecessor representable in floating-point format are 2^24-1 for float, 2^53-1 for double, and 2^64-1 for real (when real is 80-bit, e.g. on Intel machines).

// 2^24 - 1, largest proper integer representable as float
int a = 16_777_215;
assert(to!int(to!float(a)) == a);
assert(to!int(to!float(-a)) == -a);

Conversion from string types to char types enforces the input to consist of a single code point, and said code point must fit in the target type. Otherwise, ConvException is thrown.

import std.exception : assertThrown;

assert(to!char("a") == 'a');
assertThrown(to!char("ñ")); // 'ñ' does not fit into a char
assert(to!wchar("ñ") == 'ñ');
assertThrown(to!wchar("😃")); // '😃' does not fit into a wchar
assert(to!dchar("😃") == '😃');

// Using wstring or dstring as source type does not affect the result
assert(to!char("a"w) == 'a');
assert(to!char("a"d) == 'a');

// Two code points cannot be converted to a single one
assertThrown(to!char("ab"));

Converting an array to another array type works by converting each element in turn. Associative arrays can be converted to associative arrays as long as keys and values can in turn be converted.

import std.string : split;

int[] a = [1, 2, 3];
auto b = to!(float[])(a);
assert(b == [1.0f, 2, 3]);
string str = "1 2 3 4 5 6";
auto numbers = to!(double[])(split(str));
assert(numbers == [1.0, 2, 3, 4, 5, 6]);
int[string] c;
c["a"] = 1;
c["b"] = 2;
auto d = to!(double[wstring])(c);
assert(d["a"w] == 1 && d["b"w] == 2);

Conversions operate transitively, meaning that they work on arrays and associative arrays of any complexity.

This conversion works because to`!short` applies to an `int`, to!wstring applies to a string, to`!string` applies to a `double`, and to!(double[]) applies to an int[]. The conversion might throw an exception because ``to!short might fail the range check.

int[string][double[int[]]] a;
auto b = to!(short[wstring][string[double[]]])(a);

Object-to-object conversions by dynamic casting throw exception when the source is non-null and the target is null.

import std.exception : assertThrown;
// Testing object conversions
class A {}
class B : A {}
class C : A {}
A a1 = new A, a2 = new B, a3 = new C;
assert(to!B(a2) is a2);
assert(to!C(a3) is a3);
assertThrown!ConvException(to!B(a3));

Stringize conversion from all types is supported.

  • String to string conversion works for any two string types having (char, wchar, dchar) character widths and any combination of qualifiers (mutable, const, or immutable).

  • Converts array (other than strings) to string. Each element is converted by calling ``to!T.

  • Associative array to string conversion. Each element is converted by calling ``to!T.

  • Object to string conversion calls toString against the object or returns "null" if the object is null.

  • Struct to string conversion calls toString against the struct if it is defined.

  • For structs that do not define toString, the conversion to string produces the list of fields.

  • Enumerated types are converted to strings as their symbolic names.

  • Boolean values are converted to "true" or "false".

  • char, wchar, dchar to a string type.

  • Unsigned or signed integers to strings.

    special case

    : Convert integral value to string in radix radix. radix must be a value from 2 to 36. value is treated as a signed value only if radix is 10. The characters A through Z are used to represent values 10 through 36 and their case is determined by the letterCase parameter.

  • All floating point types to all string types.

  • Pointer to string conversions convert the pointer to a size_t value. If pointer is char*, treat it as C-style strings. In that case, this function is @system.

See formatValue on how toString should be defined.

// Conversion representing dynamic/static array with string
long[] a = [ 1, 3, 5 ];
assert(to!string(a) == "[1, 3, 5]");

// Conversion representing associative array with string
int[string] associativeArray = ["0":1, "1":2];
assert(to!string(associativeArray) == `["0":1, "1":2]` ||
       to!string(associativeArray) == `["1":2, "0":1]`);

// char* to string conversion
assert(to!string(cast(char*) null) == "");
assert(to!string("foo\0".ptr) == "foo");

// Conversion reinterpreting void array to string
auto w = "abcx"w;
const(void)[] b = w;
assert(b.length == 8);

auto c = to!(wchar[])(b);
assert(c == "abcx");

Strings can be converted to enum types. The enum member with the same name as the input string is returned. The comparison is case-sensitive.

A ConvException is thrown if the enum does not have the specified member.

import std.exception : assertThrown;

enum E { a, b, c }
assert(to!E("a") == E.a);
assert(to!E("b") == E.b);
assertThrown!ConvException(to!E("A"));
to
!
(alias) object.size_t = ulong
size_t
(
(parameter) const(char)[] path
path
[
(local variable) ulong i
i
..
(local variable) ulong j
j
]), true);
(local variable) ulong i
i
=
(local variable) ulong j
j
+ 1;
} else {
(alias) object.size_t = ulong
size_t
(local variable) ulong j
j
=
(local variable) ulong i
i
;
while (
(local variable) ulong j
j
<
(parameter) const(char)[] path
path
.
(field) ulong const(char)[].length
length
&&
(parameter) const(char)[] path
path
[
(local variable) ulong j
j
] != '.' &&
(parameter) const(char)[] path
path
[
(local variable) ulong j
j
] != '[')
(local variable) ulong j
j
++;
(local variable) property_tree_edit_commands.Seg[] segs
segs
~=
(struct) property_tree_edit_commands.Seg
Seg
(
(parameter) const(char)[] path
path
[
(local variable) ulong i
i
..
(local variable) ulong j
j
].
string object.idup!(const(char))(const(char)[] a) pure nothrow @property @safe

Provide the .idup array property, which creates an immutable duplicate.

idup
, 0, false);
(local variable) ulong i
i
=
(local variable) ulong j
j
;
} } return
(local variable) property_tree_edit_commands.Seg[] segs
segs
;
} // ── the subject ────────────────────────────────────────────────────────────── enum
(enum) property_tree_edit_commands.Cap
Cap
{
(enum value) property_tree_edit_commands.Cap.butt = 0
butt
,
(enum value) property_tree_edit_commands.Cap.round = 1
round
,
(enum value) property_tree_edit_commands.Cap.square = 2
square
}
struct
(struct) property_tree_edit_commands.Stroke
Stroke
{ double
(field) double property_tree_edit_commands.Stroke.width
width
= 1;
(enum) property_tree_edit_commands.Cap
Cap
(field) property_tree_edit_commands.Cap property_tree_edit_commands.Stroke.cap
cap
; }
struct
(struct) property_tree_edit_commands.Layer
Layer
{
(alias) object.string = string
string
(field) string property_tree_edit_commands.Layer.name
name
= "layer";
bool
(field) bool property_tree_edit_commands.Layer.visible
visible
= true;
int
(field) int property_tree_edit_commands.Layer.order
order
;
(struct) property_tree_edit_commands.Stroke
Stroke
(field) property_tree_edit_commands.Stroke property_tree_edit_commands.Layer.stroke
stroke
;
@
(enum) property_tree_edit_commands.readOnly
readOnly
ulong
(field) ulong property_tree_edit_commands.Layer.id
id
= 42;
} // C15: the sum type. struct
(struct) property_tree_edit_commands.Solid
Solid
{ uint
(field) uint property_tree_edit_commands.Solid.rgba
rgba
; }
struct
(struct) property_tree_edit_commands.Gradient
Gradient
{
(alias) object.string = string
string
(field) string property_tree_edit_commands.Gradient.from
from
,
(field) string property_tree_edit_commands.Gradient.to
to
; int
(field) int property_tree_edit_commands.Gradient.stops
stops
= 2; }
alias
(alias) property_tree_edit_commands.Paint = std.sumtype.SumType!(Solid, Gradient)
Paint
=
(struct) std.sumtype.SumType!(property_tree_edit_commands.Solid, property_tree_edit_commands.Gradient)

A tagged union that can hold a single value from any of a specified set of types.

The value in a SumType can be operated on using pattern matching.

To avoid ambiguity, duplicate types are not allowed (but see the "basic usage" example for a workaround).

The special type This can be used as a placeholder to create self-referential types, just like with Algebraic. See the "Recursive SumTypes" example for usage.

A SumType is initialized by default to hold the .init value of its first member type, just like a regular union. The version identifier SumTypeNoDefaultCtor can be used to disable this behavior.

@seeAlgebraic
SumType
!(
(struct) property_tree_edit_commands.Solid
Solid
,
(struct) property_tree_edit_commands.Gradient
Gradient
);
/// The one `@trusted` seam a variant switch needs, and its precondition. /// PRECONDITION: no reference into the old payload outlives this call — which /// the frame model guarantees, since rows are rebuilt after every edit. void
void property_tree_edit_commands.switchTo!(property_tree_edit_commands.Gradient)(ref std.sumtype.SumType!(Solid, Gradient) p, property_tree_edit_commands.Gradient v) pure nothrow @nogc @trusted

The one @trusted seam a variant switch needs, and its precondition.

PRECONDITION

no reference into the old payload outlives this call — which the frame model guarantees, since rows are rebuilt after every edit.

switchTo
(V)(ref
(alias) property_tree_edit_commands.Paint = std.sumtype.SumType!(Solid, Gradient)
Paint
(parameter) std.sumtype.SumType!(Solid, Gradient) p
p
,
(alias) V = property_tree_edit_commands.Gradient
V
(parameter) property_tree_edit_commands.Gradient v
v
) @trusted {
(parameter) std.sumtype.SumType!(Solid, Gradient) p
p
=
std.sumtype.SumType!(Solid, Gradient) std.sumtype.SumType!(property_tree_edit_commands.Solid, property_tree_edit_commands.Gradient).opAssign(property_tree_edit_commands.Gradient rhs) pure nothrow @nogc ref @safe

Assigns a value to a SumType.

If any of the SumType's members other than the one being assigned to contain pointers or references, it is possible for the assignment to cause memory corruption (see the "Memory corruption" example below for an illustration of how). Therefore, such assignments are considered @system.

An individual assignment can be @trusted if the caller can guarantee that there are no outstanding references to any SumType members that contain pointers or references at the time the assignment occurs.

Examples

Memory corruption

This example shows how assignment to a SumType can be used to cause memory corruption in @system code. In @safe code, the assignment s = 123 would not be allowed.

SumType!(int*, int) s = new int;
s.tryMatch!(
    (ref int* p) {
        s = 123; // overwrites `p`
        return *p; // undefined behavior
    }
);
v
; }
void
void D main() @safe
main
()
{ 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) writefln = std.stdio.writefln(alias fmt, A...)(A args) if (isSomeString!(typeof(fmt)))

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

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

Params: args = the items to write to stdout

Throws: In case of an I/O error, throws an $(LREF StdioException). 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
;
(struct) property_tree_edit_commands.Layer
Layer
(local variable) property_tree_edit_commands.Layer l
l
;
(struct) property_tree_edit_commands.Edit

The whole mutation API surface: one value.

Edit
[]
(local variable) property_tree_edit_commands.Edit[] undo
undo
;
void
void property_tree_edit_commands.main.run(property_tree_edit_commands.Edit e, property_tree_edit_commands.Policy pol = Policy(false)) @safe
run
(
(struct) property_tree_edit_commands.Edit

The whole mutation API surface: one value.

Edit
(parameter) property_tree_edit_commands.Edit e
e
,
(struct) property_tree_edit_commands.Policy
Policy
(parameter) property_tree_edit_commands.Policy pol
pol
=
(struct) property_tree_edit_commands.Policy
Policy
.
(constant) property_tree_edit_commands.Policy property_tree_edit_commands.Policy.init = Policy(false)
init
)
{ const
(local variable) const(property_tree_edit_commands.Applied) r
r
=
property_tree_edit_commands.Applied property_tree_edit_commands.apply!(property_tree_edit_commands.Layer)(ref property_tree_edit_commands.Layer subject, in property_tree_edit_commands.Edit e, in property_tree_edit_commands.Policy pol = Policy(false)) @safe

The public verb. Read-only policy is refused before the walk (C13).

apply
(
(local variable) property_tree_edit_commands.Layer l
l
,
(parameter) property_tree_edit_commands.Edit e
e
,
(parameter) property_tree_edit_commands.Policy pol
pol
);
void std.stdio.writefln!(char, string, string, property_tree_edit_commands.Phase, const(property_tree_edit_commands.Refusal), string)(in char[] fmt, string __param_1, string __param_2, property_tree_edit_commands.Phase __param_3, const(property_tree_edit_commands.Refusal) __param_4, string __param_5) @safe

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

writefln
(" %-22s %-10s %-8s → %-14s %s",
(parameter) property_tree_edit_commands.Edit e
e
.
(field) string property_tree_edit_commands.Edit.path
path
,
(parameter) property_tree_edit_commands.Edit e
e
.
(field) property_tree_edit_commands.EditValue property_tree_edit_commands.Edit.value
value
.
string property_tree_edit_commands.EditValue.toString() const pure @safe
toString
,
(parameter) property_tree_edit_commands.Edit e
e
.
(field) property_tree_edit_commands.Phase property_tree_edit_commands.Edit.phase
phase
,
(local variable) const(property_tree_edit_commands.Applied) r
r
.
(field) property_tree_edit_commands.Refusal property_tree_edit_commands.Applied.refusal
refusal
,
(local variable) const(property_tree_edit_commands.Applied) r
r
.
bool property_tree_edit_commands.Applied.ok() const pure nothrow @nogc @safe
ok
&&
(parameter) property_tree_edit_commands.Edit e
e
.
(field) property_tree_edit_commands.Phase property_tree_edit_commands.Edit.phase
phase
==
(enum) property_tree_edit_commands.Phase
Phase
.
(enum value) property_tree_edit_commands.Phase.commit = 1
commit
? "inverse " ~
(local variable) const(property_tree_edit_commands.Applied) r
r
.
(field) property_tree_edit_commands.Edit property_tree_edit_commands.Applied.inverse

valid when refusal == none && phase == commit

inverse
.
(field) property_tree_edit_commands.EditValue property_tree_edit_commands.Edit.value
value
.
string property_tree_edit_commands.EditValue.toString() const pure @safe
toString
: "");
if (
(local variable) const(property_tree_edit_commands.Applied) r
r
.
bool property_tree_edit_commands.Applied.ok() const pure nothrow @nogc @safe
ok
&&
(parameter) property_tree_edit_commands.Edit e
e
.
(field) property_tree_edit_commands.Phase property_tree_edit_commands.Edit.phase
phase
==
(enum) property_tree_edit_commands.Phase
Phase
.
(enum value) property_tree_edit_commands.Phase.commit = 1
commit
)
(local variable) property_tree_edit_commands.Edit[] undo
undo
~=
(local variable) const(property_tree_edit_commands.Applied) r
r
.
(field) property_tree_edit_commands.Edit property_tree_edit_commands.Applied.inverse

valid when refusal == none && phase == commit

inverse
;
}
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
("C12/C13 — edits as values; every write returns its inverse\n");
void std.stdio.writefln!(char, string, string, string, string, string)(in char[] fmt, string __param_1, string __param_2, string __param_3, string __param_4, string __param_5) @safe

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

writefln
(" %-22s %-10s %-8s %-14s %s",
"path", "value", "phase", "refusal", "undo");
void property_tree_edit_commands.main.run(property_tree_edit_commands.Edit e, property_tree_edit_commands.Policy pol = Policy(false)) @safe
run
(
(struct) property_tree_edit_commands.Edit

The whole mutation API surface: one value.

Edit
("name",
(struct) property_tree_edit_commands.EditValue

Every leaf the component can produce an edit for, as one value type.

EditValue
.
property_tree_edit_commands.EditValue property_tree_edit_commands.EditValue.of(string v) @safe
of
("background")));
void property_tree_edit_commands.main.run(property_tree_edit_commands.Edit e, property_tree_edit_commands.Policy pol = Policy(false)) @safe
run
(
(struct) property_tree_edit_commands.Edit

The whole mutation API surface: one value.

Edit
("visible",
(struct) property_tree_edit_commands.EditValue

Every leaf the component can produce an edit for, as one value type.

EditValue
.
property_tree_edit_commands.EditValue property_tree_edit_commands.EditValue.of(bool v) @safe
of
(false)));
void property_tree_edit_commands.main.run(property_tree_edit_commands.Edit e, property_tree_edit_commands.Policy pol = Policy(false)) @safe
run
(
(struct) property_tree_edit_commands.Edit

The whole mutation API surface: one value.

Edit
("stroke.width",
(struct) property_tree_edit_commands.EditValue

Every leaf the component can produce an edit for, as one value type.

EditValue
.
property_tree_edit_commands.EditValue property_tree_edit_commands.EditValue.of(double v) @safe
of
(2.5)));
void property_tree_edit_commands.main.run(property_tree_edit_commands.Edit e, property_tree_edit_commands.Policy pol = Policy(false)) @safe
run
(
(struct) property_tree_edit_commands.Edit

The whole mutation API surface: one value.

Edit
("stroke.cap",
(struct) property_tree_edit_commands.EditValue

Every leaf the component can produce an edit for, as one value type.

EditValue
.
property_tree_edit_commands.EditValue property_tree_edit_commands.EditValue.of(string v) @safe
of
("round")));
void property_tree_edit_commands.main.run(property_tree_edit_commands.Edit e, property_tree_edit_commands.Policy pol = Policy(false)) @safe
run
(
(struct) property_tree_edit_commands.Edit

The whole mutation API surface: one value.

Edit
("id",
(struct) property_tree_edit_commands.EditValue

Every leaf the component can produce an edit for, as one value type.

EditValue
.
property_tree_edit_commands.EditValue property_tree_edit_commands.EditValue.of(long v) @safe
of
(7L))); // @readOnly field
void property_tree_edit_commands.main.run(property_tree_edit_commands.Edit e, property_tree_edit_commands.Policy pol = Policy(false)) @safe
run
(
(struct) property_tree_edit_commands.Edit

The whole mutation API surface: one value.

Edit
("order",
(struct) property_tree_edit_commands.EditValue

Every leaf the component can produce an edit for, as one value type.

EditValue
.
property_tree_edit_commands.EditValue property_tree_edit_commands.EditValue.of(string v) @safe
of
("nope"))); // type mismatch
void property_tree_edit_commands.main.run(property_tree_edit_commands.Edit e, property_tree_edit_commands.Policy pol = Policy(false)) @safe
run
(
(struct) property_tree_edit_commands.Edit

The whole mutation API surface: one value.

Edit
("stroke.nope",
(struct) property_tree_edit_commands.EditValue

Every leaf the component can produce an edit for, as one value type.

EditValue
.
property_tree_edit_commands.EditValue property_tree_edit_commands.EditValue.of(long v) @safe
of
(1L))); // no such path
void property_tree_edit_commands.main.run(property_tree_edit_commands.Edit e, property_tree_edit_commands.Policy pol = Policy(false)) @safe
run
(
(struct) property_tree_edit_commands.Edit

The whole mutation API surface: one value.

Edit
("name",
(struct) property_tree_edit_commands.EditValue

Every leaf the component can produce an edit for, as one value type.

EditValue
.
property_tree_edit_commands.EditValue property_tree_edit_commands.EditValue.of(string v) @safe
of
("x")),
(struct) property_tree_edit_commands.Policy
Policy
(readOnly: true)); // policy
void std.stdio.writefln!(char, property_tree_edit_commands.Layer)(in char[] fmt, property_tree_edit_commands.Layer __param_1) @safe

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

writefln
("\n subject now: %s",
(local variable) property_tree_edit_commands.Layer l
l
);
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
("\nC12 — undo is the host replaying the inverses, newest first:");
foreach_reverse (
(parameter) property_tree_edit_commands.Edit e
e
;
(local variable) property_tree_edit_commands.Edit[] undo
undo
)
{ const
(local variable) const(property_tree_edit_commands.Applied) r
r
=
property_tree_edit_commands.Applied property_tree_edit_commands.apply!(property_tree_edit_commands.Layer)(ref property_tree_edit_commands.Layer subject, in property_tree_edit_commands.Edit e, in property_tree_edit_commands.Policy pol = Policy(false)) @safe

The public verb. Read-only policy is refused before the walk (C13).

apply
(
(local variable) property_tree_edit_commands.Layer l
l
,
(local variable) property_tree_edit_commands.Edit e
e
);
void std.stdio.writefln!(char, string, string, const(property_tree_edit_commands.Refusal))(in char[] fmt, string __param_1, string __param_2, const(property_tree_edit_commands.Refusal) __param_3) @safe

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

writefln
(" undo %-14s %-12s %s",
(local variable) property_tree_edit_commands.Edit e
e
.
(field) string property_tree_edit_commands.Edit.path
path
,
(local variable) property_tree_edit_commands.Edit e
e
.
(field) property_tree_edit_commands.EditValue property_tree_edit_commands.Edit.value
value
.
string property_tree_edit_commands.EditValue.toString() const pure @safe
toString
,
(local variable) const(property_tree_edit_commands.Applied) r
r
.
(field) property_tree_edit_commands.Refusal property_tree_edit_commands.Applied.refusal
refusal
);
}
void std.stdio.writefln!(char, property_tree_edit_commands.Layer)(in char[] fmt, property_tree_edit_commands.Layer __param_1) @safe

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

writefln
(" subject restored: %s",
(local variable) property_tree_edit_commands.Layer l
l
);
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
("\nC14 — a drag: previews mutate, only the commit records undo");
(local variable) property_tree_edit_commands.Edit[] undo
undo
= null;
foreach (
(parameter) double w
w
; [1.5, 2.0, 2.5, 3.0])
void property_tree_edit_commands.main.run(property_tree_edit_commands.Edit e, property_tree_edit_commands.Policy pol = Policy(false)) @safe
run
(
(struct) property_tree_edit_commands.Edit

The whole mutation API surface: one value.

Edit
("stroke.width",
(struct) property_tree_edit_commands.EditValue

Every leaf the component can produce an edit for, as one value type.

EditValue
.
property_tree_edit_commands.EditValue property_tree_edit_commands.EditValue.of(double v) @safe
of
(
(local variable) double w
w
),
(enum) property_tree_edit_commands.Phase
Phase
.
(enum value) property_tree_edit_commands.Phase.preview = cast(ubyte)0u
preview
));
void property_tree_edit_commands.main.run(property_tree_edit_commands.Edit e, property_tree_edit_commands.Policy pol = Policy(false)) @safe
run
(
(struct) property_tree_edit_commands.Edit

The whole mutation API surface: one value.

Edit
("stroke.width",
(struct) property_tree_edit_commands.EditValue

Every leaf the component can produce an edit for, as one value type.

EditValue
.
property_tree_edit_commands.EditValue property_tree_edit_commands.EditValue.of(double v) @safe
of
(3.0),
(enum) property_tree_edit_commands.Phase
Phase
.
(enum value) property_tree_edit_commands.Phase.commit = 1
commit
));
void std.stdio.writefln!(char, ulong, double)(in char[] fmt, ulong __param_1, double __param_2) @safe

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

writefln
(" undo entries after the whole drag: %s (width=%s)",
(local variable) property_tree_edit_commands.Edit[] undo
undo
.
(field) ulong property_tree_edit_commands.Edit[].length
length
,
(local variable) property_tree_edit_commands.Layer l
l
.
(field) property_tree_edit_commands.Stroke property_tree_edit_commands.Layer.stroke
stroke
.
(field) double property_tree_edit_commands.Stroke.width
width
);
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
(" NOTE: the inverse of the COMMIT is the value at the drag's end,");
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
(" not its start — so a host that wants one undo per drag must");
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
(" keep the first preview's prior value. That is a spec decision.");
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
("\nC15 — the variant switch is not an assignment of any leaf type");
(alias) property_tree_edit_commands.Paint = std.sumtype.SumType!(Solid, Gradient)
Paint
(local variable) std.sumtype.SumType!(Solid, Gradient) p
p
=
(struct) property_tree_edit_commands.Solid
Solid
(0xff0000ff);
void std.stdio.writefln!(char, string)(in char[] fmt, string __param_1) @safe

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

writefln
(" before: %s",
(local variable) std.sumtype.SumType!(Solid, Gradient) p
p
.
string property_tree_edit_commands.main.match!(std.sumtype.SumType!(Solid, Gradient))(ref std.sumtype.SumType!(Solid, Gradient) __param_0) pure nothrow @safe

Calls a type-appropriate function with the value held in a SumType.

For each possible type the SumType can hold, the given handlers are checked, in order, to see whether they accept a single argument of that type. The first one that does is chosen as the match for that type. (Note that the first match may not always be the most exact match. See "Avoiding unintentional matches" for one common pitfall.)

Every type must have a matching handler, and every handler must match at least one type. This is enforced at compile time.

Handlers may be functions, delegates, or objects with opCall overloads. If a function with more than one overload is given as a handler, all of the overloads are considered as potential matches.

Templated handlers are also accepted, and will match any type for which they can be implicitly instantiated. (Remember that a function literal without an explicit argument type is considered a template.)

If multiple SumTypes are passed to match, their values are passed to the handlers as separate arguments, and matching is done for each possible combination of value types. See "Multiple dispatch" for an example.

Examples

Avoiding unintentional matches

Sometimes, implicit conversions may cause a handler to match more types than intended. The example below shows two solutions to this problem.

alias Number = SumType!(double, int);

Number x;

// Problem: because int implicitly converts to double, the double
// handler is used for both types, and the int handler never matches.
assert(!__traits(compiles,
    x.match!(
        (double d) => "got double",
        (int n) => "got int"
    )
));

// Solution 1: put the handler for the "more specialized" type (in this
// case, int) before the handler for the type it converts to.
assert(__traits(compiles,
    x.match!(
        (int n) => "got int",
        (double d) => "got double"
    )
));

// Solution 2: use a template that only accepts the exact type it's
// supposed to match, instead of any type that implicitly converts to it.
alias exactly(T, alias fun) = function (arg)
{
    static assert(is(typeof(arg) == T));
    return fun(arg);
};

// Now, even if we put the double handler first, it will only be used for
// doubles, not ints.
assert(__traits(compiles,
    x.match!(
        exactly!(double, d => "got double"),
        exactly!(int, n => "got int")
    )
));

Multiple dispatch

Pattern matching can be performed on multiple SumTypes at once by passing handlers with multiple arguments. This usually leads to more concise code than using nested calls to match, as show below.

struct Point2D { double x, y; }
struct Point3D { double x, y, z; }

alias Point = SumType!(Point2D, Point3D);

version (none)
{
    // This function works, but the code is ugly and repetitive.
    // It uses three separate calls to match!
    @safe pure nothrow @nogc
    bool sameDimensions(Point p1, Point p2)
    {
        return p1.match!(
            (Point2D _) => p2.match!(
                (Point2D _) => true,
                _ => false
            ),
            (Point3D _) => p2.match!(
                (Point3D _) => true,
                _ => false
            )
        );
    }
}

// This version is much nicer.
@safe pure nothrow @nogc
bool sameDimensions(Point p1, Point p2)
{
    alias doMatch = match!(
        (Point2D _1, Point2D _2) => true,
        (Point3D _1, Point3D _2) => true,
        (_1, _2) => false
    );

    return doMatch(p1, p2);
}

Point a = Point2D(1, 2);
Point b = Point2D(3, 4);
Point c = Point3D(5, 6, 7);
Point d = Point3D(8, 9, 0);

assert( sameDimensions(a, b));
assert( sameDimensions(c, d));
assert(!sameDimensions(a, c));
assert(!sameDimensions(d, b));
@returnsThe value returned from the handler that matches the currently-held type.@see

visit

The actual match function.

@paramargs One or more SumType objects.
match
!(s => text("Solid ", s.rgba),
g => text("Gradient ", g.stops))); // The rule is DIRECTIONAL, and finer than "SumType assignment is @system": // `opAssign` is unsafe when the variant being OVERWRITTEN may hold // indirections, because a reference into the old payload would dangle.
void std.stdio.writefln!(char, string)(in char[] fmt, string __param_1) @safe

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

writefln
(" q = Gradient(...) over a maybe-Solid @safe? %s",
__traits(compiles, () @safe {
(alias) property_tree_edit_commands.Paint = std.sumtype.SumType!(Solid, Gradient)
Paint
(local variable) std.sumtype.SumType!(Solid, Gradient) q
q
;
(local variable) std.sumtype.SumType!(Solid, Gradient) q
q
=
std.sumtype.SumType!(Solid, Gradient) std.sumtype.SumType!(property_tree_edit_commands.Solid, property_tree_edit_commands.Gradient).opAssign(property_tree_edit_commands.Gradient rhs) pure nothrow @nogc ref @safe

Assigns a value to a SumType.

If any of the SumType's members other than the one being assigned to contain pointers or references, it is possible for the assignment to cause memory corruption (see the "Memory corruption" example below for an illustration of how). Therefore, such assignments are considered @system.

An individual assignment can be @trusted if the caller can guarantee that there are no outstanding references to any SumType members that contain pointers or references at the time the assignment occurs.

Examples

Memory corruption

This example shows how assignment to a SumType can be used to cause memory corruption in @system code. In @safe code, the assignment s = 123 would not be allowed.

SumType!(int*, int) s = new int;
s.tryMatch!(
    (ref int* p) {
        s = 123; // overwrites `p`
        return *p; // undefined behavior
    }
);
Gradient
("a", "b"); }())
? "yes — Solid has no indirections" : "no");
void std.stdio.writefln!(char, string)(in char[] fmt, string __param_1) @safe

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

writefln
(" q = Solid(...) over a maybe-Gradient @safe? %s",
__traits(compiles, () @safe {
(alias) property_tree_edit_commands.Paint = std.sumtype.SumType!(Solid, Gradient)
Paint
(local variable) std.sumtype.SumType!(Solid, Gradient) q
q
;
(local variable) std.sumtype.SumType!(Solid, Gradient) q
q
=
std.sumtype.SumType!(Solid, Gradient) std.sumtype.SumType!(property_tree_edit_commands.Solid, property_tree_edit_commands.Gradient).opAssign(property_tree_edit_commands.Solid rhs) pure nothrow @nogc ref @system

Assigns a value to a SumType.

If any of the SumType's members other than the one being assigned to contain pointers or references, it is possible for the assignment to cause memory corruption (see the "Memory corruption" example below for an illustration of how). Therefore, such assignments are considered @system.

An individual assignment can be @trusted if the caller can guarantee that there are no outstanding references to any SumType members that contain pointers or references at the time the assignment occurs.

Examples

Memory corruption

This example shows how assignment to a SumType can be used to cause memory corruption in @system code. In @safe code, the assignment s = 123 would not be allowed.

SumType!(int*, int) s = new int;
s.tryMatch!(
    (ref int* p) {
        s = 123; // overwrites `p`
        return *p; // undefined behavior
    }
);
Solid
(1); }())
? "yes" : "no — Gradient holds strings, so the overwrite is @system");
void property_tree_edit_commands.switchTo!(property_tree_edit_commands.Gradient)(ref std.sumtype.SumType!(Solid, Gradient) p, property_tree_edit_commands.Gradient v) pure nothrow @nogc @trusted

The one @trusted seam a variant switch needs, and its precondition.

PRECONDITION

no reference into the old payload outlives this call — which the frame model guarantees, since rows are rebuilt after every edit.

switchTo
(
(local variable) std.sumtype.SumType!(Solid, Gradient) p
p
,
(struct) property_tree_edit_commands.Gradient
Gradient
("black", "white", 3));
void std.stdio.writefln!(char, string)(in char[] fmt, string __param_1) @safe

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

writefln
(" after switchTo (one @trusted seam): %s",
(local variable) std.sumtype.SumType!(Solid, Gradient) p
p
.
string property_tree_edit_commands.main.match!(std.sumtype.SumType!(Solid, Gradient))(ref std.sumtype.SumType!(Solid, Gradient) __param_0) pure nothrow @safe

Calls a type-appropriate function with the value held in a SumType.

For each possible type the SumType can hold, the given handlers are checked, in order, to see whether they accept a single argument of that type. The first one that does is chosen as the match for that type. (Note that the first match may not always be the most exact match. See "Avoiding unintentional matches" for one common pitfall.)

Every type must have a matching handler, and every handler must match at least one type. This is enforced at compile time.

Handlers may be functions, delegates, or objects with opCall overloads. If a function with more than one overload is given as a handler, all of the overloads are considered as potential matches.

Templated handlers are also accepted, and will match any type for which they can be implicitly instantiated. (Remember that a function literal without an explicit argument type is considered a template.)

If multiple SumTypes are passed to match, their values are passed to the handlers as separate arguments, and matching is done for each possible combination of value types. See "Multiple dispatch" for an example.

Examples

Avoiding unintentional matches

Sometimes, implicit conversions may cause a handler to match more types than intended. The example below shows two solutions to this problem.

alias Number = SumType!(double, int);

Number x;

// Problem: because int implicitly converts to double, the double
// handler is used for both types, and the int handler never matches.
assert(!__traits(compiles,
    x.match!(
        (double d) => "got double",
        (int n) => "got int"
    )
));

// Solution 1: put the handler for the "more specialized" type (in this
// case, int) before the handler for the type it converts to.
assert(__traits(compiles,
    x.match!(
        (int n) => "got int",
        (double d) => "got double"
    )
));

// Solution 2: use a template that only accepts the exact type it's
// supposed to match, instead of any type that implicitly converts to it.
alias exactly(T, alias fun) = function (arg)
{
    static assert(is(typeof(arg) == T));
    return fun(arg);
};

// Now, even if we put the double handler first, it will only be used for
// doubles, not ints.
assert(__traits(compiles,
    x.match!(
        exactly!(double, d => "got double"),
        exactly!(int, n => "got int")
    )
));

Multiple dispatch

Pattern matching can be performed on multiple SumTypes at once by passing handlers with multiple arguments. This usually leads to more concise code than using nested calls to match, as show below.

struct Point2D { double x, y; }
struct Point3D { double x, y, z; }

alias Point = SumType!(Point2D, Point3D);

version (none)
{
    // This function works, but the code is ugly and repetitive.
    // It uses three separate calls to match!
    @safe pure nothrow @nogc
    bool sameDimensions(Point p1, Point p2)
    {
        return p1.match!(
            (Point2D _) => p2.match!(
                (Point2D _) => true,
                _ => false
            ),
            (Point3D _) => p2.match!(
                (Point3D _) => true,
                _ => false
            )
        );
    }
}

// This version is much nicer.
@safe pure nothrow @nogc
bool sameDimensions(Point p1, Point p2)
{
    alias doMatch = match!(
        (Point2D _1, Point2D _2) => true,
        (Point3D _1, Point3D _2) => true,
        (_1, _2) => false
    );

    return doMatch(p1, p2);
}

Point a = Point2D(1, 2);
Point b = Point2D(3, 4);
Point c = Point3D(5, 6, 7);
Point d = Point3D(8, 9, 0);

assert( sameDimensions(a, b));
assert( sameDimensions(c, d));
assert(!sameDimensions(a, c));
assert(!sameDimensions(d, b));
@returnsThe value returned from the handler that matches the currently-held type.@see

visit

The actual match function.

@paramargs One or more SumType objects.
match
!(s => text("Solid ", s.rgba), g => text("Gradient ", g.stops)));
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
(" so the edit vocabulary needs EditValue.Kind.variant, and the");
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
(" component needs exactly one @trusted function, not @trusted rows.");
}