diff --git a/autoddoc.cfg b/autoddoc.cfg index 78410f0..15aea1f 100644 --- a/autoddoc.cfg +++ b/autoddoc.cfg @@ -29,7 +29,7 @@ links = ../index.html Documentation home # Source files or patterns to ignore. Supports regexp syntax. # E.g; To ignore main.d and all source files in the test/ directory, # you would use: "main.d test/*" -ignore = test/*, examples/*, docsrc/*, autoddoc/*, yaml.d, unittest.d, cdc.d, dyaml/composer.d, dyaml/event.d, dyaml/parser.d, dyaml/reader.d, dyaml/scanner.d, dyaml/token.d, dyaml/util.d, dyaml/anchor.d, dyaml/emitter.d, dyaml/flags.d, dyaml/serializer.d, dyaml/sharedobject.d, dyaml/tag.d, dyaml/tagdirectives.d, dyaml/queue.d, dyaml/escapes.d, dyaml/fastcharsearch.d, dyaml/std/variant.d +ignore = test/*, examples/*, docsrc/*, autoddoc/*, yaml.d, unittest.d, cdc.d, dyaml/composer.d, dyaml/event.d, dyaml/parser.d, dyaml/reader.d, dyaml/scanner.d, dyaml/token.d, dyaml/util.d, dyaml/anchor.d, dyaml/emitter.d, dyaml/flags.d, dyaml/serializer.d, dyaml/sharedobject.d, dyaml/tag.d, dyaml/tagdirectives.d, dyaml/queue.d, dyaml/escapes.d, dyaml/fastcharsearch.d [DDOC] # Command to use to generate the documentation. diff --git a/doc/doctrees/environment.pickle b/doc/doctrees/environment.pickle index bca770a..6555f4a 100644 Binary files a/doc/doctrees/environment.pickle and b/doc/doctrees/environment.pickle differ diff --git a/doc/html/api/dyaml.node.html b/doc/html/api/dyaml.node.html index 089e465..1b55f6b 100644 --- a/doc/html/api/dyaml.node.html +++ b/doc/html/api/dyaml.node.html @@ -52,6 +52,12 @@

Null YAML type. Used in nodes with null values.

+
const string toString(); +
+

Used for string conversion.

+ +
+
struct Node;
@@ -80,7 +86,7 @@

Construct a Pair from two values. Will be converted to Nodes if needed.

-
bool equals(ref Pair rhs); +
const bool opEquals(ref const Pair rhs);

Equality test with another Pair.

@@ -263,7 +269,7 @@

Return tag of the node.

-
bool opEquals(T)(ref T rhs); +
const bool opEquals(T)(ref const T rhs);

Equality test.

@@ -337,7 +343,7 @@ the value is out of range of requested type.
-
@property size_t length(); +
const @property size_t length();

If this is a collection, return its length.

diff --git a/doc/html/articles/spec_differences.html b/doc/html/articles/spec_differences.html index 5d4970b..82539ed 100644 --- a/doc/html/articles/spec_differences.html +++ b/doc/html/articles/spec_differences.html @@ -138,7 +138,7 @@ struct appears in Phobos.

diff --git a/doc/html/index.html b/doc/html/index.html index f721c62..f2f52f4 100644 --- a/doc/html/index.html +++ b/doc/html/index.html @@ -104,7 +104,7 @@ diff --git a/doc/html/search.html b/doc/html/search.html index 230bccf..d840f62 100644 --- a/doc/html/search.html +++ b/doc/html/search.html @@ -87,7 +87,7 @@ diff --git a/doc/html/tutorials/custom_types.html b/doc/html/tutorials/custom_types.html index bd8b27d..15c06d0 100644 --- a/doc/html/tutorials/custom_types.html +++ b/doc/html/tutorials/custom_types.html @@ -368,7 +368,7 @@ directory of the D:YAML package.

diff --git a/doc/html/tutorials/getting_started.html b/doc/html/tutorials/getting_started.html index 79c2943..ed2d39c 100644 --- a/doc/html/tutorials/getting_started.html +++ b/doc/html/tutorials/getting_started.html @@ -237,7 +237,7 @@ example in the example/getting_st diff --git a/doc/html/tutorials/yaml_syntax.html b/doc/html/tutorials/yaml_syntax.html index ea682f5..1005f1b 100644 --- a/doc/html/tutorials/yaml_syntax.html +++ b/doc/html/tutorials/yaml_syntax.html @@ -330,7 +330,7 @@ Some of these might change in the future (especially !!map and !!set).

