std.variant
Этот модуль реализует тип дискриминированного объединения (также известного как меченное объединение, алгебраический тип). Такие типы полезны для типовравных двоичных интерфейсов, взаимодействия со скриптовыми языками и удобной исследовательской программирования.
Объект Variant может хранить значение любого типа с очень небольшим количеством ограничений (таких как shared типы и некопируемые типы). Установка значения происходит так же быстро, как присваивание объекту Variant. Для считывания значения соответствующего типа T, используйте метод get. Для запроса о том, содержит ли Variant значение типа T, используйте peek. Для получения точного типа, хранящегося в данный момент, вызовите type, который вернёт TypeInfo текущего значения.
В дополнение к Variant, этот модуль также определяет конструктор типа Algebraic. В отличие от Variant, Algebraic допускает только конечный набор типов, которые задаются при создании экземпляра (например, Algebraic!(int, string) может содержать только int или string).
- Авторы
- Проверено Брэдом Робертсом. Дэниел Кип предоставил подробный обзор кода, что привело к следующим улучшениям: (1) лучшая поддержка массивов; (2) поддержка ассоциативных массивов; (3) более дружественное поведение по отношению к сборщику мусора.
- Лицензия:
- Лицензия Boost 1.0.
- Авторы:
- Андрей Александреску
- Источник
- std/variant.d
- Примеры:
-
Variant a; // Must assign before use, otherwise exception ensues // Initialize with an integer; make the type int Variant b = 42; writeln(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; writeln(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"; writeln(a); // "now I'm a string" // can also assign arrays a = new int[42]; writeln(a.length); // 42 a[5] = 7; writeln(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 preserved
- template maxSize(T...)
-
Определяет
sizeofнаибольший тип.- Примеры:
-
static assert(maxSize!(int, long) == 8); static assert(maxSize!(bool, byte) == 1); struct Cat { int a, b, c; } static assert(maxSize!(bool, Cat) == 12);
- struct VariantN(size_t maxDataSize, AllowedTypesParam...);
-
Тип, редко используемый напрямую кодом пользователя. Два наиболее часто используемых типа, использующих
VariantN:-
Algebraic: Закрытое различающееся объединение с ограниченным универсумом типов (например,Algebraic!(int, double, string)принимает только эти три типа и отклоняет любые другие). -
Variant: Открытое различающееся объединение, позволяющее неограниченное множество типов. Если любой из типов вVariantбольше, чем самый большой встроенный тип, они будут автоматически упакованы. Это означает, что даже большие типы будут иметь размер только указателя внутриVariant, но это также подразумевает некоторую издержку.Variantможет вместить все примитивные типы и все пользовательские типы.
КакAlgebraicтак иVariantиспользуют интерфейсVariantN. (См. их соответствующую документацию ниже.)
VariantN- это тип различающегося объединения, параметризованный максимальным размером хранимых типов (maxDataSize) и списком разрешенных типов (AllowedTypes). Если список пуст, то любой тип размером доmaxDataSize(округлённый до ближайшего кратного для выравнивания) может быть сохранён в объектеVariantNбез упаковки (типы большего размера будут упакованы).- Примеры:
-
alias Var = VariantN!(maxSize!(int, double, string)); Var a; // Must assign before use, otherwise exception ensues // Initialize with an integer; make the type int Var b = 42; writeln(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; writeln(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"; writeln(a); // "now I'm a string"
- Примеры:
- также можно присваивать массивы
alias Var = VariantN!(maxSize!(int[])); Var a = new int[42]; writeln(a.length); // 42 a[5] = 7; writeln(a[5]); // 7
- Примеры:
- Также можно присваивать значения классов
alias Var = VariantN!(maxSize!(int*)); // classes are pointers Var a; class Foo {} auto foo = new Foo; a = foo; assert(*a.peek!(Foo) == foo); // and full type information is preserved
- alias AllowedTypes = This2Variant!(VariantN, AllowedTypesParam);
-
Список разрешённых типов. Если пустой, то любой тип разрешён.
- enum bool allowed(T);
-
Определяет, разрешён ли тип
Tдля хранения в объектеVariantN, проверяя его вTспискеAllowedTypes. - this(T)(T value);
-
Создаёт значение
VariantNс аргументом заданного типа. Статически отклоняет запрещенные типы. - this(T : VariantN!(tsize, Types), size_t tsize, Types...)(T value)
Constraints: if (!is(T : VariantN) && (Types.length > 0) && allSatisfy!(allowed, Types)); -
Разрешает присваивание из подмножества алгебраического типа
- VariantN opAssign(T)(T rhs);
-
Присваивает значение
VariantNиз аргумента заданного типа. Статически отклоняет запрещённые типы. - const pure nothrow @property bool hasValue();
-
Возвращает true тогда и только тогда, когда объект
VariantNсодержит действительное значение (инициализирован или присвоен из действительного значения).- Примеры:
-
Variant a; assert(!a.hasValue); Variant b; a = b; assert(!a.hasValue); // still no value a = 5; assert(a.hasValue);
- inout @property inout(T)* peek(T)();
-
Если объект
VariantNсодержит значение точно типаT, возвращает указатель на это значение. В противном случае возвращаетnull. В случаях, когдаTстатически запрещен,peekне будет компилироваться.- Примеры:
-
Variant a = 5; auto b = a.peek!(int); assert(b !is null); *b = 6; writeln(a); // 6
- const nothrow @property @trusted TypeInfo type();
-
Возвращает
typeidтекущего хранимого значения. - const @property bool convertsTo(T)();
-
Возвращает
trueтогда и только тогда, когда объектVariantNсодержит объект, неявно преобразуемый к типуT. Неявная преобразуемость определяется согласно ImplicitConversionTargets. - inout @property inout(T) get(T)();
inout @property auto get(uint index)()
Constraints: if (index < AllowedTypes.length); -
Возвращает значение, хранящееся в объекте
VariantN, либо указав необходимый тип, либо индекс в списке разрешенных типов. Последняя перегрузка применима только для ограниченных вариантов (например,Algebraic).- Параметры:
T Запрашиваемый тип. Текущее хранящееся значение должно неявно преобразовываться к запрошенному типу, фактически DecayStaticToDynamicArray!T. Если неявное преобразование невозможно, выбрасываетсяVariantException.index Индекс типа среди AllowedTypesParam, нумерация с нуля.
- @property T coerce(T)();
-
Возвращает значение, хранящееся в объекте
VariantN, явно преобразованное (принудительное преобразование) к запрошенному типуT. ЕслиT- строковый тип, значение форматируется как строка. Если объектVariantNявляется строкой, выполняется попытка разбора строки в типT. Если преобразование невозможно, выбрасываетсяVariantException. - string toString();
-
Форматирует сохранённое значение в виде строки.
- const bool opEquals(T)(auto ref T rhs)
Constraints: if (allowed!T || is(immutable(T) == immutable(VariantN))); -
Сравнение на равенство, используемое операторами "==" и "!=".
- int opCmp(T)(T rhs)
Constraints: if (allowed!T); -
Сравнение порядка, используемое операторами "<", "<=", ">", и ">=". В случае, если сравнение не имеет смысла между сохранённым значением и
rhs, выбрасывается исключение. - const nothrow @safe size_t toHash();
-
Вычисляет хэш сохранённого значения.
- VariantN opBinary(string op, T)(T rhs)
Constraints: if ((op == "+" || op == "-" || op == "*" || op == "/" || op == "^^" || op == "%") && is(typeof(opArithmetic!(T, op)(rhs))));
VariantN opBinary(string op, T)(T rhs)
Constraints: if ((op == "&" || op == "|" || op == "^" || op == ">>" || op == "<<" || op == ">>>") && is(typeof(opLogic!(T, op)(rhs))));
VariantN opBinaryRight(string op, T)(T lhs)
Constraints: if ((op == "+" || op == "*") && is(typeof(opArithmetic!(T, op)(lhs))));
VariantN opBinaryRight(string op, T)(T lhs)
Constraints: if ((op == "&" || op == "|" || op == "^") && is(typeof(opLogic!(T, op)(lhs))));
VariantN opBinary(string op, T)(T rhs)
Constraints: if (op == "~");
VariantN opOpAssign(string op, T)(T rhs); -
Арифметические операции между объектами
VariantNи числовыми значениями. Все арифметические операции возвращают объектVariantN, тип которого зависит от типов участвующих значений. Правила преобразования имитируют встроенные правила D для арифметических преобразований. - inout inout(Variant) opIndex(K)(K i);
Variant opIndexAssign(T, N)(T value, N i);
Variant opIndexOpAssign(string op, T, N)(T value, N i); -
Операции с массивами и ассоциативными массивами. Если
VariantNсодержит (ассоциативный) массив, к нему можно обратиться по индексу. В противном случае выбрасывается исключение.- Примеры:
-
Variant a = new int[10]; a[5] = 42; writeln(a[5]); // 42 a[5] += 8; writeln(a[5]); // 50 int[int] hash = [ 42:24 ]; a = hash; writeln(a[42]); // 24 a[42] /= 2; writeln(a[42]); // 12
- @property size_t length();
-
Если
VariantNсодержит (ассоциативный) массив, возвращает длину этого массива. В противном случае выбрасывается исключение. - int opApply(Delegate)(scope Delegate dg)
Constraints: if (is(Delegate == delegate)); -
Если
VariantNсодержит массив, применяетdgк каждому элементу массива по очереди. В противном случае выбрасывается исключение.
-
- template Algebraic(T...)
-
Тип данных алгебраического типа, ограниченный замкнутым набором возможных типов. Это псевдоним для
VariantNс соответствующим максимальным размером.Algebraicполезно, когда требуется ограничить то, что может содержать различающийся тип, для определения более простых и эффективных способов работы.- Примеры:
-
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
- Примеры:
-
Самоссылочные типы
Полезное и популярное применение структур алгебраических данных — для определения самоссылочных структур данных, т. е. структур, которые содержат ссылки на значения своего собственного типа. Это достигается вAlgebraicиспользованиемThisв качестве заглушки всякий раз, когда требуется ссылка на определяемый тип. ЭкземплярAlgebraicвыполнит альфа-переименование своих составляющих типов, заменивThisна самоссылочный тип. Структура типа, включающегоThis, может быть произвольно сложной.import std.typecons : Tuple, tuple; // A tree is either a leaf or a branch of two other trees alias Tree(Leaf) = Algebraic!(Leaf, Tuple!(This*, This*)); Tree!int tree = tuple(new Tree!int(42), new Tree!int(43)); Tree!int* right = tree.get!1[1]; writeln(*right); // 43 // An object is a double, a string, or a hash of objects alias Obj = Algebraic!(double, string, This[string]); Obj obj = "hello"; writeln(obj.get!1); // "hello" obj = 42.0; writeln(obj.get!0); // 42 obj = ["customer": Obj("John"), "paid": Obj(23.95)]; writeln(obj.get!2["customer"]); // "John"
- alias Variant = VariantN!32LU.VariantN;
-
Псевдоним для
VariantN, созданного с наибольшим размеромcreal,char[], иvoid delegate(). Это гарантирует, чтоVariantдостаточно велик, чтобы содержать все предопределённые типы D без упаковывания, включая все числовые типы, указатели, делегаты и ссылки на классы. Вы можете использоватьVariantNнапрямую с другим максимальным размером для хранения более крупных типов без упаковки или для экономии памяти.- Примеры:
-
Variant a; // Must assign before use, otherwise exception ensues // Initialize with an integer; make the type int Variant b = 42; writeln(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; writeln(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"; writeln(a); // "now I'm a string"
- Примеры:
- также можно присваивать массивы
Variant a = new int[42]; writeln(a.length); // 42 a[5] = 7; writeln(a[5]); // 7
- Примеры:
- Также можно присваивать значения классов
Variant a; class Foo {} auto foo = new Foo; a = foo; assert(*a.peek!(Foo) == foo); // and full type information is preserved
- Variant[] variantArray(T...)(T args);
-
Возвращает массив вариантов, построенных из
args.Это сделано по умолчанию. Во время создания
Variantтребует статической информации о типе хранимого значения для хранения указателя на функцию для быстрого извлечения.- Примеры:
-
auto a = variantArray(1, 3.14, "Hi!"); writeln(a[1]); // 3.14 auto b = Variant(a); // variant array as variant writeln(b[1]); // 3.14
- class VariantException: object.Exception;
-
Выбрасывается в трёх случаях:
- Неинициализированный
Variantиспользуется любым способом, кроме присвоения иhasValue; - Попытка преобразования
getилиcoerceв несовместимый целевой тип; - Попытка сравнения объектов
Variantнесовместимых типов.
- Примеры:
-
import std.exception : assertThrown; Variant v; // uninitialized use assertThrown!VariantException(v + 1); assertThrown!VariantException(v.length); // .get with an incompatible target type assertThrown!VariantException(Variant("a").get!int); // comparison between incompatible types assertThrown!VariantException(Variant(3) < Variant("a"));
- TypeInfo source;
-
Исходный тип при преобразовании или сравнении
- TypeInfo target;
-
Целевой тип при преобразовании или сравнении
- Неинициализированный
- template visit(Handlers...) if (Handlers.length > 0)
-
Применяет делегат или функцию к заданному
Algebraicв зависимости от хранимого типа, гарантируя, что все типы обрабатываются функциями обработки.Делегат или функция с текущим значением как параметром вызывается с текущим значением
variant. Обработчики обработки передаются в списке параметров шаблона. Статически гарантируется, что все типы, хранимые вvariantобрабатываются во всех обработчиках.visitпозволяет передавать делегаты и статические функции в качестве параметров.
Если указана функция с нетипизированным параметром, эта функция вызывается, когда переменная содержит тип, не соответствующий никакой другой функции. Это можно использовать для применения одной и той же функции к нескольким возможным типам. Разрешена ровно одна обобщённая функция.
Если указана функция без параметров, эта функция вызывается, когдаvariantне содержит значения. Разрешена ровно одна функция без параметров.
Повторные перегрузки, соответствующие одному и тому же типу в одном из посетителей, запрещены.- Возвращает:
- Тип возвращаемого значения visit определяется по функциям обработки и должен быть одинаковым во всех перегрузках.
- Исключения:
-
VariantException, еслиvariantне содержит значения и не указана функция по умолчанию без параметров.
- Примеры:
-
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) => i, (string s) => cast(int) s.length)() == 6); // Error function usage Algebraic!(int, string) emptyVar; auto rslt = emptyVar.visit!((string s) => cast(int) s.length, (int i) => i, () => -1)(); writeln(rslt); // -1 // Generic function usage Algebraic!(int, float, real) number = 2; writeln(number.visit!(x => x += 1)); // 3 // Generic function for int/float with separate behavior for string Algebraic!(int, float, string) something = 2; assert(something.visit!((string s) => s.length, x => x) == 2); // generic something = "asdf"; assert(something.visit!((string s) => s.length, x => x) == 4); // string // Generic handler and empty handler Algebraic!(int, float, real) empty2; writeln(empty2.visit!(x => x + 1, () => -1)); // -1
- auto visit(VariantType)(VariantType variant)
Constraints: if (isAlgebraic!VariantType);
- template tryVisit(Handlers...) if (Handlers.length > 0)
-
Поведение как у
visit, но не требует обработки всех типов функциями обработки.Если указана функция без параметров, она вызывается, когда
variantне содержит значения или содержит тип, который не обрабатывается функциями обработки.- Возвращает:
- Тип возвращаемого значения tryVisit определяется по функциям обработки и должен быть одинаковым во всех перегрузках.
- Исключения:
-
VariantException, еслиvariantне содержит значения илиvariantсодержит значение, которое не обрабатывается функциями обработки, когда не указана функция по умолчанию без параметров.
- Примеры:
-
Algebraic!(int, string) variant; variant = 10; auto which = -1; variant.tryVisit!((int i) { which = 0; })(); writeln(which); // 0 // Error function usage variant = "test"; variant.tryVisit!((int i) { which = 0; }, () { which = -100; })(); writeln(which); // -100
- auto tryVisit(VariantType)(VariantType variant)
Constraints: if (isAlgebraic!VariantType);
© 1999–2021 The D Language Foundation
Licensed under the Boost License 1.0.
https://dlang.org/phobos/std_variant.html