std.typecons
Source: std/typecons.d
Synopsis:
// value tuples alias Coord = Tuple!(float, "x", float, "y", float, "z"); Coord c; c[1] = 1; // access by index c.z = 1; // access by given name alias DicEntry = Tuple!(string, string); // names can be omitted // Rebindable references to const and immutable objects void bar() { const w1 = new Widget, w2 = new Widget; w1.foo(); // w1 = w2 would not work; can't rebind const object auto r = Rebindable!(const Widget)(w1); // invoke method as if r were a Widget object r.foo(); // rebind r to refer to another object r = w2; }
- struct Unique(T);
- Encapsulates unique ownership of a resource. Resource of type T is deleted at the end of the scope, unless it is transferred. The transfer can be explicit, by calling release, or implicit, when returning Unique from a function. The resource can be a polymorphic class object, in which case Unique behaves polymorphically too.Examples:
static struct S { int i; this(int i){this.i = i;} } Unique!S produce() { // Construct a unique instance of S on the heap Unique!S ut = new S(5); // Implicit transfer of ownership return ut; } // Borrow a unique resource by ref void increment(ref Unique!S ur) { ur.i++; } void consume(Unique!S u2) { assert(u2.i == 6); // Resource automatically deleted here } Unique!S u1; assert(u1.isEmpty); u1 = produce(); increment(u1); assert(u1.i == 6); //consume(u1); // Error: u1 is not copyable // Transfer ownership of the resource consume(u1.release); assert(u1.isEmpty);
- alias RefT = T;
- Represents a reference to T. Resolves to T* if T is a value type.
- Unique!T create(A...)(auto ref A args) if (__traits(compiles, new T(args)));
- Allows safe construction of Unique. It creates the resource and guarantees unique ownership of it (unless T publishes aliases of this).
Note: Nested structs/classes cannot be created.
Parameters:A args Arguments to pass to T's constructor. static class C {} auto u = Unique!(C).create();
- this(RefT p);
- Constructor that takes an rvalue. It will ensure uniqueness, as long as the rvalue isn't just a view on an lvalue (e.g., a cast). Typical usage:
Unique!Foo f = new Foo;
- this(ref RefT p);
- Constructor that takes an lvalue. It nulls its source. The nulling will ensure uniqueness as long as there are no previous aliases to the source.
- this(U)(Unique!U u) if (is(u.RefT : RefT));
- Constructor that takes a Unique of a type that is convertible to our type.Typically used to transfer a Unique rvalue of derived type to a Unique of base type.
Example:
class C : Object {} Unique!C uc = new C; Unique!Object uo = uc.release;
- void opAssign(U)(Unique!U u) if (is(u.RefT : RefT));
- Transfer ownership from a Unique of a type that is convertible to our type.
- const @property bool isEmpty();
- Returns whether the resource exists.
- Unique release();
- Transfer ownership to a Unique rvalue. Nullifies the current contents.
- RefT opDot();
- Forwards member access to contents.
- struct Tuple(Specs...);
- Tuple of values, for example Tuple!(int, string) is a record that stores an int and a string. Tuple can be used to bundle values together, notably when returning multiple values from a function. If obj is a tuple, the individual members are accessible with the syntax obj[0] for the first field, obj[1] for the second, and so on.The choice of zero-based indexing instead of one-base indexing was motivated by the ability to use value tuples with various compile-time loop constructs (e.g. type tuple iteration), all of which use zero-based indexing.
Example:
Tuple!(int, int) point; // assign coordinates point[0] = 5; point[1] = 6; // read coordinates auto x = point[0]; auto y = point[1];
Tuple members can be named. It is legal to mix named and unnamed members. The method above is still applicable to all fields.Example:
alias Entry = Tuple!(int, "index", string, "value"); Entry e; e.index = 4; e.value = "Hello"; assert(e[1] == "Hello"); assert(e[0] == 4);
Tuples with named fields are distinct types from tuples with unnamed fields, i.e. each naming imparts a separate type for the tuple. Two tuple differing in naming only are still distinct, even though they might have the same structure.Example:
Tuple!(int, "x", int, "y") point1; Tuple!(int, int) point2; assert(!is(typeof(point1) == typeof(point2))); // passes
- alias Types = staticMap!(extractType, fieldSpecs);
- The type of the tuple's components.
- alias fieldNames = staticMap!(extractName, fieldSpecs);
- The names of the tuple's components. Unnamed fields have empty names.Examples:
alias Fields = Tuple!(int, "id", string, float); static assert(Fields.fieldNames == TypeTuple!("id", "", ""));
- Types expand;
- Use t.expand for a tuple t to expand it into its components. The result of expand acts as if the tuple components were listed as a list of values. (Ordinarily, a Tuple acts as a single value.)Examples:
auto t = tuple(1, " hello ", 2.3); writeln(t); // Tuple!(int, string, double)(1, " hello ", 2.3) writeln(t.expand); // 1 hello 2.3
- this(Types values);
- Constructor taking one value for each field.
- this(U, size_t n)(U[n] values) if (n == Types.length && allSatisfy!(isBuildableFrom!U, Types));
- Constructor taking a compatible array.Examples:
int[2] ints; Tuple!(int, int) t = ints;
- this(U)(U another) if (areBuildCompatibleTuples!(typeof(this), U));
- Constructor taking a compatible tuple.
- bool opEquals(R)(R rhs) if (areCompatibleTuples!(typeof(this), R, "=="));
const bool opEquals(R)(R rhs) if (areCompatibleTuples!(typeof(this), R, "==")); - Comparison for equality.
- int opCmp(R)(R rhs) if (areCompatibleTuples!(typeof(this), R, "<"));
const int opCmp(R)(R rhs) if (areCompatibleTuples!(typeof(this), R, "<")); - Comparison for ordering.
- void opAssign(R)(auto ref R rhs) if (areCompatibleTuples!(typeof(this), R, "="));
- Assignment from another tuple. Each element of the source must be implicitly assignable to the respective element of the target.
- @property ref @trusted Tuple!(sliceSpecs!(from, to)) slice(size_t from, size_t to)() if (from <= to && to <= Types.length);
-
Examples:
Tuple!(int, string, float, double) a; a[1] = "abc"; a[2] = 4.5; auto s = a.slice!(1, 3); static assert(is(typeof(s) == Tuple!(string, float))); assert(s[0] == "abc" && s[1] == 4.5);
- void toString(DG)(scope DG sink);
- Converts to string.
- ReverseTupleType!T reverse(T)(T t) if (isTuple!T);
-
Examples:
auto tup = tuple(1, "2"); assert(tup.reverse == tuple("2", 1));
- template tuple(Names...)
- Returns a Tuple object instantiated and initialized according to the arguments.Examples:
auto value = tuple(5, 6.7, "hello"); assert(value[0] == 5); assert(value[1] == 6.7); assert(value[2] == "hello"); // Field names can be provided. auto entry = tuple!("index", "value")(4, "Hello"); assert(entry.index == 4); assert(entry.value == "Hello");
- template isTuple(T)
- Returns true if and only if T is an instance of the Tuple struct template.Examples:
static assert(isTuple!(Tuple!())); static assert(isTuple!(Tuple!(int))); static assert(isTuple!(Tuple!(int, real, string))); static assert(isTuple!(Tuple!(int, "x", real, "y"))); static assert(isTuple!(Tuple!(int, Tuple!(real), string)));
- template Rebindable(T) if (is(T == class) || is(T == interface) || isDynamicArray!T)
- Rebindable!(T) is a simple, efficient wrapper that behaves just like an object of type T, except that you can reassign it to refer to another object. For completeness, Rebindable!(T) aliases itself away to T if T is a non-const object type. However, Rebindable!(T) does not compile if T is a non-class type.Regular const object references cannot be reassigned:
class Widget { int x; int y() const { return x; } } const a = new Widget; a.y(); // fine a.x = 5; // error! can't modify const a a = new Widget; // error! can't modify const a
However, Rebindable!(Widget) does allow reassignment, while otherwise behaving exactly like a const Widget:auto a = Rebindable!(const Widget)(new Widget); a.y(); // fine a.x = 5; // error! can't modify const a a = new Widget; // fine
You may want to use Rebindable when you want to have mutable storage referring to const objects, for example an array of references that must be sorted in place. Rebindable does not break the soundness of D's type system and does not incur any of the risks usually associated with cast. - Rebindable!T rebindable(T)(T obj) if (is(T == class) || is(T == interface) || isDynamicArray!T);
- Convenience function for creating a Rebindable using automatic type inference.
- Rebindable!T rebindable(T)(Rebindable!T obj);
- This function simply returns the Rebindable object passed in. It's useful in generic programming cases when a given object may be either a regular class or a Rebindable.
- template UnqualRef(T) if (is(T == class) || is(T == interface))
- Similar to Rebindable!(T) but strips all qualifiers from the reference as opposed to just constness / immutability. Primary intended use case is with shared (having thread-local reference to shared class data)Examples:
class Data {} static shared(Data) a; static UnqualRef!(shared Data) b; import core.thread; auto thread = new core.thread.Thread({ a = new shared Data(); b = new shared Data(); }); thread.start(); thread.join(); assert(a !is null); assert(b is null);
- string alignForSize(E...)(string[] names...);
- Order the provided members to minimize size while preserving alignment. Returns a declaration to be mixed in. Alignment is not always optimal for 80-bit reals, nor for structs declared as align(1).Examples:
struct Banner { mixin(alignForSize!(byte[6], double)(["name", "height"])); }
- struct Nullable(T);
- Defines a value paired with a distinctive "null" state that denotes the absence of a value. If default constructed, a Nullable!T object starts in the null state. Assigning it renders it non-null. Calling nullify can nullify it again.Examples:
Nullable!int a; assert(a.isNull); a = 5; assert(!a.isNull); assert(a == 5);
- inout this(inout T value);
- Constructor initializing this with value.
- const pure nothrow @property @safe bool isNull();
- Returns true if and only if this is in the null state.
- void nullify()();
- Forces this to the null state.
- void opAssign()(T value);
- Assigns value to the internally-held state. If the assignment succeeds, this becomes non-null.
- inout pure nothrow @property ref @safe inout(T) get();
- Gets the value. this must not be in the null state. This function is also called for the implicit conversion to T.
- struct Nullable(T, T nullValue);
- Just like Nullable!T, except that the null state is defined as a particular value. For example, Nullable!(uint, uint.max) is an uint that sets aside the value uint.max to denote a null state. Nullable!(T, nullValue) is more storage-efficient than Nullable!T because it does not need to store an extra bool.
- this(T value);
- Constructor initializing this with value.
- const @property bool isNull();
- Returns true if and only if this is in the null state.
- void nullify()();
- Forces this to the null state.
- void opAssign()(T value);
- Assigns value to the internally-held state. No null checks are made. Note that the assignment may leave this in the null state.
- inout @property ref inout(T) get();
- Gets the value. this must not be in the null state. This function is also called for the implicit conversion to T.
- struct NullableRef(T);
- Just like Nullable!T, except that the object refers to a value sitting elsewhere in memory. This makes assignments overwrite the initially assigned value. Internally NullableRef!T only stores a pointer to T (i.e., Nullable!T.sizeof == (T*).sizeof).
- pure nothrow @safe this(T* value);
- Constructor binding this with value.
- pure nothrow @safe void bind(T* value);
- Binds the internal state to value.
- const pure nothrow @property @safe bool isNull();
- Returns true if and only if this is in the null state.
- pure nothrow @safe void nullify();
- Forces this to the null state.
- void opAssign()(T value) if (isAssignable!T);
- Assigns value to the internally-held state.
- inout pure nothrow @property ref @safe inout(T) get();
- Gets the value. this must not be in the null state. This function is also called for the implicit conversion to T.
- template BlackHole(Base)
- BlackHole!Base is a subclass of Base which automatically implements all abstract member functions in Base as do-nothing functions. Each auto-implemented function just returns the default value of the return type without doing anything.The name came from Class::BlackHole Perl module by Sean M. Burke.
Example:
abstract class C { int m_value; this(int v) { m_value = v; } int value() @property { return m_value; } abstract real realValue() @property; abstract void doSomething(); } void main() { auto c = new BlackHole!C(42); writeln(c.value); // prints "42" // Abstract functions are implemented as do-nothing: writeln(c.realValue); // prints "NaN" c.doSomething(); // does nothing }
See Also:AutoImplement, generateEmptyFunction - template WhiteHole(Base)
- WhiteHole!Base is a subclass of Base which automatically implements all abstract member functions as throw-always functions. Each auto-implemented function fails with throwing an Error and does never return. Useful for trapping use of not-yet-implemented functions.The name came from Class::WhiteHole Perl module by Michael G Schwern.
Example:
class C { abstract void notYetImplemented(); } void main() { auto c = new WhiteHole!C; c.notYetImplemented(); // throws an Error }
See Also:AutoImplement, generateAssertTrap - class AutoImplement(Base, alias how, alias what = isAbstractFunction): Base;
- AutoImplement automatically implements (by default) all abstract member functions in the class or interface Base in specified way.Parameters:
how template which specifies how functions will be implemented/overridden. Two arguments are passed to how: the type Base and an alias to an implemented function. Then how must return an implemented function body as a string. The generated function body can use these keywords: - a0, a1, …: arguments passed to the function;
- args: a tuple of the arguments;
- self: an alias to the function itself;
- parent: an alias to the overridden function (if any).
// Prints log messages for each call to overridden functions. string generateLogger(C, alias fun)() @property { import std.traits; enum qname = C.stringof ~ "." ~ __traits(identifier, fun); string stmt; stmt ~= q{ struct Importer { import std.stdio; } }; stmt ~= `Importer.writeln("Log: ` ~ qname ~ `(", args, ")");`; static if (!__traits(isAbstractFunction, fun)) { static if (is(ReturnType!fun == void)) stmt ~= q{ parent(args); }; else stmt ~= q{ auto r = parent(args); Importer.writeln("--> ", r); return r; }; } return stmt; }
what template which determines what functions should be implemented/overridden. An argument is passed to what: an alias to a non-final member function in Base. Then what must return a boolean value. Return true to indicate that the passed function should be implemented/overridden. // Sees if fun returns something. enum bool hasValue(alias fun) = !is(ReturnType!(fun) == void);
Note: Generated code is inserted in the scope of std.typecons module. Thus, any useful functions outside std.typecons cannot be used in the generated code. To workaround this problem, you may import necessary things in a local struct, as done in the generateLogger() template in the above example.
Bugs:- Variadic arguments to constructors are not forwarded to super.
- Deep interface inheritance causes compile error with messages like "Error: function std.typecons.AutoImplement!(Foo).AutoImplement.bar does not override any function". [Bugzilla 2525, Bugzilla 3525]
- The parent keyword is actually a delegate to the super class' corresponding member function. [Bugzilla 2540]
- Using alias template parameter in how and/or what may cause strange compile error. Use template tuple parameter instead to workaround this problem. [Bugzilla 4217]
- template generateEmptyFunction(C, func...)
template generateAssertTrap(C, func...) - Predefined how-policies for AutoImplement. These templates are used by BlackHole and WhiteHole, respectively.
- template wrap(Targets...) if (Targets.length >= 1 && allSatisfy!(isMutable, Targets))
template wrap(Targets...) if (Targets.length >= 1 && !allSatisfy!(isMutable, Targets)) - Supports structural based typesafe conversion.
- template unwrap(Target) if (isMutable!Target)
template unwrap(Target) if (!isMutable!Target) - Extract object which wrapped by wrap.Examples:
interface Quack { int quack(); @property int height(); } interface Flyer { @property int height(); } class Duck : Quack { int quack() { return 1; } @property int height() { return 10; } } class Human { int quack() { return 2; } @property int height() { return 20; } } Duck d1 = new Duck(); Human h1 = new Human(); interface Refleshable { int reflesh(); } // does not have structural conformance static assert(!__traits(compiles, d1.wrap!Refleshable)); static assert(!__traits(compiles, h1.wrap!Refleshable)); // strict upcast Quack qd = d1.wrap!Quack; assert(qd is d1); assert(qd.quack() == 1); // calls Duck.quack // strict downcast Duck d2 = qd.unwrap!Duck; assert(d2 is d1); // structural upcast Quack qh = h1.wrap!Quack; assert(qh.quack() == 2); // calls Human.quack // structural downcast Human h2 = qh.unwrap!Human; assert(h2 is h1); // structural upcast (two steps) Quack qx = h1.wrap!Quack; // Human -> Quack Flyer fx = qx.wrap!Flyer; // Quack -> Flyer assert(fx.height == 20); // calls Human.height // strucural downcast (two steps) Quack qy = fx.unwrap!Quack; // Flyer -> Quack Human hy = qy.unwrap!Human; // Quack -> Human assert(hy is h1); // strucural downcast (one step) Human hz = fx.unwrap!Human; // Flyer -> Human assert(hz is h1);
Examples:interface A { int run(); } interface B { int stop(); @property int status(); } class X { int run() { return 1; } int stop() { return 2; } @property int status() { return 3; } } auto x = new X(); auto ab = x.wrap!(A, B); A a = ab; B b = ab; assert(a.run() == 1); assert(b.stop() == 2); assert(b.status == 3); static assert(functionAttributes!(typeof(ab).status) & FunctionAttribute.property);
- enum RefCountedAutoInitialize: int;
- Options regarding auto-initialization of a RefCounted object (see the definition of RefCounted below).
- struct RefCounted(T, RefCountedAutoInitialize autoInit = RefCountedAutoInitialize.yes) if (!is(T == class) && !is(T == interface));
- Defines a reference-counted object containing a T value as payload. RefCounted keeps track of all references of an object, and when the reference count goes down to zero, frees the underlying store. RefCounted uses malloc and free for operation.RefCounted is unsafe and should be used with care. No references to the payload should be escaped outside the RefCounted object. The autoInit option makes the object ensure the store is automatically initialized. Leaving autoInit == RefCountedAutoInitialize.yes (the default option) is convenient but has the cost of a test whenever the payload is accessed. If autoInit == RefCountedAutoInitialize.no, user code must call either refCountedStore.isInitialized or refCountedStore.ensureInitialized before attempting to access the payload. Not doing so results in null pointer dereference.Examples:
// A pair of an $(D int) and a $(D size_t) - the latter being the // reference count - will be dynamically allocated auto rc1 = RefCounted!int(5); assert(rc1 == 5); // No more allocation, add just one extra reference count auto rc2 = rc1; // Reference semantics rc2 = 42; assert(rc1 == 42); // the pair will be freed when rc1 and rc2 go out of scope
- struct RefCountedStore;
- RefCounted storage implementation.
- const nothrow @property @safe bool isInitialized();
- Returns true if and only if the underlying store has been allocated and initialized.
- const nothrow @property @safe size_t refCount();
- Returns underlying reference count if it is allocated and initialized (a positive integer), and 0 otherwise.
- void ensureInitialized();
- Makes sure the payload was properly initialized. Such a call is typically inserted before using the payload.
- inout nothrow @property ref @safe inout(RefCountedStore) refCountedStore();
- Returns storage implementation struct.
- this(A...)(auto ref A args) if (A.length > 0);
- Constructor that initializes the payload.
Postcondition: refCountedStore.isInitialized
- void opAssign(typeof(this) rhs);
void opAssign(T rhs); - Assignment operators
- @property ref T refCountedPayload();
inout nothrow @property ref @safe inout(T) refCountedPayload();
- template Proxy(alias a)
- Make proxy for a.Examples:
struct MyInt { private int value; mixin Proxy!value; this(int n){ value = n; } } MyInt n = 10; // Enable operations that original type has. ++n; assert(n == 11); assert(n * 2 == 22); void func(int n) { } // Disable implicit conversions to original type. //int x = n; //func(n);
- struct Typedef(T, T init = T.init, string cookie = null);
- Typedef allows the creation of a unique type which is based on an existing type. Unlike the alias feature, Typedef ensures the two types are not considered as equals.
Example:
alias MyInt = Typedef!int; static void takeInt(int) { } static void takeMyInt(MyInt) { } int i; takeInt(i); // ok takeMyInt(i); // fails MyInt myInt; takeInt(myInt); // fails takeMyInt(myInt); // ok
Parameters:init Optional initial value for the new type. For example: alias MyInt = Typedef!(int, 10); MyInt myInt; assert(myInt == 10); // default-initialized to 10
cookie Optional, used to create multiple unique types which are based on the same origin type T. For example: alias TypeInt1 = Typedef!int; alias TypeInt2 = Typedef!int; // The two Typedefs are the same type. static assert(is(TypeInt1 == TypeInt2)); alias MoneyEuros = Typedef!(float, float.init, "euros"); alias MoneyDollars = Typedef!(float, float.init, "dollars"); // The two Typedefs are _not_ the same type. static assert(!is(MoneyEuros == MoneyDollars));
- template TypedefType(T)
- Get the underlying type which a Typedef wraps. If T is not a Typedef it will alias itself to T.Examples:
import std.typecons: Typedef, TypedefType; import std.conv: to; alias MyInt = Typedef!int; static assert(is(TypedefType!MyInt == int)); /// Instantiating with a non-Typedef will return that type static assert(is(TypedefType!int == int)); string num = "5"; // extract the needed type MyInt myInt = MyInt( num.to!(TypedefType!MyInt) ); assert(myInt == 5); // cast to the underlying type to get the value that's being wrapped int x = cast(TypedefType!MyInt)myInt; alias MyIntInit = Typedef!(int, 42); static assert(is(TypedefType!MyIntInit == int)); static assert(MyIntInit() == 42);
- template scoped(T) if (is(T == class))
- Allocates a class object right inside the current scope, therefore avoiding the overhead of new. This facility is unsafe; it is the responsibility of the user to not escape a reference to the object outside the scope.
Note: it's illegal to move a class reference even if you are sure there are no pointers to it. As such, it is illegal to move a scoped object.
Examples:class A { int x; this() {x = 0;} this(int i){x = i;} } // Standard usage auto a1 = scoped!A(); auto a2 = scoped!A(1); a1.x = 42; assert(a1.x == 42); assert(a2.x == 1); // Restrictions static assert(!is(typeof({ auto e1 = a1; // illegal, scoped objects can't be copied assert([a2][0].x == 42); // ditto alias ScopedObject = typeof(a1); auto e2 = ScopedObject(); //Illegal, must be built via scoped!A auto e3 = ScopedObject(1); //Illegal, must be built via scoped!A }))); // Use as member variable struct B { typeof(scoped!A()) a; // note the trailing parentheses } // Use with alias alias makeScopedA = scoped!A; auto a6 = makeScopedA(); auto a7 = makeScopedA();
- template Flag(string name)
- Defines a simple, self-documenting yes/no flag. This makes it easy for APIs to define functions accepting flags without resorting to bool, which is opaque in calls, and without needing to define an enumerated type separately. Using Flag!"Name" instead of bool makes the flag's meaning visible in calls. Each yes/no flag has its own type, which makes confusions and mix-ups impossible.
Example: Code calling getLine (usually far away from its definition) can't be understood without looking at the documentation, even by users familiar with the API:
string getLine(bool keepTerminator) { ... if (keepTerminator) ... ... } ... auto line = getLine(false);
Assuming the reverse meaning (i.e. "ignoreTerminator") and inserting the wrong code compiles and runs with erroneous results. After replacing the boolean parameter with an instantiation of Flag, code calling getLine can be easily read and understood even by people not fluent with the API:string getLine(Flag!"keepTerminator" keepTerminator) { ... if (keepTerminator) ... ... } ... auto line = getLine(Flag!"keepTerminator".yes);
Passing categorical data by means of unstructured bool parameters is classified under "simple-data coupling" by Steve McConnell in the Code Complete book, along with three other kinds of coupling. The author argues citing several studies that coupling has a negative effect on code quality. Flag offers a simple structuring method for passing yes/no flags to APIs. An alias can be used to reduce the verbosity of the flag's type:alias KeepTerminator = Flag!"keepTerminator"; string getline(KeepTerminator keepTerminator) { ... if (keepTerminator) ... ... } ... // Code calling getLine can now refer to flag values using the shorter name: auto line = getLine(KeepTerminator.yes);
- struct Yes;
struct No; - Convenience names that allow using e.g. Yes.encryption instead of Flag!"encryption".yes and No.encryption instead of Flag!"encryption".no.Examples:
Flag!"abc" flag1; assert(flag1 == Flag!"abc".no); assert(flag1 == No.abc); assert(!flag1); if (flag1) assert(false); flag1 = Yes.abc; assert(flag1); if (!flag1) assert(false); if (flag1) {} else assert(false); assert(flag1 == Yes.abc);
- template isBitFlagEnum(E)
- Detect whether an enum is of integral type and has only "flag" values (i.e. values with a bit count of exactly 1). Additionally, a zero value is allowed for compatibility with enums including a "None" value.Examples:
enum A { None, A = 1<<0, B = 1<<1, C = 1<<2, D = 1<<3, } static assert(isBitFlagEnum!A); enum B { A, B, C, D // D == 3 } static assert(!isBitFlagEnum!B); enum C: double { A = 1<<0, B = 1<<1 } static assert(!isBitFlagEnum!C);
- struct BitFlags(E, Flag!"unsafe" unsafe = No.unsafe) if (unsafe || isBitFlagEnum!E);
- A typesafe structure for storing combination of enum values.This template defines a simple struct to represent bitwise OR combinations of enum values. It can be used if all the enum values are integral constants with a bit count of at most 1, or if the unsafe parameter is explicitly set to Yes. This is much safer than using the enum itself to store the OR combination, which can produce surprising effects like this:
enum E { A = 1<<0, B = 1<<1 } E e = E.A | E.B; // will throw SwitchError final switch(e) { case E.A: return; case E.B: return; }
Examples:BitFlags can be manipulated with the usual operators// You can use such an enum with BitFlags straight away enum Enum { None, A = 1<<0, B = 1<<1, C = 1<<2 } static assert(__traits(compiles, BitFlags!Enum)); // You need to specify the $(D unsafe) parameter for enum with custom values enum UnsafeEnum { A, B, C, D = B|C } static assert(!__traits(compiles, BitFlags!UnsafeEnum)); static assert(__traits(compiles, BitFlags!(UnsafeEnum, Yes.unsafe))); immutable BitFlags!Enum flags_empty; // A default constructed BitFlags has no value set assert(!(flags_empty & Enum.A) && !(flags_empty & Enum.B) && !(flags_empty & Enum.C)); // Value can be set with the | operator immutable BitFlags!Enum flags_A = flags_empty | Enum.A; // And tested with the & operator assert(flags_A & Enum.A); // Which commutes assert(Enum.A & flags_A); // BitFlags can be variadically initialized immutable BitFlags!Enum flags_AB = BitFlags!Enum(Enum.A, Enum.B); assert((flags_AB & Enum.A) && (flags_AB & Enum.B) && !(flags_AB & Enum.C)); // Use the ~ operator for subtracting flags immutable BitFlags!Enum flags_B = flags_AB & ~BitFlags!Enum(Enum.A); assert(!(flags_B & Enum.A) && (flags_B & Enum.B) && !(flags_B & Enum.C)); // You can use the EnumMembers template to set all flags immutable BitFlags!Enum flags_all = EnumMembers!Enum; // use & between BitFlags for intersection immutable BitFlags!Enum flags_BC = BitFlags!Enum(Enum.B, Enum.C); assert (flags_B == (flags_BC & flags_AB)); // All the binary operators work in their assignment version BitFlags!Enum temp = flags_empty; temp |= flags_AB; assert(temp == (flags_empty | flags_AB)); temp = flags_empty; temp |= Enum.B; assert(temp == (flags_empty | Enum.B)); temp = flags_empty; temp &= flags_AB; assert(temp == (flags_empty & flags_AB)); temp = flags_empty; temp &= Enum.A; assert(temp == (flags_empty & Enum.A)); // BitFlags with no value set evaluate to false assert(!flags_empty); // BitFlags with at least one value set evaluate to true assert(flags_A); // This can be useful to check intersection between BitFlags assert(flags_A & flags_AB); assert(flags_AB & Enum.A); // Finally, you can of course get you raw value out of flags auto value = cast(int)flags_A; assert(value == Enum.A);