diff --git a/dyaml/node.d b/dyaml/node.d index 2875a29..9851ed0 100644 --- a/dyaml/node.d +++ b/dyaml/node.d @@ -16,11 +16,12 @@ import std.conv; import std.datetime; import std.exception; import std.math; +import std.range; import std.stdio; import std.string; import std.traits; import std.typecons; -import dyaml.std.variant; +import std.variant; import dyaml.event; import dyaml.exception; @@ -73,7 +74,7 @@ package abstract class YAMLObject protected: ///Test for equality with another YAMLObject. - bool equals(YAMLObject rhs) {assert(false);} + bool equals(const YAMLObject rhs) const {assert(false);} } //Stores a user defined YAML data type. @@ -102,10 +103,10 @@ package class YAMLContainer(T) if (!Node.Value.allowed!T): YAMLObject protected: //Test for equality with another YAMLObject. - override bool equals(YAMLObject rhs) + override bool equals(const YAMLObject rhs) const { if(rhs.type !is typeid(T)){return false;} - return cast(T)value_ == (cast(YAMLContainer)rhs).value_; + return cast(T)value_ == (cast(const YAMLContainer)rhs).value_; } private: @@ -134,7 +135,6 @@ struct Node Node value; public: - @disable bool opEquals(ref Pair); @disable int opCmp(ref Pair); ///Construct a Pair from two values. Will be converted to Nodes if needed. @@ -147,10 +147,10 @@ struct Node } ///Equality test with another Pair. - bool equals(ref Pair rhs) + bool opEquals(const ref Pair rhs) const { - return equals_!true(rhs); - } + return equals!true(rhs); + } private: /* @@ -159,10 +159,10 @@ struct Node * useTag determines whether or not we consider node tags * in the test. */ - bool equals_(bool useTag)(ref Pair rhs) + bool equals(bool useTag)(ref const(Pair) rhs) const { - return key.equals!(Node, useTag)(rhs.key) && - value.equals!(Node, useTag)(rhs.value); + return key.equals!(useTag)(rhs.key) && + value.equals!(useTag)(rhs.value); } } @@ -188,8 +188,6 @@ struct Node public: @disable int opCmp(ref Node); - @disable bool opEquals(T)(ref T) const if(is(T == const)); - /** * Construct a Node from a value. * @@ -468,9 +466,9 @@ struct Node * * Returns: true if equal, false otherwise. */ - bool opEquals(T)(ref T rhs) if(!is(T == const)) + bool opEquals(T)(const ref T rhs) const { - return equals!(T, true)(rhs); + return equals!true(rhs); } ///Shortcut for get(). @@ -517,37 +515,23 @@ struct Node */ @property T get(T)() if(!is(T == const)) { - if(isType!T) - { - return value_.get!T; - } + if(isType!T){return value_.get!T;} - static if(!Value.allowed!T) + ///Must go before others, as even string/int/etc could be stored in a YAMLObject. + static if(!Value.allowed!T) if(isUserType) { - ///Must go before others, as even string/int/etc could be stored in a YAMLObject. - if(isUserType) + auto object = as!YAMLObject; + if(object.type is typeid(T)) { - auto object = as!YAMLObject; - if(object.type is typeid(T)) - { - return (cast(YAMLContainer!T)object).value_; - } + return (cast(YAMLContainer!T)object).value_; } + throw new Error("Node has unexpected type: " ~ object.type.toString ~ + ". Expected: " ~ typeid(T).toString, startMark_); } //If we're getting from a mapping and we're not getting Node.Pair[], //we're getting the default value. - if(isMapping) - { - return this["="].as!T; - } - - void throwUnexpectedType() - { - //Can't get the value. - throw new Error("Node has unexpected type: " ~ type.toString ~ - ". Expected: " ~ typeid(T).toString, startMark_); - } + if(isMapping){return this["="].as!T;} static if(isSomeString!T) { @@ -561,46 +545,79 @@ struct Node throw new Error("Unable to convert node value to string", startMark_); } } - else static if(isFloatingPoint!T) + else { - ///Can convert int to float. - if(isInt()) + static if(isFloatingPoint!T) { - return to!T(value_.get!long); + ///Can convert int to float. + if(isInt()) {return to!T(value_.get!(const long));} + else if(isFloat()){return to!T(value_.get!(const real));} } - else if(isFloat()) - { - return to!T(value_.get!real); - } - else - { - throwUnexpectedType(); - assert(false); - } - } - else static if(isIntegral!T) - { - if(isInt()) + else static if(isIntegral!T) if(isInt()) { - const temp = value_.get!long; - if(temp < T.min || temp > T.max) - { - throw new Error("Integer value out of range of type " ~ - typeid(T).toString ~ "Value: " ~ to!string(temp), - startMark_); - } + const temp = value_.get!(const long); + enforce(temp >= T.min && temp <= T.max, + new Error("Integer value of type " ~ typeid(T).toString ~ + " out of range. Value: " ~ to!string(temp), startMark_)); return to!T(temp); } - else + throw new Error("Node has unexpected type: " ~ type.toString ~ + ". Expected: " ~ typeid(T).toString, startMark_); + } + } + + //Const version of get. + @property T get(T)() const if(is(T == const)) + { + if(isType!T){return value_.get!T;} + + ///Must go before others, as even string/int/etc could be stored in a YAMLObject. + static if(!Value.allowed!T) if(isUserType) + { + auto object = as!(const YAMLObject); + if(object.type is typeid(T)) { - throwUnexpectedType(); - assert(false); + return (cast(const YAMLContainer!T)object).value_; + } + throw new Error("Node has unexpected type: " ~ object.type.toString ~ + ". Expected: " ~ typeid(T).toString, startMark_); + } + + //If we're getting from a mapping and we're not getting Node.Pair[], + //we're getting the default value. + if(isMapping){return indexConst("=").as!T;} + + static if(isSomeString!T) + { + //Try to convert to string. + try + { + //NOTE: We are casting away const here + return (cast(Value)value_).coerce!T(); + } + catch(VariantException e) + { + throw new Error("Unable to convert node value to string", startMark_); } } - else + else { - throwUnexpectedType(); - assert(false); + static if(isFloatingPoint!T) + { + ///Can convert int to float. + if(isInt()) {return to!T(value_.get!(const long));} + else if(isFloat()){return to!T(value_.get!(const real));} + } + else static if(isIntegral!T) if(isInt()) + { + const temp = value_.get!(const long); + enforce(temp >= T.min && temp <= T.max, + new Error("Integer value of type " ~ typeid(T).toString ~ + " out of range. Value: " ~ to!string(temp), startMark_)); + return to!T(temp); + } + throw new Error("Node has unexpected type: " ~ type.toString ~ + ". Expected: " ~ typeid(T).toString, startMark_); } } @@ -643,13 +660,19 @@ struct Node if(isSequence) { checkSequenceIndex(index); - static if(isIntegral!T){return value_.get!(Node[])[index];} + static if(isIntegral!T) + { + return cast(Node)value_.get!(Node[])[index]; + } assert(false); } else if(isMapping) { auto idx = findPair(index); - if(idx >= 0){return as!(Pair[])[idx].value;} + if(idx >= 0) + { + return cast(Node)value_.get!(Pair[])[idx].value; + } string msg = "Mapping index not found" ~ (isSomeString!T ? ": " ~ to!string(index) : ""); throw new Error(msg, startMark_); @@ -1141,7 +1164,7 @@ struct Node * * useTag determines whether or not to consider tags in node-node comparisons. */ - bool equals(T, bool useTag)(ref T rhs) + bool equals(bool useTag, T)(ref T rhs) const { static if(is(Unqual!T == Node)) { @@ -1155,55 +1178,50 @@ struct Node { return false; } - if(isSequence) + + static bool compareCollection(T)(const ref Node lhs, const ref Node rhs) { - auto seq1 = as!(Node[]); - auto seq2 = rhs.as!(Node[]); - if(seq1 is seq2){return true;} - if(seq1.length != seq2.length){return false;} - foreach(node; 0 .. seq1.length) + const c1 = lhs.value_.get!(const T); + const c2 = rhs.value_.get!(const T); + if(c1 is c2){return true;} + if(c1.length != c2.length){return false;} + foreach(ref i1, ref i2; lockstep(c1, c2)) { - if(!seq1[node].equals!(T, useTag)(seq2[node])){return false;} + if(!i1.equals!useTag(i2)){return false;} } return true; } - if(isMapping) + + static bool compare(T)(const ref Node lhs, const ref Node rhs) { - auto map1 = as!(Node.Pair[]); - auto map2 = rhs.as!(Node.Pair[]); - if(map1 is map2){return true;} - if(map1.length != map2.length){return false;} - foreach(pair; 0 .. map1.length) - { - if(!map1[pair].equals_!useTag(map2[pair])){return false;} - } - return true; + return lhs.value_.get!(const T) == rhs.value_.get!(const T); } - if(isScalar) + + if(isSequence) {return compareCollection!(Node[])(this, rhs);} + else if(isMapping){return compareCollection!(Pair[])(this, rhs);} + else if(isString) {return compare!string(this, rhs);} + else if(isInt) {return compare!long(this, rhs);} + else if(isBool) {return compare!bool(this, rhs);} + else if(isBinary) {return compare!(ubyte[])(this, rhs);} + else if(isNull) {return true;} + else if(isFloat) { - if(isUserType) - { - if(!rhs.isUserType){return false;} - return get!YAMLObject.equals(rhs.as!YAMLObject); - } - if(isFloat) - { - if(!rhs.isFloat){return false;} - real r1 = get!real; - real r2 = rhs.get!real; - bool equals(real r1, real r2) - { - return r1 <= r2 + real.epsilon && r1 >= r2 - real.epsilon; - } - if(isNaN(r1)){return isNaN(r2);} - return equals(r1, r2); - } - else - { - return value_ == rhs.value_; - } + const r1 = value_.get!(const real); + const r2 = rhs.value_.get!(const real); + return isNaN(r1) ? isNaN(r2) + : (r1 <= r2 + real.epsilon && r1 >= r2 - real.epsilon); } - assert(false, "Unknown kind of node"); + else if(isTime) + { + const t1 = value_.get!(const SysTime); + const t2 = rhs.value_.get!(const SysTime); + return t1 == t2; + } + else if(isUserType) + { + return value_.get!(const YAMLObject).equals(rhs.value_.get!(const YAMLObject)); + } + assert(false, "Unknown kind of node (equality comparison) : " ~ type.toString); } else { @@ -1265,15 +1283,27 @@ struct Node @property bool isType(T)() const {return value_.type is typeid(Unqual!T);} private: - //Is the value an integer of some kind? + //Is the value a bool? + alias isType!bool isBool; + + //Is the value a raw binary buffer? + alias isType!(ubyte[]) isBinary; + + //Is the value an integer? alias isType!long isInt; - //Is the value a floating point number of some kind? + //Is the value a floating point number? alias isType!real isFloat; - //Is the value a string of some kind? + //Is the value a string? alias isType!string isString; + //Is the value a timestamp? + alias isType!SysTime isTime; + + //Is the value a null value? + alias isType!YAMLNull isNull; + //Does given node have the same type as this node? bool hasEqualType(const ref Node node) const { @@ -1293,11 +1323,11 @@ struct Node } //Get index of pair with key (or value, if value is true) matching index. - long findPair(T, bool value = false)(const ref T index) + long findPair(T, bool value = false)(const ref T index) const { - auto pairs = as!(Node.Pair[])(); - Node* node; - foreach(idx, ref pair; pairs) + const pairs = value_.get!(const Pair[])(); + const(Node)* node; + foreach(idx, ref const(Pair) pair; pairs) { static if(value){node = &pair.value;} else{node = &pair.key;} @@ -1317,7 +1347,7 @@ struct Node } else { - try if(node.as!T == index){return idx;} + try if(node.as!(const T) == index){return idx;} catch(NodeException e) { continue; @@ -1341,6 +1371,32 @@ struct Node startMark_)); } } + + //Const version of opIndex. + ref const(Node) indexConst(T)(T index) const + { + if(isSequence) + { + checkSequenceIndex(index); + static if(isIntegral!T) + { + return value_.get!(const Node[])[index]; + } + assert(false); + } + else if(isMapping) + { + auto idx = findPair(index); + if(idx >= 0) + { + return value_.get!(const Pair[])[idx].value; + } + + string msg = "Mapping index not found" ~ (isSomeString!T ? ": " ~ to!string(index) : ""); + throw new Error(msg, startMark_); + } + throw new Error("Trying to index node that does not support indexing", startMark_); + } } package: diff --git a/dyaml/std/variant.d b/dyaml/std/variant.d deleted file mode 100644 index 32616b4..0000000 --- a/dyaml/std/variant.d +++ /dev/null @@ -1,1493 +0,0 @@ -// Written in the D programming language. - -/** - * This module implements a - * $(LINK2 http://erdani.org/publications/cuj-04-2002.html,discriminated union) - * type (a.k.a. - * $(LINK2 http://en.wikipedia.org/wiki/Tagged_union,tagged union), - * $(LINK2 http://en.wikipedia.org/wiki/Algebraic_data_type,algebraic type)). - * Such types are useful - * for type-uniform binary interfaces, interfacing with scripting - * languages, and comfortable exploratory programming. - * - * Macros: - * WIKI = Phobos/StdVariant - * - * 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 preserved - * ---- - * - * Credits: - * - * 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. - * - * Copyright: Copyright Andrei Alexandrescu 2007 - 2009. - * License: Boost License 1.0. - * Authors: $(WEB erdani.org, Andrei Alexandrescu) - * Source: $(PHOBOSSRC std/_variant.d) - */ -/* Copyright Andrei Alexandrescu 2007 - 2009. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ -module dyaml.std.variant; - -import std.traits, std.c.string, std.typetuple, std.conv, std.exception; -// version(unittest) -// { - import std.exception, std.stdio; -//} - -private template maxSize(T...) -{ - static if (T.length == 1) - { - enum size_t maxSize = T[0].sizeof; - } - else - { - enum size_t maxSize = T[0].sizeof >= maxSize!(T[1 .. $]) - ? T[0].sizeof : maxSize!(T[1 .. $]); - } -} - -struct This; - -template AssociativeArray(T) -{ - enum bool valid = false; - alias void Key; - alias void Value; -} - -template AssociativeArray(T : V[K], K, V) -{ - enum bool valid = true; - alias K Key; - alias V Value; -} - -template This2Variant(V, T...) -{ - static if (T.length == 0) alias TypeTuple!() This2Variant; - else static if (is(AssociativeArray!(T[0]).Key == This)) - { - static if (is(AssociativeArray!(T[0]).Value == This)) - alias TypeTuple!(V[V], - This2Variant!(V, T[1 .. $])) This2Variant; - else - alias TypeTuple!(AssociativeArray!(T[0]).Value[V], - This2Variant!(V, T[1 .. $])) This2Variant; - } - else static if (is(AssociativeArray!(T[0]).Value == This)) - alias TypeTuple!(V[AssociativeArray!(T[0]).Key], - This2Variant!(V, T[1 .. $])) This2Variant; - else static if (is(T[0] == This[])) - alias TypeTuple!(V[], This2Variant!(V, T[1 .. $])) This2Variant; - else static if (is(T[0] == This*)) - alias TypeTuple!(V*, This2Variant!(V, T[1 .. $])) This2Variant; - else - alias TypeTuple!(T[0], This2Variant!(V, T[1 .. $])) This2Variant; -} - -/** - * $(D_PARAM VariantN) is a back-end type seldom used directly by user - * code. Two commonly-used types using $(D_PARAM VariantN) as - * back-end are: - * - * $(OL $(LI $(B Algebraic): A closed discriminated union with a - * limited type universe (e.g., $(D_PARAM Algebraic!(int, double, - * string)) only accepts these three types and rejects anything - * else).) $(LI $(B 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 $(D_PARAM Variant) can accommodate all primitive types - * and all user-defined types except for large $(D_PARAM struct)s.) ) - * - * Both $(D_PARAM Algebraic) and $(D_PARAM Variant) share $(D_PARAM - * VariantN)'s interface. (See their respective documentations below.) - * - * $(D_PARAM VariantN) is a discriminated union type parameterized - * with the largest size of the types stored ($(D_PARAM maxDataSize)) - * and with the list of allowed types ($(D_PARAM AllowedTypes)). If - * the list is empty, then any type up of size up to $(D_PARAM - * maxDataSize) (rounded up for alignment) can be stored in a - * $(D_PARAM VariantN) object. - * - */ - -struct VariantN(size_t maxDataSize, AllowedTypesX...) -{ - alias This2Variant!(VariantN, AllowedTypesX) AllowedTypes; - -private: - // Compute the largest practical size from maxDataSize - struct SizeChecker - { - int function() fptr; - ubyte[maxDataSize] data; - } - enum size = SizeChecker.sizeof - (int function()).sizeof; - static assert(size >= (void*).sizeof); - - /** Tells whether a type $(D_PARAM T) is statically allowed for - * storage inside a $(D_PARAM VariantN) object by looking - * $(D_PARAM T) up in $(D_PARAM AllowedTypes). If $(D_PARAM - * AllowedTypes) is empty, all types of size up to $(D_PARAM - * maxSize) are allowed. - */ - public template allowed(T) - { - enum bool allowed - = is(T == VariantN) - || - //T.sizeof <= size && - (AllowedTypes.length == 0 || staticIndexOf!(T, AllowedTypes) >= 0); - } - - // Each internal operation is encoded with an identifier. See - // the "handler" function below. - enum OpID { getTypeInfo, get, compare, testConversion, toString, - index, indexAssign, catAssign, copyOut, length, - apply } - - // state - sizediff_t function(OpID selector, ubyte[size]* store, void* data) fptr - = &handler!(void); - union - { - ubyte[size] store; - // conservatively mark the region as pointers - static if (size >= (void*).sizeof) - void* p[size / (void*).sizeof]; - } - - // internals - // Handler for an uninitialized value - static sizediff_t handler(A : void)(OpID selector, ubyte[size]*, void* parm) - { - switch (selector) - { - case OpID.getTypeInfo: - *cast(TypeInfo *) parm = typeid(A); - break; - case OpID.copyOut: - auto target = cast(VariantN *) parm; - target.fptr = &handler!(A); - // no need to copy the data (it's garbage) - break; - case OpID.compare: - auto rhs = cast(VariantN *) parm; - return rhs.peek!(A) - ? 0 // all uninitialized are equal - : int.min; // uninitialized variant is not comparable otherwise - case OpID.toString: - string * target = cast(string*) parm; - *target = ""; - break; - case OpID.get: - case OpID.testConversion: - case OpID.index: - case OpID.indexAssign: - case OpID.catAssign: - case OpID.length: - throw new VariantException( - "Attempt to use an uninitialized VariantN"); - default: assert(false, "Invalid OpID"); - } - return 0; - } - - // Handler for all of a type's operations - static sizediff_t handler(A)(OpID selector, ubyte[size]* pStore, void* parm) - { - static A* getPtr(void* untyped) - { - if (untyped) - { - static if (A.sizeof <= size) - return cast(A*) untyped; - else - return *cast(A**) untyped; - } - return null; - } - auto zis = getPtr(pStore); - // Input: TypeInfo object - // Output: target points to a copy of *me, if me was not null - // Returns: true iff the A can be converted to the type represented - // by the incoming TypeInfo - static bool tryPutting(A* src, TypeInfo targetType, void* target) - { - alias TypeTuple!(A, ImplicitConversionTargets!A) AllTypes; - foreach (T ; AllTypes) - { - if (targetType != typeid(T) && - targetType != typeid(const(T))) - { - static if (isImplicitlyConvertible!(T, immutable(T))) - { - if (targetType != typeid(immutable(T))) - { - continue; - } - } - else - { - continue; - } - } - // found!!! - static if (is(typeof(*cast(T*) target = *src))) - { - auto zat = cast(T*) target; - if (src) - { - assert(target, "target must be non-null"); - *zat = *src; - } - } - else - { - // type is not assignable - if (src) assert(false, A.stringof); - } - return true; - } - return false; - } - - switch (selector) - { - case OpID.getTypeInfo: - *cast(TypeInfo *) parm = typeid(A); - break; - case OpID.copyOut: - auto target = cast(VariantN *) parm; - assert(target); - tryPutting(zis, typeid(A), cast(void*) getPtr(&target.store)) - || assert(false); - target.fptr = &handler!(A); - break; - case OpID.get: - return !tryPutting(zis, *cast(TypeInfo*) parm, parm); - case OpID.testConversion: - return !tryPutting(null, *cast(TypeInfo*) parm, null); - case OpID.compare: - auto rhsP = cast(VariantN *) parm; - auto rhsType = rhsP.type; - // Are we the same? - if (rhsType == typeid(A)) - { - // cool! Same type! - auto rhsPA = getPtr(&rhsP.store); - static if (is(typeof(A.init == A.init))) - { - if (*rhsPA == *zis) - { - return 0; - } - static if (is(typeof(A.init < A.init))) - { - return *zis < *rhsPA ? -1 : 1; - } - } - else - { - // type doesn't support ordering comparisons - return int.min; - } - } else if (rhsType == typeid(void)) - { - // No support for ordering comparisons with - // uninitialized vars - return int.min; - } - VariantN temp; - // Do I convert to rhs? - if (tryPutting(zis, rhsType, &temp.store)) - { - // cool, I do; temp's store contains my data in rhs's type! - // also fix up its fptr - temp.fptr = rhsP.fptr; - // now lhsWithRhsType is a full-blown VariantN of rhs's type - return temp.opCmp(*rhsP); - } - // Does rhs convert to zis? - *cast(TypeInfo*) &temp.store = typeid(A); - if (rhsP.fptr(OpID.get, &rhsP.store, &temp.store) == 0) - { - // cool! Now temp has rhs in my type! - auto rhsPA = getPtr(&temp.store); - static if (is(typeof(A.init == A.init))) - { - if (*rhsPA == *zis) - { - return 0; - } - static if (is(typeof(A.init < A.init))) - { - return *zis < *rhsPA ? -1 : 1; - } - } - else - { - // type doesn't support ordering comparisons - return int.min; - } - } - return int.min; // dunno - case OpID.toString: - auto target = cast(string*) parm; - static if (is(typeof(to!(string)(*zis)))) - { - *target = to!(string)(*zis); - break; - } - // TODO: The following test evaluates to true for shared objects. - // Use __traits for now until this is sorted out. - // else static if (is(typeof((*zis).toString))) - else static if (__traits(compiles, {(*zis).toString;})) - { - *target = (*zis).toString; - break; - } - else - { - throw new VariantException(typeid(A), typeid(string)); - } - - case OpID.index: - // Added allowed!(...) prompted by a bug report by Chris - // Nicholson-Sauls. - static if (isStaticArray!(A) && allowed!(typeof(A.init))) - { - enforce(0, "Not implemented"); - } - static if (isDynamicArray!(A) && allowed!(typeof(A.init[0]))) - { - // array type; input and output are the same VariantN - auto result = cast(VariantN*) parm; - size_t index = result.convertsTo!(int) - ? result.get!(int) : result.get!(size_t); - *result = (*zis)[index]; - break; - } - else static if (isAssociativeArray!(A) - && allowed!(typeof(A.init.values[0]))) - { - auto result = cast(VariantN*) parm; - *result = (*zis)[result.get!(typeof(A.keys[0]))]; - break; - } - else - { - throw new VariantException(typeid(A), typeid(void[])); - } - - case OpID.indexAssign: - static if (isArray!(A) && is(typeof((*zis)[0] = (*zis)[0]))) - { - // array type; result comes first, index comes second - auto args = cast(VariantN*) parm; - size_t index = args[1].convertsTo!(int) - ? args[1].get!(int) : args[1].get!(size_t); - (*zis)[index] = args[0].get!(typeof((*zis)[0])); - break; - } - else static if (isAssociativeArray!(A)) - { - auto args = cast(VariantN*) parm; - (*zis)[args[1].get!(typeof(A.keys[0]))] - = args[0].get!(typeof(A.values[0])); - break; - } - else - { - throw new VariantException(typeid(A), typeid(void[])); - } - - case OpID.catAssign: - static if (is(typeof((*zis)[0])) && is(typeof((*zis) ~= *zis))) - { - // array type; parm is the element to append - auto arg = cast(VariantN*) parm; - alias typeof((*zis)[0]) E; - if (arg[0].convertsTo!(E)) - { - // append one element to the array - (*zis) ~= [ arg[0].get!(E) ]; - } - else - { - // append a whole array to the array - (*zis) ~= arg[0].get!(A); - } - break; - } - else - { - throw new VariantException(typeid(A), typeid(void[])); - } - - case OpID.length: - static if (is(typeof(zis.length))) - { - return zis.length; - } - else - { - throw new VariantException(typeid(A), typeid(void[])); - } - - case OpID.apply: - assert(0); - - default: assert(false); - } - return 0; - } - -public: - /** Constructs a $(D_PARAM VariantN) value given an argument of a - * generic type. Statically rejects disallowed types. - */ - - this(T)(T value) - { - static assert(allowed!(T), "Cannot store a " ~ T.stringof - ~ " in a " ~ VariantN.stringof); - opAssign(value); - } - - /** Assigns a $(D_PARAM VariantN) from a generic - * argument. Statically rejects disallowed types. */ - - VariantN opAssign(T)(T rhs) - { - //writeln(typeid(rhs)); - static assert(allowed!(T), "Cannot store a " ~ T.stringof - ~ " in a " ~ VariantN.stringof ~ ". Valid types are " - ~ AllowedTypes.stringof); - static if (is(T : VariantN)) - { - rhs.fptr(OpID.copyOut, &rhs.store, &this); - } - else static if (is(T : const(VariantN))) - { - static assert(false, - "Assigning Variant objects from const Variant" - " objects is currently not supported."); - } - else - { - static if (T.sizeof <= size) - { - // If T is a class we're only copying the reference, so it - // should be safe to cast away shared so the memcpy will work. - // - // TODO: If a shared class has an atomic reference then using - // an atomic load may be more correct. Just make sure - // to use the fastest approach for the load op. - static if (is(T == class) && is(T == shared)) - memcpy(&store, cast(const(void*)) &rhs, rhs.sizeof); - else - memcpy(&store, &rhs, rhs.sizeof); - } - else - { - static if (__traits(compiles, {new T(rhs);})) - { - auto p = new T(rhs); - } - else - { - auto p = new T; - *p = rhs; - } - memcpy(&store, &p, p.sizeof); - } - fptr = &handler!(T); - } - return this; - } - - /** Returns true if and only if the $(D_PARAM VariantN) object - * holds a valid value (has been initialized with, or assigned - * from, a valid value). - * Example: - * ---- - * Variant a; - * assert(!a.hasValue); - * Variant b; - * a = b; - * assert(!a.hasValue); // still no value - * a = 5; - * assert(a.hasValue); - * ---- - */ - - bool hasValue() const - { - // @@@BUG@@@ in compiler, the cast shouldn't be needed - return cast(typeof(&handler!(void))) fptr != &handler!(void); - } - - /** - * If the $(D_PARAM VariantN) object holds a value of the - * $(I exact) type $(D_PARAM T), returns a pointer to that - * value. Otherwise, returns $(D_PARAM null). In cases - * where $(D_PARAM T) is statically disallowed, $(D_PARAM - * peek) will not compile. - * - * Example: - * ---- - * Variant a = 5; - * auto b = a.peek!(int); - * assert(b !is null); - * *b = 6; - * assert(a == 6); - * ---- - */ - T * peek(T)() - { - static if (!is(T == void)) - static assert(allowed!(T), "Cannot store a " ~ T.stringof - ~ " in a " ~ VariantN.stringof); - return type == typeid(T) ? cast(T*) &store : null; - } - - /** - * Returns the $(D_PARAM typeid) of the currently held value. - */ - - TypeInfo type() const - { - TypeInfo result; - fptr(OpID.getTypeInfo, null, &result); - return result; - } - - /** - * Returns $(D_PARAM true) if and only if the $(D_PARAM VariantN) - * object holds an object implicitly convertible to type $(D_PARAM - * U). Implicit convertibility is defined as per - * $(LINK2 std_traits.html#ImplicitConversionTargets,ImplicitConversionTargets). - */ - - bool convertsTo(T)() - { - TypeInfo info = typeid(T); - return fptr(OpID.testConversion, null, &info) == 0; - } - - // private T[] testing123(T)(T*); - - // /** - // * A workaround for the fact that functions cannot return - // * statically-sized arrays by value. Essentially $(D_PARAM - // * DecayStaticToDynamicArray!(T[N])) is an alias for $(D_PARAM - // * T[]) and $(D_PARAM DecayStaticToDynamicArray!(T)) is an alias - // * for $(D_PARAM T). - // */ - - // template DecayStaticToDynamicArray(T) - // { - // static if (isStaticArray!(T)) - // { - // alias typeof(testing123(&T[0])) DecayStaticToDynamicArray; - // } - // else - // { - // alias T DecayStaticToDynamicArray; - // } - // } - - // static assert(is(DecayStaticToDynamicArray!(immutable(char)[21]) == - // immutable(char)[]), - // DecayStaticToDynamicArray!(immutable(char)[21]).stringof); - - /** - * Returns the value stored in the $(D_PARAM VariantN) object, - * implicitly converted to the requested type $(D_PARAM T), in - * fact $(D_PARAM DecayStaticToDynamicArray!(T)). If an implicit - * conversion is not possible, throws a $(D_PARAM - * VariantException). - */ - - T get(T)() if (!is(T == const)) - { - union Buf - { - @disable bool opEquals(ref Buf); - @disable int opCmp(ref Buf); - - TypeInfo info; - T result; - }; - auto p = *cast(T**) &store; - Buf buf = { typeid(T) }; - if (fptr(OpID.get, &store, &buf)) - { - throw new VariantException(type, typeid(T)); - } - return buf.result; - } - - T get(T)() const if (is(T == const)) - { - union Buf - { - @disable bool opEquals(ref Buf); - @disable int opCmp(ref Buf); - - TypeInfo info; - Unqual!T result; - }; - auto p = *cast(T**) &store; - Buf buf = { typeid(T) }; - if (fptr(OpID.get, cast(ubyte[size]*) &store, &buf)) - { - throw new VariantException(type, typeid(T)); - } - return buf.result; - } - - /** - * Returns the value stored in the $(D_PARAM VariantN) object, - * explicitly converted (coerced) to the requested type $(D_PARAM - * T). If $(D_PARAM T) is a string type, the value is formatted as - * a string. If the $(D_PARAM VariantN) object is a string, a - * parse of the string to type $(D_PARAM T) is attempted. If a - * conversion is not possible, throws a $(D_PARAM - * VariantException). - */ - - T coerce(T)() - { - static if (isNumeric!(T)) - { - if (convertsTo!real()) - { - // maybe optimize this fella; handle ints separately - return to!T(get!real); - } - else if (convertsTo!(const(char)[])) - { - return to!T(get!(const(char)[])); - } - else - { - enforce(false, text("Type ", type(), " does not convert to ", - typeid(T))); - assert(0); - } - } - else static if (is(T : Object)) - { - return to!(T)(get!(Object)); - } - else static if (isSomeString!(T)) - { - return to!(T)(toString); - } - else - { - // Fix for bug 1649 - static assert(false, "unsupported type for coercion"); - } - } - - /** - * Formats the stored value as a string. - */ - - string toString() - { - string result; - fptr(OpID.toString, &store, &result) == 0 || assert(false); - return result; - } - - /** - * Comparison for equality used by the "==" and "!=" operators. - */ - - // returns 1 if the two are equal - bool opEquals(T)(T rhs) - { - static if (is(T == VariantN)) - alias rhs temp; - else - auto temp = Variant(rhs); - return fptr(OpID.compare, &store, &temp) == 0; - } - - /** - * Ordering comparison used by the "<", "<=", ">", and ">=" - * operators. In case comparison is not sensible between the held - * value and $(D_PARAM rhs), an exception is thrown. - */ - - int opCmp(T)(T rhs) - { - static if (is(T == VariantN)) - alias rhs temp; - else - auto temp = Variant(rhs); - auto result = fptr(OpID.compare, &store, &temp); - if (result == sizediff_t.min) - { - throw new VariantException(type, temp.type); - } - - assert(result >= -1 && result <= 1); // Should be true for opCmp. - return cast(int) result; - } - - /** - * Computes the hash of the held value. - */ - - size_t toHash() - { - return type.getHash(&store); - } - - private VariantN opArithmetic(T, string op)(T other) - { - VariantN result; - static if (is(T == VariantN)) - { - if (convertsTo!(uint) && other.convertsTo!(uint)) - result = mixin("get!(uint) " ~ op ~ " other.get!(uint)"); - else if (convertsTo!(int) && other.convertsTo!(int)) - result = mixin("get!(int) " ~ op ~ " other.get!(int)"); - else if (convertsTo!(ulong) && other.convertsTo!(ulong)) - result = mixin("get!(ulong) " ~ op ~ " other.get!(ulong)"); - else if (convertsTo!(long) && other.convertsTo!(long)) - result = mixin("get!(long) " ~ op ~ " other.get!(long)"); - else if (convertsTo!(double) && other.convertsTo!(double)) - result = mixin("get!(double) " ~ op ~ " other.get!(double)"); - else - result = mixin("get!(real) " ~ op ~ " other.get!(real)"); - } - else - { - if (is(typeof(T.max) : uint) && T.min == 0 && convertsTo!(uint)) - result = mixin("get!(uint) " ~ op ~ " other"); - else if (is(typeof(T.max) : int) && T.min < 0 && convertsTo!(int)) - result = mixin("get!(int) " ~ op ~ " other"); - else if (is(typeof(T.max) : ulong) && T.min == 0 - && convertsTo!(ulong)) - result = mixin("get!(ulong) " ~ op ~ " other"); - else if (is(typeof(T.max) : long) && T.min < 0 && convertsTo!(long)) - result = mixin("get!(long) " ~ op ~ " other"); - else if (is(T : double) && convertsTo!(double)) - result = mixin("get!(double) " ~ op ~ " other"); - else - result = mixin("get!(real) " ~ op ~ " other"); - } - return result; - } - - private VariantN opLogic(T, string op)(T other) - { - VariantN result; - static if (is(T == VariantN)) - { - if (convertsTo!(uint) && other.convertsTo!(uint)) - result = mixin("get!(uint) " ~ op ~ " other.get!(uint)"); - else if (convertsTo!(int) && other.convertsTo!(int)) - result = mixin("get!(int) " ~ op ~ " other.get!(int)"); - else if (convertsTo!(ulong) && other.convertsTo!(ulong)) - result = mixin("get!(ulong) " ~ op ~ " other.get!(ulong)"); - else - result = mixin("get!(long) " ~ op ~ " other.get!(long)"); - } - else - { - if (is(typeof(T.max) : uint) && T.min == 0 && convertsTo!(uint)) - result = mixin("get!(uint) " ~ op ~ " other"); - else if (is(typeof(T.max) : int) && T.min < 0 && convertsTo!(int)) - result = mixin("get!(int) " ~ op ~ " other"); - else if (is(typeof(T.max) : ulong) && T.min == 0 - && convertsTo!(ulong)) - result = mixin("get!(ulong) " ~ op ~ " other"); - else - result = mixin("get!(long) " ~ op ~ " other"); - } - return result; - } - - /** - * Arithmetic between $(D_PARAM VariantN) objects and numeric - * values. All arithmetic operations return a $(D_PARAM VariantN) - * object typed depending on the types of both values - * involved. The conversion rules mimic D's built-in rules for - * arithmetic conversions. - */ - - // Adapted from http://www.prowiki.org/wiki4d/wiki.cgi?DanielKeep/Variant - // arithmetic - VariantN opAdd(T)(T rhs) { return opArithmetic!(T, "+")(rhs); } - ///ditto - VariantN opSub(T)(T rhs) { return opArithmetic!(T, "-")(rhs); } - - // Commenteed all _r versions for now because of ambiguities - // arising when two Variants are used - - /////ditto - // VariantN opSub_r(T)(T lhs) - // { - // return VariantN(lhs).opArithmetic!(VariantN, "-")(this); - // } - ///ditto - VariantN opMul(T)(T rhs) { return opArithmetic!(T, "*")(rhs); } - ///ditto - VariantN opDiv(T)(T rhs) { return opArithmetic!(T, "/")(rhs); } - // ///ditto - // VariantN opDiv_r(T)(T lhs) - // { - // return VariantN(lhs).opArithmetic!(VariantN, "/")(this); - // } - ///ditto - VariantN opMod(T)(T rhs) { return opArithmetic!(T, "%")(rhs); } - // ///ditto - // VariantN opMod_r(T)(T lhs) - // { - // return VariantN(lhs).opArithmetic!(VariantN, "%")(this); - // } - ///ditto - VariantN opAnd(T)(T rhs) { return opLogic!(T, "&")(rhs); } - ///ditto - VariantN opOr(T)(T rhs) { return opLogic!(T, "|")(rhs); } - ///ditto - VariantN opXor(T)(T rhs) { return opLogic!(T, "^")(rhs); } - ///ditto - VariantN opShl(T)(T rhs) { return opLogic!(T, "<<")(rhs); } - // ///ditto - // VariantN opShl_r(T)(T lhs) - // { - // return VariantN(lhs).opLogic!(VariantN, "<<")(this); - // } - ///ditto - VariantN opShr(T)(T rhs) { return opLogic!(T, ">>")(rhs); } - // ///ditto - // VariantN opShr_r(T)(T lhs) - // { - // return VariantN(lhs).opLogic!(VariantN, ">>")(this); - // } - ///ditto - VariantN opUShr(T)(T rhs) { return opLogic!(T, ">>>")(rhs); } - // ///ditto - // VariantN opUShr_r(T)(T lhs) - // { - // return VariantN(lhs).opLogic!(VariantN, ">>>")(this); - // } - ///ditto - VariantN opCat(T)(T rhs) - { - auto temp = this; - temp ~= rhs; - return temp; - } - // ///ditto - // VariantN opCat_r(T)(T rhs) - // { - // VariantN temp = rhs; - // temp ~= this; - // return temp; - // } - - ///ditto - VariantN opAddAssign(T)(T rhs) { return this = this + rhs; } - ///ditto - VariantN opSubAssign(T)(T rhs) { return this = this - rhs; } - ///ditto - VariantN opMulAssign(T)(T rhs) { return this = this * rhs; } - ///ditto - VariantN opDivAssign(T)(T rhs) { return this = this / rhs; } - ///ditto - VariantN opModAssign(T)(T rhs) { return this = this % rhs; } - ///ditto - VariantN opAndAssign(T)(T rhs) { return this = this & rhs; } - ///ditto - VariantN opOrAssign(T)(T rhs) { return this = this | rhs; } - ///ditto - VariantN opXorAssign(T)(T rhs) { return this = this ^ rhs; } - ///ditto - VariantN opShlAssign(T)(T rhs) { return this = this << rhs; } - ///ditto - VariantN opShrAssign(T)(T rhs) { return this = this >> rhs; } - ///ditto - VariantN opUShrAssign(T)(T rhs) { return this = this >>> rhs; } - ///ditto - VariantN opCatAssign(T)(T rhs) - { - auto toAppend = VariantN(rhs); - fptr(OpID.catAssign, &store, &toAppend) == 0 || assert(false); - return this; - } - - /** - * Array and associative array operations. If a $(D_PARAM - * VariantN) contains an (associative) array, it can be indexed - * into. Otherwise, an exception is thrown. - * - * Example: - * ---- - * auto a = Variant(new int[10]); - * a[5] = 42; - * assert(a[5] == 42); - * int[int] hash = [ 42:24 ]; - * a = hash; - * assert(a[42] == 24); - * ---- - * - * Caveat: - * - * Due to limitations in current language, read-modify-write - * operations $(D_PARAM op=) will not work properly: - * - * ---- - * Variant a = new int[10]; - * a[5] = 42; - * a[5] += 8; - * assert(a[5] == 50); // fails, a[5] is still 42 - * ---- - */ - VariantN opIndex(K)(K i) - { - auto result = VariantN(i); - fptr(OpID.index, &store, &result) == 0 || assert(false); - return result; - } - - unittest - { - int[int] hash = [ 42:24 ]; - Variant v = hash; - assert(v[42] == 24); - v[42] = 5; - assert(v[42] == 5); - } - - /// ditto - VariantN opIndexAssign(T, N)(T value, N i) - { - VariantN[2] args = [ VariantN(value), VariantN(i) ]; - fptr(OpID.indexAssign, &store, &args) == 0 || assert(false); - return args[0]; - } - - /** If the $(D_PARAM VariantN) contains an (associative) array, - * returns the length of that array. Otherwise, throws an - * exception. - */ - @property size_t length() - { - return cast(size_t) fptr(OpID.length, &store, null); - } - - /** - If the $(D VariantN) contains an array, applies $(D dg) to each - element of the array in turn. Otherwise, throws an exception. - */ - int opApply(Delegate)(scope Delegate dg) if (is(Delegate == delegate)) - { - alias ParameterTypeTuple!(Delegate)[0] A; - if (type() == typeid(A[])) - { - auto arr = get!(A[]); - foreach (ref e; arr) - { - if (dg(e)) return 1; - } - } - else static if (is(A == VariantN)) - { - foreach (i; 0 .. length) - { - // @@@TODO@@@: find a better way to not confuse - // clients who think they change values stored in the - // Variant when in fact they are only changing tmp. - auto tmp = this[i]; - debug scope(exit) assert(tmp == this[i]); - if (dg(tmp)) return 1; - } - } - else - { - enforce(false, text("Variant type ", type(), - " not iterable with values of type ", - A.stringof)); - } - return 0; - } -} - -/** - * Algebraic data type restricted to a closed set of possible - * types. It's an alias for a $(D_PARAM VariantN) with an - * appropriately-constructed maximum size. $(D_PARAM 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 $(D_PARAM Algebraic) will allow compile-time - * checking that all possible types are handled by user code, - * eliminating a large class of errors. - * - * Bugs: - * - * Currently, $(D_PARAM 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 - * ---- - */ - -template Algebraic(T...) -{ - alias VariantN!(maxSize!(T), T) Algebraic; -} - -/** -$(D_PARAM Variant) is an alias for $(D_PARAM VariantN) instantiated -with the largest of $(D_PARAM creal), $(D_PARAM char[]), and $(D_PARAM -void delegate()). This ensures that $(D_PARAM 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 -$(D_PARAM VariantN) directly with a different maximum size either for -storing larger types, or for saving memory. - */ - -alias VariantN!(maxSize!(creal, char[], void delegate())) Variant; - -/** - * Returns an array of variants constructed from $(D_PARAM 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 $(D_PARAM boxArray) - * function in the $(D_PARAM 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 $(D_PARAM Variant) needs - * static type information about the type being held, so as to store a - * pointer to function for fast retrieval. - */ - -Variant[] variantArray(T...)(T args) -{ - Variant[] result; - foreach (arg; args) - { - result ~= Variant(arg); - } - return result; -} - -/** - * Thrown in three cases: - * - * $(OL $(LI An uninitialized Variant is used in any way except - * assignment and $(D_PARAM hasValue);) $(LI A $(D_PARAM get) or - * $(D_PARAM coerce) is attempted with an incompatible target type;) - * $(LI A comparison between $(D_PARAM Variant) objects of - * incompatible types is attempted.)) - * - */ - -// @@@ BUG IN COMPILER. THE 'STATIC' BELOW SHOULD NOT COMPILE -static class VariantException : Exception -{ - /// The source type in the conversion or comparison - TypeInfo source; - /// The target type in the conversion or comparison - TypeInfo target; - this(string s) - { - super(s); - } - this(TypeInfo source, TypeInfo target) - { - super("Variant: attempting to use incompatible types " - ~ source.toString - ~ " and " ~ target.toString); - this.source = source; - this.target = target; - } -} - -unittest -{ - alias This2Variant!(char, int, This[int]) W1; - alias TypeTuple!(int, char[int]) W2; - static assert(is(W1 == W2)); - - alias Algebraic!(void, string) var_t; - var_t foo = "quux"; -} - -unittest -{ - // @@@BUG@@@ - // alias Algebraic!(real, This[], This[int], This[This]) A; - // A v1, v2, v3; - // v2 = 5.0L; - // v3 = 42.0L; - // //v1 = [ v2 ][]; - // auto v = v1.peek!(A[]); - // //writeln(v[0]); - // v1 = [ 9 : v3 ]; - // //writeln(v1); - // v1 = [ v3 : v3 ]; - // //writeln(v1); -} - -unittest -{ - // try it with an oddly small size - VariantN!(1) test; - assert(test.size > 1); - - // variantArray tests - auto heterogeneous = variantArray(1, 4.5, "hi"); - assert(heterogeneous.length == 3); - auto variantArrayAsVariant = Variant(heterogeneous); - assert(variantArrayAsVariant[0] == 1); - assert(variantArrayAsVariant.length == 3); - - // array tests - auto arr = Variant([1.2].dup); - auto e = arr[0]; - assert(e == 1.2); - arr[0] = 2.0; - assert(arr[0] == 2); - arr ~= 4.5; - assert(arr[1] == 4.5); - - // general tests - Variant a; - auto b = Variant(5); - assert(!b.peek!(real) && b.peek!(int)); - // assign - a = *b.peek!(int); - // comparison - assert(a == b, a.type.toString ~ " " ~ b.type.toString); - auto c = Variant("this is a string"); - assert(a != c); - // comparison via implicit conversions - a = 42; b = 42.0; assert(a == b); - - // try failing conversions - bool failed = false; - try - { - auto d = c.get!(int); - } - catch (Exception e) - { - //writeln(stderr, e.toString); - failed = true; - } - assert(failed); // :o) - - // toString tests - a = Variant(42); assert(a.toString == "42"); - a = Variant(42.22); assert(a.toString == "42.22"); - - // coerce tests - a = Variant(42.22); assert(a.coerce!(int) == 42); - a = cast(short) 5; assert(a.coerce!(double) == 5); - - // Object tests - class B1 {} - class B2 : B1 {} - a = new B2; - assert(a.coerce!(B1) !is null); - a = new B1; -// BUG: I can't get the following line to pass: -// assert(collectException(a.coerce!(B2) is null)); - a = cast(Object) new B2; // lose static type info; should still work - assert(a.coerce!(B2) !is null); - -// struct Big { int a[45]; } -// a = Big.init; - - // hash - assert(a.toHash != 0); -} - -// tests adapted from -// http://www.dsource.org/projects/tango/browser/trunk/tango/core/Variant.d?rev=2601 -unittest -{ - Variant v; - - assert(!v.hasValue); - v = 42; - assert( v.peek!(int) ); - assert( v.convertsTo!(long) ); - assert( v.get!(int) == 42 ); - assert( v.get!(long) == 42L ); - assert( v.get!(ulong) == 42uL ); - - // should be string... @@@BUG IN COMPILER - v = "Hello, World!"c; - assert( v.peek!(string) ); - - assert( v.get!(string) == "Hello, World!" ); - assert(!is(char[] : wchar[])); - assert( !v.convertsTo!(wchar[]) ); - assert( v.get!(string) == "Hello, World!" ); - - // Literal arrays are dynamically-typed - v = cast(int[5]) [1,2,3,4,5]; - assert( v.peek!(int[5]) ); - assert( v.get!(int[5]) == [1,2,3,4,5] ); - - { - // @@@BUG@@@: array literals should have type T[], not T[5] (I guess) - // v = [1,2,3,4,5]; - // assert( v.peek!(int[]) ); - // assert( v.get!(int[]) == [1,2,3,4,5] ); - } - - v = 3.1413; - assert( v.peek!(double) ); - assert( v.convertsTo!(real) ); - //@@@ BUG IN COMPILER: DOUBLE SHOULD NOT IMPLICITLY CONVERT TO FLOAT - assert( !v.convertsTo!(float) ); - assert( *v.peek!(double) == 3.1413 ); - - auto u = Variant(v); - assert( u.peek!(double) ); - assert( *u.peek!(double) == 3.1413 ); - - // operators - v = 38; - assert( v + 4 == 42 ); - assert( 4 + v == 42 ); - assert( v - 4 == 34 ); - assert( Variant(4) - v == -34 ); - assert( v * 2 == 76 ); - assert( 2 * v == 76 ); - assert( v / 2 == 19 ); - assert( Variant(2) / v == 0 ); - assert( v % 2 == 0 ); - assert( Variant(2) % v == 2 ); - assert( (v & 6) == 6 ); - assert( (6 & v) == 6 ); - assert( (v | 9) == 47 ); - assert( (9 | v) == 47 ); - assert( (v ^ 5) == 35 ); - assert( (5 ^ v) == 35 ); - assert( v << 1 == 76 ); - assert( Variant(1) << Variant(2) == 4 ); - assert( v >> 1 == 19 ); - assert( Variant(4) >> Variant(2) == 1 ); - assert( Variant("abc") ~ "def" == "abcdef" ); - assert( Variant("abc") ~ Variant("def") == "abcdef" ); - - v = 38; - v += 4; - assert( v == 42 ); - v = 38; v -= 4; assert( v == 34 ); - v = 38; v *= 2; assert( v == 76 ); - v = 38; v /= 2; assert( v == 19 ); - v = 38; v %= 2; assert( v == 0 ); - v = 38; v &= 6; assert( v == 6 ); - v = 38; v |= 9; assert( v == 47 ); - v = 38; v ^= 5; assert( v == 35 ); - v = 38; v <<= 1; assert( v == 76 ); - v = 38; v >>= 1; assert( v == 19 ); - v = 38; v += 1; assert( v < 40 ); - - v = "abc"; - v ~= "def"; - assert( v == "abcdef", *v.peek!(char[]) ); - assert( Variant(0) < Variant(42) ); - assert( Variant(42) > Variant(0) ); - assert( Variant(42) > Variant(0.1) ); - assert( Variant(42.1) > Variant(1) ); - assert( Variant(21) == Variant(21) ); - assert( Variant(0) != Variant(42) ); - assert( Variant("bar") == Variant("bar") ); - assert( Variant("foo") != Variant("bar") ); - - { - auto v1 = Variant(42); - auto v2 = Variant("foo"); - auto v3 = Variant(1+2.0i); - - int[Variant] hash; - hash[v1] = 0; - hash[v2] = 1; - hash[v3] = 2; - - assert( hash[v1] == 0 ); - assert( hash[v2] == 1 ); - assert( hash[v3] == 2 ); - } - /+ - // @@@BUG@@@ - // dmd: mtype.c:3886: StructDeclaration* TypeAArray::getImpl(): Assertion `impl' failed. - { - int[char[]] hash; - hash["a"] = 1; - hash["b"] = 2; - hash["c"] = 3; - Variant vhash = hash; - - assert( vhash.get!(int[char[]])["a"] == 1 ); - assert( vhash.get!(int[char[]])["b"] == 2 ); - assert( vhash.get!(int[char[]])["c"] == 3 ); - } - +/ -} - -unittest -{ - // bug 1558 - Variant va=1; - Variant vb=-2; - assert((va+vb).get!(int) == -1); - assert((va-vb).get!(int) == 3); -} - -unittest -{ - Variant a; - a=5; - Variant b; - b=a; - Variant[] c; - c = variantArray(1, 2, 3.0, "hello", 4); - assert(c[3] == "hello"); -} - -unittest -{ - Variant v = 5; - assert (!__traits(compiles, v.coerce!(bool delegate()))); -} - - -unittest -{ - struct Huge { - real a, b, c, d, e, f, g; - } - - Huge huge; - huge.e = 42; - Variant v; - v = huge; // Compile time error. - assert(v.get!(Huge).e == 42); -} - -unittest -{ - const x = Variant(42); - auto y1 = x.get!(const int)(); - // @@@BUG@@@ - //auto y2 = x.get!(immutable int)(); -} - -// test iteration -unittest -{ - auto v = Variant([ 1, 2, 3, 4 ][]); - auto j = 0; - foreach (int i; v) - { - assert(i == ++j); - } - assert(j == 4); -} - -// test convertibility -unittest -{ - auto v = Variant("abc".dup); - assert(v.convertsTo!(char[])); -} - -// http://d.puremagic.com/issues/show_bug.cgi?id=5424 -unittest -{ - interface A { - void func1(); - } - static class AC: A { - void func1() { - } - } - - A a = new AC(); - a.func1(); - Variant b = Variant(a); -} diff --git a/test/src/constructor.d b/test/src/constructor.d index 82c8b90..ce2b076 100644 --- a/test/src/constructor.d +++ b/test/src/constructor.d @@ -403,7 +403,7 @@ void testConstructor(bool verbose, string dataFilename, string codeDummy) size_t i = 0; foreach(node; loader) { - if(!node.equals!(Node, false)(exp[i])) + if(!node.equals!false(exp[i])) { if(verbose) { diff --git a/test/src/representer.d b/test/src/representer.d index 5178611..d7cccee 100644 --- a/test/src/representer.d +++ b/test/src/representer.d @@ -69,7 +69,7 @@ void testRepresenterTypes(bool verbose, string codeFilename) assert(expectedNodes.length == readNodes.length); foreach(n; 0 .. expectedNodes.length) { - assert(expectedNodes[n].equals!(Node, false)(readNodes[n])); + assert(expectedNodes[n].equals!false(readNodes[n])); } } }