Improve this page
Quickly fork, edit online, and submit a pull request for this page.
Requires a signed-in GitHub account. This works well for small changes.
If you'd like to make larger changes you may want to consider using
local clone.
Page wiki
View or edit the community-maintained wiki page associated with this page.
std.variant
This module implements a discriminated union type (a.k.a. tagged union, algebraic type). Such types are useful for type-uniform binary interfaces, interfacing with scripting languages, and comfortable exploratory programming. Synopsis:Variant a; // Must assign before use, otherwise exception ensues // Initialize with an integer; make the type int Variant b = 42; assert(b.type == typeid(int)); // Peek at the value assert(b.peek!(int) !is null && *b.peek!(int) == 42); // Automatically convert per language rules auto x = b.get!(real); // Assign any other type, including other variants a = b; a = 3.14; assert(a.type == typeid(double)); // Implicit conversions work just as with built-in types assert(a < b); // Check for convertibility assert(!a.convertsTo!(int)); // double not convertible to int // Strings and all other arrays are supported a = "now I'm a string"; assert(a == "now I'm a string"); a = new int[42]; // can also assign arrays assert(a.length == 42); a[5] = 7; assert(a[5] == 7); // Can also assign class values class Foo {} auto foo = new Foo; a = foo; assert(*a.peek!(Foo) == foo); // and full type information is preservedCredits:
Reviewed by Brad Roberts. Daniel Keep provided a detailed code review prompting the following improvements: (1) better support for arrays; (2) support for associative arrays; (3) friendlier behavior towards the garbage collector. License:
Boost License 1.0. Authors:
Andrei Alexandrescu Source:
std/variant.d
- template maxSize(T...)
- Gives the sizeof the largest type given.
- struct VariantN(size_t maxDataSize, AllowedTypesX...);
- VariantN is a back-end type seldom used directly by user
code. Two commonly-used types using VariantN as
back-end are:
- Algebraic: A closed discriminated union with a limited type universe (e.g., Algebraic!(int, double, string) only accepts these three types and rejects anything else).
- Variant: An open discriminated union allowing an unbounded set of types. The restriction is that the size of the stored type cannot be larger than the largest built-in type. This means that Variant can accommodate all primitive types and all user-defined types except for large structs.
- template Algebraic(T...)
- Algebraic data type restricted to a closed set of possible
types. It's an alias for a VariantN with an
appropriately-constructed maximum size. Algebraic is
useful when it is desirable to restrict what a discriminated type
could hold to the end of defining simpler and more efficient
manipulation.
Future additions to Algebraic will allow compile-time
checking that all possible types are handled by user code,
eliminating a large class of errors.
BUGS:
Currently, Algebraic does not allow recursive data types. They will be allowed in a future iteration of the implementation. Example:
auto v = Algebraic!(int, double, string)(5); assert(v.peek!(int)); v = 3.14; assert(v.peek!(double)); // auto x = v.peek!(long); // won't compile, type long not allowed // v = '1'; // won't compile, type char not allowed
- alias Variant = VariantN!(24u).VariantN;
- Variant is an alias for VariantN instantiated with the largest of creal, char[], and void delegate(). This ensures that Variant is large enough to hold all of D's predefined types, including all numeric types, pointers, delegates, and class references. You may want to use VariantN directly with a different maximum size either for storing larger types, or for saving memory.
- Variant[] variantArray(T...)(T args);
- Returns an array of variants constructed from args.
Example:
auto a = variantArray(1, 3.14, "Hi!"); assert(a[1] == 3.14); auto b = Variant(a); // variant array as variant assert(b[1] == 3.14);
Code that needs functionality similar to the boxArray function in the std.boxer module can achieve it like this:// old Box[] fun(...) { ... return boxArray(_arguments, _argptr); } // new Variant[] fun(T...)(T args) { ... return variantArray(args); }
This is by design. During construction the Variant needs static type information about the type being held, so as to store a pointer to function for fast retrieval. - class VariantException: object.Exception;
- Thrown in three cases:
- An uninitialized Variant is used in any way except assignment and hasValue;
- A get or coerce is attempted with an incompatible target type;
- A comparison between Variant objects of incompatible types is attempted.
- template visit(Handler...) if (Handler.length > 0)
- Applies a delegate or function to the given Algebraic depending on the held type,
ensuring that all types are handled by the visiting functions.
The delegate or function having the currently held value as parameter is called
with 's current value. Visiting handlers are passed
in the template parameter list.
It is statically ensured that all types of
variant are handled accross all handlers.
visit allows delegates and static functions to be passed
as parameters.
If a function without parameters is specified, this function is called
when variant doesn't hold a value. Exactly one parameter-less function
is allowed.
Duplicate overloads matching the same type in one of the visitors are disallowed.
Example:
Algebraic!(int, string) variant; variant = 10; assert(variant.visit!((string s) => cast(int)s.length, (int i) => i)() == 10); variant = "string"; assert(variant.visit!((int i) => return i, (string s) => cast(int)s.length)() == 6); // Error function usage Algebraic!(int, string) emptyVar; assert(variant.visit!((string s) => cast(int)s.length, (int i) => i, () => -1)() == -1);
Returns:
The return type of visit is deduced from the visiting functions and must be the same accross all overloads. Throws:
If no parameter-less, error function is specified: VariantException if variant doesn't hold a value. - template tryVisit(Handler...) if (Handler.length > 0)
- Behaves as visit but doesn't enforce that all types are handled
by the visiting functions.
If a parameter-less function is specified it is called when
either variant doesn't hold a value or holds a type
which isn't handled by the visiting functions.
Example:
Algebraic!(int, string) variant; variant = 10; auto which = -1; variant.tryVisit!((int i) { which = 0; })(); assert(which = 0); // Error function usage variant = "test"; variant.tryVisit!((int i) { which = 0; }, () { which = -100; })(); assert(which == -100);
Returns:
The return type of tryVisit is deduced from the visiting functions and must be the same accross all overloads. Throws:
If no parameter-less, error function is specified: VariantException if variant doesn't hold a value or if variant holds a value which isn't handled by the visiting functions.