Spec-Zone.ru › D

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:

  1. Algebraic: Закрытое различающееся объединение с ограниченным универсумом типов (например, Algebraic!(int, double, string) принимает только эти три типа и отклоняет любые другие).
  2. 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;

Выбрасывается в трёх случаях:

  1. Неинициализированный Variant используется любым способом, кроме присвоения и hasValue;
  2. Попытка преобразования get или coerce в несовместимый целевой тип;
  3. Попытка сравнения объектов 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API