/std/conv.d
http://github.com/jcd/phobos · D · 4888 lines · 3689 code · 493 blank · 706 comment · 935 complexity · 9f8975efc579f42aff691130644fc5ba MD5 · raw file
Large files are truncated click here to view the full file
- // Written in the D programming language.
- /**
- A one-stop shop for converting values from one type to another.
- Copyright: Copyright Digital Mars 2007-.
- License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
- Authors: $(WEB digitalmars.com, Walter Bright),
- $(WEB erdani.org, Andrei Alexandrescu),
- Shin Fujishiro,
- Adam D. Ruppe,
- Kenji Hara
- Source: $(PHOBOSSRC std/_conv.d)
- Macros:
- WIKI = Phobos/StdConv
- */
- module std.conv;
- import core.stdc.string;
- import std.algorithm, std.array, std.ascii, std.exception, std.range,
- std.string, std.traits, std.typecons, std.typetuple, std.uni,
- std.utf;
- import std.format;
- //debug=conv; // uncomment to turn on debugging printf's
- /* ************* Exceptions *************** */
- /**
- * Thrown on conversion errors.
- */
- class ConvException : Exception
- {
- @safe pure nothrow
- this(string s, string fn = __FILE__, size_t ln = __LINE__)
- {
- super(s, fn, ln);
- }
- }
- private string convError_unexpected(S)(S source)
- {
- return source.empty ? "end of input" : text("'", source.front, "'");
- }
- private auto convError(S, T)(S source, string fn = __FILE__, size_t ln = __LINE__)
- {
- return new ConvException(
- text("Unexpected ", convError_unexpected(source),
- " when converting from type "~S.stringof~" to type "~T.stringof),
- fn, ln);
- }
- private auto convError(S, T)(S source, int radix, string fn = __FILE__, size_t ln = __LINE__)
- {
- return new ConvException(
- text("Unexpected ", convError_unexpected(source),
- " when converting from type "~S.stringof~" base ", radix,
- " to type "~T.stringof),
- fn, ln);
- }
- @safe pure/* nothrow*/ // lazy parameter bug
- private auto parseError(lazy string msg, string fn = __FILE__, size_t ln = __LINE__)
- {
- return new ConvException(text("Can't parse string: ", msg), fn, ln);
- }
- private void parseCheck(alias source)(dchar c, string fn = __FILE__, size_t ln = __LINE__)
- {
- if (source.empty)
- throw parseError(text("unexpected end of input when expecting", "\"", c, "\""));
- if (source.front != c)
- throw parseError(text("\"", c, "\" is missing"), fn, ln);
- source.popFront();
- }
- private
- {
- template isImaginary(T)
- {
- enum bool isImaginary = staticIndexOf!(Unqual!T,
- ifloat, idouble, ireal) >= 0;
- }
- template isComplex(T)
- {
- enum bool isComplex = staticIndexOf!(Unqual!T,
- cfloat, cdouble, creal) >= 0;
- }
- template isNarrowInteger(T)
- {
- enum bool isNarrowInteger = staticIndexOf!(Unqual!T,
- byte, ubyte, short, ushort) >= 0;
- }
- T toStr(T, S)(S src)
- if (isSomeString!T)
- {
- import std.format : FormatSpec, formatValue;
- auto w = appender!T();
- FormatSpec!(ElementEncodingType!T) f;
- formatValue(w, src, f);
- return w.data;
- }
- template isExactSomeString(T)
- {
- enum isExactSomeString = isSomeString!T && !is(T == enum);
- }
- template isEnumStrToStr(S, T)
- {
- enum isEnumStrToStr = isImplicitlyConvertible!(S, T) &&
- is(S == enum) && isExactSomeString!T;
- }
- template isNullToStr(S, T)
- {
- enum isNullToStr = isImplicitlyConvertible!(S, T) &&
- (is(Unqual!S == typeof(null))) && isExactSomeString!T;
- }
- template isRawStaticArray(T, A...)
- {
- enum isRawStaticArray =
- A.length == 0 &&
- isStaticArray!T &&
- !is(T == class) &&
- !is(T == interface) &&
- !is(T == struct) &&
- !is(T == union);
- }
- }
- /**
- * Thrown on conversion overflow errors.
- */
- class ConvOverflowException : ConvException
- {
- @safe pure nothrow
- this(string s, string fn = __FILE__, size_t ln = __LINE__)
- {
- super(s, fn, ln);
- }
- }
- /**
- The $(D_PARAM to) family of functions converts a value from type
- $(D_PARAM Source) to type $(D_PARAM Target). The source type is
- deduced and the target type must be specified, for example the
- expression $(D_PARAM to!int(42.0)) converts the number 42 from
- $(D_PARAM double) to $(D_PARAM int). The conversion is "safe", i.e.,
- it checks for overflow; $(D_PARAM to!int(4.2e10)) would throw the
- $(D_PARAM ConvOverflowException) exception. Overflow checks are only
- inserted when necessary, e.g., $(D_PARAM to!double(42)) does not do
- any checking because any int fits in a double.
- Converting a value to its own type (useful mostly for generic code)
- simply returns its argument.
- Example:
- -------------------------
- int a = 42;
- auto b = to!int(a); // b is int with value 42
- auto c = to!double(3.14); // c is double with value 3.14
- -------------------------
- Converting among numeric types is a safe way to cast them around.
- Conversions from floating-point types to integral types allow loss of
- precision (the fractional part of a floating-point number). The
- conversion is truncating towards zero, the same way a cast would
- truncate. (To round a floating point value when casting to an
- integral, use $(D_PARAM roundTo).)
- Examples:
- -------------------------
- int a = 420;
- auto b = to!long(a); // same as long b = a;
- auto c = to!byte(a / 10); // fine, c = 42
- auto d = to!byte(a); // throw ConvOverflowException
- double e = 4.2e6;
- auto f = to!int(e); // f == 4200000
- e = -3.14;
- auto g = to!uint(e); // fails: floating-to-integral negative overflow
- e = 3.14;
- auto h = to!uint(e); // h = 3
- e = 3.99;
- h = to!uint(a); // h = 3
- e = -3.99;
- f = to!int(a); // f = -3
- -------------------------
- Conversions from integral types to floating-point types always
- succeed, but might lose accuracy. The largest integers with a
- predecessor representable in floating-point format are 2^24-1 for
- float, 2^53-1 for double, and 2^64-1 for $(D_PARAM real) (when
- $(D_PARAM real) is 80-bit, e.g. on Intel machines).
- Example:
- -------------------------
- int a = 16_777_215; // 2^24 - 1, largest proper integer representable as float
- assert(to!int(to!float(a)) == a);
- assert(to!int(to!float(-a)) == -a);
- a += 2;
- assert(to!int(to!float(a)) == a); // fails!
- -------------------------
- Conversions from string to numeric types differ from the C equivalents
- $(D_PARAM atoi()) and $(D_PARAM atol()) by checking for overflow and
- not allowing whitespace.
- For conversion of strings to signed types, the grammar recognized is:
- <pre>
- $(I Integer): $(I Sign UnsignedInteger)
- $(I UnsignedInteger)
- $(I Sign):
- $(B +)
- $(B -)
- </pre>
- For conversion to unsigned types, the grammar recognized is:
- <pre>
- $(I UnsignedInteger):
- $(I DecimalDigit)
- $(I DecimalDigit) $(I UnsignedInteger)
- </pre>
- Converting an array to another array type works by converting each
- element in turn. Associative arrays can be converted to associative
- arrays as long as keys and values can in turn be converted.
- Example:
- -------------------------
- int[] a = ([1, 2, 3]).dup;
- auto b = to!(float[])(a);
- assert(b == [1.0f, 2, 3]);
- string str = "1 2 3 4 5 6";
- auto numbers = to!(double[])(split(str));
- assert(numbers == [1.0, 2, 3, 4, 5, 6]);
- int[string] c;
- c["a"] = 1;
- c["b"] = 2;
- auto d = to!(double[wstring])(c);
- assert(d["a"w] == 1 && d["b"w] == 2);
- -------------------------
- Conversions operate transitively, meaning that they work on arrays and
- associative arrays of any complexity:
- -------------------------
- int[string][double[int[]]] a;
- ...
- auto b = to!(short[wstring][string[double[]]])(a);
- -------------------------
- This conversion works because $(D_PARAM to!short) applies to an
- $(D_PARAM int), $(D_PARAM to!wstring) applies to a $(D_PARAM
- string), $(D_PARAM to!string) applies to a $(D_PARAM double), and
- $(D_PARAM to!(double[])) applies to an $(D_PARAM int[]). The
- conversion might throw an exception because $(D_PARAM to!short)
- might fail the range check.
- */
- /**
- Entry point that dispatches to the appropriate conversion
- primitive. Client code normally calls $(D _to!TargetType(value))
- (and not some variant of $(D toImpl)).
- */
- template to(T)
- {
- T to(A...)(A args)
- if (!isRawStaticArray!A)
- {
- return toImpl!T(args);
- }
- // Fix issue 6175
- T to(S)(ref S arg)
- if (isRawStaticArray!S)
- {
- return toImpl!T(arg);
- }
- }
- // Tests for issue 6175
- @safe pure unittest
- {
- char[9] sarr = "blablabla";
- auto darr = to!(char[])(sarr);
- assert(sarr.ptr == darr.ptr);
- assert(sarr.length == darr.length);
- }
- // Tests for issue 7348
- @safe pure unittest
- {
- assert(to!string(null) == "null");
- assert(text(null) == "null");
- }
- // Tests for issue 11390
- @safe pure unittest
- {
- const(typeof(null)) ctn;
- immutable(typeof(null)) itn;
- assert(to!string(ctn) == "null");
- assert(to!string(itn) == "null");
- }
- // Tests for issue 8729: do NOT skip leading WS
- @safe pure unittest
- {
- foreach (T; TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong))
- {
- assertThrown!ConvException(to!T(" 0"));
- assertThrown!ConvException(to!T(" 0", 8));
- }
- foreach (T; TypeTuple!(float, double, real))
- {
- assertThrown!ConvException(to!T(" 0"));
- }
- assertThrown!ConvException(to!bool(" true"));
- alias NullType = typeof(null);
- assertThrown!ConvException(to!NullType(" null"));
- alias ARR = int[];
- assertThrown!ConvException(to!ARR(" [1]"));
- alias AA = int[int];
- assertThrown!ConvException(to!AA(" [1:1]"));
- }
- /**
- If the source type is implicitly convertible to the target type, $(D
- to) simply performs the implicit conversion.
- */
- T toImpl(T, S)(S value)
- if (isImplicitlyConvertible!(S, T) &&
- !isEnumStrToStr!(S, T) && !isNullToStr!(S, T))
- {
- template isSignedInt(T)
- {
- enum isSignedInt = isIntegral!T && isSigned!T;
- }
- alias isUnsignedInt = isUnsigned;
- // Conversion from integer to integer, and changing its sign
- static if (isUnsignedInt!S && isSignedInt!T && S.sizeof == T.sizeof)
- { // unsigned to signed & same size
- enforce(value <= cast(S)T.max,
- new ConvOverflowException("Conversion positive overflow"));
- }
- else static if (isSignedInt!S && isUnsignedInt!T)
- { // signed to unsigned
- enforce(0 <= value,
- new ConvOverflowException("Conversion negative overflow"));
- }
- return value;
- }
- @safe pure unittest
- {
- enum E { a } // Issue 9523 - Allow identity enum conversion
- auto e = to!E(E.a);
- assert(e == E.a);
- }
- @safe pure unittest
- {
- debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded.");
- int a = 42;
- auto b = to!long(a);
- assert(a == b);
- }
- // Tests for issue 6377
- @safe pure unittest
- {
- // Conversion between same size
- foreach (S; TypeTuple!(byte, short, int, long))
- {
- alias Unsigned!S U;
- foreach (Sint; TypeTuple!(S, const S, immutable S))
- foreach (Uint; TypeTuple!(U, const U, immutable U))
- {
- // positive overflow
- Uint un = Uint.max;
- assertThrown!ConvOverflowException(to!Sint(un),
- text(Sint.stringof, ' ', Uint.stringof, ' ', un));
- // negative overflow
- Sint sn = -1;
- assertThrown!ConvOverflowException(to!Uint(sn),
- text(Sint.stringof, ' ', Uint.stringof, ' ', un));
- }
- }
- // Conversion between different size
- foreach (i, S1; TypeTuple!(byte, short, int, long))
- foreach ( S2; TypeTuple!(byte, short, int, long)[i+1..$])
- {
- alias Unsigned!S1 U1;
- alias Unsigned!S2 U2;
- static assert(U1.sizeof < S2.sizeof);
- // small unsigned to big signed
- foreach (Uint; TypeTuple!(U1, const U1, immutable U1))
- foreach (Sint; TypeTuple!(S2, const S2, immutable S2))
- {
- Uint un = Uint.max;
- assertNotThrown(to!Sint(un));
- assert(to!Sint(un) == un);
- }
- // big unsigned to small signed
- foreach (Uint; TypeTuple!(U2, const U2, immutable U2))
- foreach (Sint; TypeTuple!(S1, const S1, immutable S1))
- {
- Uint un = Uint.max;
- assertThrown(to!Sint(un));
- }
- static assert(S1.sizeof < U2.sizeof);
- // small signed to big unsigned
- foreach (Sint; TypeTuple!(S1, const S1, immutable S1))
- foreach (Uint; TypeTuple!(U2, const U2, immutable U2))
- {
- Sint sn = -1;
- assertThrown!ConvOverflowException(to!Uint(sn));
- }
- // big signed to small unsigned
- foreach (Sint; TypeTuple!(S2, const S2, immutable S2))
- foreach (Uint; TypeTuple!(U1, const U1, immutable U1))
- {
- Sint sn = -1;
- assertThrown!ConvOverflowException(to!Uint(sn));
- }
- }
- }
- /*
- Converting static arrays forwards to their dynamic counterparts.
- */
- T toImpl(T, S)(ref S s)
- if (isRawStaticArray!S)
- {
- return toImpl!(T, typeof(s[0])[])(s);
- }
- @safe pure unittest
- {
- char[4] test = ['a', 'b', 'c', 'd'];
- static assert(!isInputRange!(Unqual!(char[4])));
- assert(to!string(test) == test);
- }
- /**
- When source type supports member template function opCast, is is used.
- */
- T toImpl(T, S)(S value)
- if (!isImplicitlyConvertible!(S, T) &&
- is(typeof(S.init.opCast!T()) : T) &&
- !isExactSomeString!T)
- {
- return value.opCast!T();
- }
- @safe pure unittest
- {
- debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded.");
- class B
- {
- T opCast(T)() { return 43; }
- }
- auto b = new B;
- assert(to!int(b) == 43);
- debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded.");
- struct S
- {
- T opCast(T)() { return 43; }
- }
- auto s = S();
- assert(to!int(s) == 43);
- }
- /**
- When target type supports 'converting construction', it is used.
- $(UL $(LI If target type is struct, $(D T(value)) is used.)
- $(LI If target type is class, $(D new T(value)) is used.))
- */
- T toImpl(T, S)(S value)
- if (!isImplicitlyConvertible!(S, T) &&
- is(T == struct) && is(typeof(T(value))))
- {
- return T(value);
- }
- // Bugzilla 3961
- @safe pure unittest
- {
- debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded.");
- struct Int
- {
- int x;
- }
- Int i = to!Int(1);
- static struct Int2
- {
- int x;
- this(int x) @safe pure { this.x = x; }
- }
- Int2 i2 = to!Int2(1);
- static struct Int3
- {
- int x;
- static Int3 opCall(int x) @safe pure
- {
- Int3 i;
- i.x = x;
- return i;
- }
- }
- Int3 i3 = to!Int3(1);
- }
- // Bugzilla 6808
- @safe pure unittest
- {
- static struct FakeBigInt
- {
- this(string s) @safe pure {}
- }
- string s = "101";
- auto i3 = to!FakeBigInt(s);
- }
- /// ditto
- T toImpl(T, S)(S value)
- if (!isImplicitlyConvertible!(S, T) &&
- is(T == class) && is(typeof(new T(value))))
- {
- return new T(value);
- }
- @safe pure unittest
- {
- static struct S
- {
- int x;
- }
- static class C
- {
- int x;
- this(int x) @safe pure { this.x = x; }
- }
- static class B
- {
- int value;
- this(S src) @safe pure { value = src.x; }
- this(C src) @safe pure { value = src.x; }
- }
- S s = S(1);
- auto b1 = to!B(s); // == new B(s)
- assert(b1.value == 1);
- C c = new C(2);
- auto b2 = to!B(c); // == new B(c)
- assert(b2.value == 2);
- auto c2 = to!C(3); // == new C(3)
- assert(c2.x == 3);
- }
- @safe pure unittest
- {
- struct S
- {
- class A
- {
- this(B b) @safe pure {}
- }
- class B : A
- {
- this() @safe pure { super(this); }
- }
- }
- S.B b = new S.B();
- S.A a = to!(S.A)(b); // == cast(S.A)b
- // (do not run construction conversion like new S.A(b))
- assert(b is a);
- static class C : Object
- {
- this() @safe pure {}
- this(Object o) @safe pure {}
- }
- Object oc = new C();
- C a2 = to!C(oc); // == new C(a)
- // Construction conversion overrides down-casting conversion
- assert(a2 !is a); //
- }
- /**
- Object-to-object conversions by dynamic casting throw exception when the source is
- non-null and the target is null.
- */
- T toImpl(T, S)(S value)
- if (!isImplicitlyConvertible!(S, T) &&
- (is(S == class) || is(S == interface)) && !is(typeof(value.opCast!T()) : T) &&
- (is(T == class) || is(T == interface)) && !is(typeof(new T(value))))
- {
- static if (is(T == immutable))
- {
- // immutable <- immutable
- enum isModConvertible = is(S == immutable);
- }
- else static if (is(T == const))
- {
- static if (is(T == shared))
- {
- // shared const <- shared
- // shared const <- shared const
- // shared const <- immutable
- enum isModConvertible = is(S == shared) || is(S == immutable);
- }
- else
- {
- // const <- mutable
- // const <- immutable
- enum isModConvertible = !is(S == shared);
- }
- }
- else
- {
- static if (is(T == shared))
- {
- // shared <- shared mutable
- enum isModConvertible = is(S == shared) && !is(S == const);
- }
- else
- {
- // (mutable) <- (mutable)
- enum isModConvertible = is(Unqual!S == S);
- }
- }
- static assert(isModConvertible, "Bad modifier conversion: "~S.stringof~" to "~T.stringof);
- auto result = ()@trusted{ return cast(T) value; }();
- if (!result && value)
- {
- throw new ConvException("Cannot convert object of static type "
- ~S.classinfo.name~" and dynamic type "~value.classinfo.name
- ~" to type "~T.classinfo.name);
- }
- return result;
- }
- @safe pure unittest
- {
- debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded.");
- // Testing object conversions
- class A {}
- class B : A {}
- class C : A {}
- A a1 = new A, a2 = new B, a3 = new C;
- assert(to!B(a2) is a2);
- assert(to!C(a3) is a3);
- assertThrown!ConvException(to!B(a3));
- }
- // Unittest for 6288
- @safe pure unittest
- {
- template Identity(T) { alias T Identity; }
- template toConst(T) { alias const(T) toConst; }
- template toShared(T) { alias shared(T) toShared; }
- template toSharedConst(T) { alias shared(const(T)) toSharedConst; }
- template toImmutable(T) { alias immutable(T) toImmutable; }
- template AddModifier(int n) if (0 <= n && n < 5)
- {
- static if (n == 0) alias Identity AddModifier;
- else static if (n == 1) alias toConst AddModifier;
- else static if (n == 2) alias toShared AddModifier;
- else static if (n == 3) alias toSharedConst AddModifier;
- else static if (n == 4) alias toImmutable AddModifier;
- }
- interface I {}
- interface J {}
- class A {}
- class B : A {}
- class C : B, I, J {}
- class D : I {}
- foreach (m1; TypeTuple!(0,1,2,3,4)) // enumerate modifiers
- foreach (m2; TypeTuple!(0,1,2,3,4)) // ditto
- {
- alias AddModifier!m1 srcmod;
- alias AddModifier!m2 tgtmod;
- //pragma(msg, srcmod!Object, " -> ", tgtmod!Object, ", convertible = ",
- // isImplicitlyConvertible!(srcmod!Object, tgtmod!Object));
- // Compile time convertible equals to modifier convertible.
- static if (isImplicitlyConvertible!(srcmod!Object, tgtmod!Object))
- {
- // Test runtime conversions: class to class, class to interface,
- // interface to class, and interface to interface
- // Check that the runtime conversion to succeed
- srcmod!A ac = new srcmod!C();
- srcmod!I ic = new srcmod!C();
- assert(to!(tgtmod!C)(ac) !is null); // A(c) to C
- assert(to!(tgtmod!I)(ac) !is null); // A(c) to I
- assert(to!(tgtmod!C)(ic) !is null); // I(c) to C
- assert(to!(tgtmod!J)(ic) !is null); // I(c) to J
- // Check that the runtime conversion fails
- srcmod!A ab = new srcmod!B();
- srcmod!I id = new srcmod!D();
- assertThrown(to!(tgtmod!C)(ab)); // A(b) to C
- assertThrown(to!(tgtmod!I)(ab)); // A(b) to I
- assertThrown(to!(tgtmod!C)(id)); // I(d) to C
- assertThrown(to!(tgtmod!J)(id)); // I(d) to J
- }
- else
- {
- // Check that the conversion is rejected statically
- static assert(!is(typeof(to!(tgtmod!C)(srcmod!A.init)))); // A to C
- static assert(!is(typeof(to!(tgtmod!I)(srcmod!A.init)))); // A to I
- static assert(!is(typeof(to!(tgtmod!C)(srcmod!I.init)))); // I to C
- static assert(!is(typeof(to!(tgtmod!J)(srcmod!I.init)))); // I to J
- }
- }
- }
- /**
- Stringize conversion from all types is supported.
- $(UL
- $(LI String _to string conversion works for any two string types having
- ($(D char), $(D wchar), $(D dchar)) character widths and any
- combination of qualifiers (mutable, $(D const), or $(D immutable)).)
- $(LI Converts array (other than strings) to string.
- Each element is converted by calling $(D to!T).)
- $(LI Associative array to string conversion.
- Each element is printed by calling $(D to!T).)
- $(LI Object to string conversion calls $(D toString) against the object or
- returns $(D "null") if the object is null.)
- $(LI Struct to string conversion calls $(D toString) against the struct if
- it is defined.)
- $(LI For structs that do not define $(D toString), the conversion to string
- produces the list of fields.)
- $(LI Enumerated types are converted to strings as their symbolic names.)
- $(LI Boolean values are printed as $(D "true") or $(D "false").)
- $(LI $(D char), $(D wchar), $(D dchar) to a string type.)
- $(LI Unsigned or signed integers to strings.
- $(DL $(DT [special case])
- $(DD Convert integral value to string in $(D_PARAM radix) radix.
- radix must be a value from 2 to 36.
- value is treated as a signed value only if radix is 10.
- The characters A through Z are used to represent values 10 through 36
- and their case is determined by the $(D_PARAM letterCase) parameter.)))
- $(LI All floating point types to all string types.)
- $(LI Pointer to string conversions prints the pointer as a $(D size_t) value.
- If pointer is $(D char*), treat it as C-style strings.
- In that case, this function is $(D @system).))
- */
- T toImpl(T, S)(S value)
- if (!(isImplicitlyConvertible!(S, T) &&
- !isEnumStrToStr!(S, T) && !isNullToStr!(S, T)) &&
- isExactSomeString!T)
- {
- static if (isExactSomeString!S && value[0].sizeof == ElementEncodingType!T.sizeof)
- {
- // string-to-string with incompatible qualifier conversion
- static if (is(ElementEncodingType!T == immutable))
- {
- // conversion (mutable|const) -> immutable
- return value.idup;
- }
- else
- {
- // conversion (immutable|const) -> mutable
- return value.dup;
- }
- }
- else static if (isExactSomeString!S)
- {
- // other string-to-string
- //Use Appender directly instead of toStr, which also uses a formatedWrite
- auto w = appender!T();
- w.put(value);
- return w.data;
- }
- else static if (isIntegral!S && !is(S == enum))
- {
- // other integral-to-string conversions with default radix
- return toImpl!(T, S)(value, 10);
- }
- else static if (is(S == void[]) || is(S == const(void)[]) || is(S == immutable(void)[]))
- {
- // Converting void array to string
- alias Unqual!(ElementEncodingType!T) Char;
- auto raw = cast(const(ubyte)[]) value;
- enforce(raw.length % Char.sizeof == 0,
- new ConvException("Alignment mismatch in converting a "
- ~ S.stringof ~ " to a "
- ~ T.stringof));
- auto result = new Char[raw.length / Char.sizeof];
- ()@trusted{ memcpy(result.ptr, value.ptr, value.length); }();
- return cast(T) result;
- }
- else static if (isPointer!S && is(S : const(char)*))
- {
- // It is unsafe because we cannot guarantee that the pointer is null terminated.
- return value ? cast(T) value[0 .. strlen(value)].dup : cast(string)null;
- }
- else static if (isSomeString!T && is(S == enum))
- {
- static if (isSwitchable!(OriginalType!S) && EnumMembers!S.length <= 50)
- {
- switch(value)
- {
- foreach (I, member; NoDuplicates!(EnumMembers!S))
- {
- case member:
- return to!T(enumRep!(immutable(T), S, I));
- }
- default:
- }
- }
- else
- {
- foreach (I, member; EnumMembers!S)
- {
- if (value == member)
- return to!T(enumRep!(immutable(T), S, I));
- }
- }
- import std.format : FormatSpec, formatValue;
- //Default case, delegate to format
- //Note: we don't call toStr directly, to avoid duplicate work.
- auto app = appender!T();
- app.put("cast(");
- app.put(S.stringof);
- app.put(')');
- FormatSpec!char f;
- formatValue(app, cast(OriginalType!S)value, f);
- return app.data;
- }
- else
- {
- // other non-string values runs formatting
- return toStr!T(value);
- }
- }
- /*
- Check whether type $(D T) can be used in a switch statement.
- This is useful for compile-time generation of switch case statements.
- */
- private template isSwitchable(E)
- {
- enum bool isSwitchable = is(typeof({
- switch (E.init) { default: }
- }));
- }
- //
- unittest
- {
- static assert(isSwitchable!int);
- static assert(!isSwitchable!double);
- static assert(!isSwitchable!real);
- }
- //Static representation of the index I of the enum S,
- //In representation T.
- //T must be an immutable string (avoids un-necessary initializations).
- private template enumRep(T, S, size_t I)
- if (is (T == immutable) && isExactSomeString!T && is(S == enum))
- {
- static T enumRep = to!T(__traits(allMembers, S)[I]);
- }
- @safe pure unittest
- {
- void dg()
- {
- // string to string conversion
- debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded.");
- alias TypeTuple!(char, wchar, dchar) Chars;
- foreach (LhsC; Chars)
- {
- alias TypeTuple!(LhsC[], const(LhsC)[], immutable(LhsC)[]) LhStrings;
- foreach (Lhs; LhStrings)
- {
- foreach (RhsC; Chars)
- {
- alias TypeTuple!(RhsC[], const(RhsC)[], immutable(RhsC)[])
- RhStrings;
- foreach (Rhs; RhStrings)
- {
- Lhs s1 = to!Lhs("wyda");
- Rhs s2 = to!Rhs(s1);
- //writeln(Lhs.stringof, " -> ", Rhs.stringof);
- assert(s1 == to!Lhs(s2));
- }
- }
- }
- }
- foreach (T; Chars)
- {
- foreach (U; Chars)
- {
- T[] s1 = to!(T[])("Hello, world!");
- auto s2 = to!(U[])(s1);
- assert(s1 == to!(T[])(s2));
- auto s3 = to!(const(U)[])(s1);
- assert(s1 == to!(T[])(s3));
- auto s4 = to!(immutable(U)[])(s1);
- assert(s1 == to!(T[])(s4));
- }
- }
- }
- dg();
- assertCTFEable!dg;
- }
- @safe pure unittest
- {
- // Conversion reinterpreting void array to string
- debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded.");
- auto a = "abcx"w;
- const(void)[] b = a;
- assert(b.length == 8);
- auto c = to!(wchar[])(b);
- assert(c == "abcx");
- }
- @system pure unittest
- {
- // char* to string conversion
- debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded.");
- debug(conv) printf("string.to!string(char*).unittest\n");
- assert(to!string(cast(char*) null) == "");
- assert(to!string("foo\0".ptr) == "foo");
- }
- @safe pure unittest
- {
- // Conversion representing bool value with string
- debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded.");
- bool b;
- assert(to!string(b) == "false");
- b = true;
- assert(to!string(b) == "true");
- }
- @safe pure unittest
- {
- // Conversion representing character value with string
- debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded.");
- alias TypeTuple!(
- char, const( char), immutable( char),
- wchar, const(wchar), immutable(wchar),
- dchar, const(dchar), immutable(dchar)) AllChars;
- foreach (Char1; AllChars)
- {
- foreach (Char2; AllChars)
- {
- Char1 c = 'a';
- assert(to!(Char2[])(c)[0] == c);
- }
- uint x = 4;
- assert(to!(Char1[])(x) == "4");
- }
- string s = "foo";
- string s2;
- foreach (char c; s)
- {
- s2 ~= to!string(c);
- }
- //printf("%.*s", s2);
- assert(s2 == "foo");
- }
- @safe pure unittest
- {
- // Conversion representing integer values with string
- foreach (Int; TypeTuple!(ubyte, ushort, uint, ulong))
- {
- debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded.");
- debug(conv) printf("string.to!string(%.*s).unittest\n", Int.stringof.length, Int.stringof.ptr);
- assert(to!string(to!Int(0)) == "0");
- assert(to!string(to!Int(9)) == "9");
- assert(to!string(to!Int(123)) == "123");
- }
- foreach (Int; TypeTuple!(byte, short, int, long))
- {
- debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded.");
- debug(conv) printf("string.to!string(%.*s).unittest\n", Int.stringof.length, Int.stringof.ptr);
- assert(to!string(to!Int(0)) == "0");
- assert(to!string(to!Int(9)) == "9");
- assert(to!string(to!Int(123)) == "123");
- assert(to!string(to!Int(-0)) == "0");
- assert(to!string(to!Int(-9)) == "-9");
- assert(to!string(to!Int(-123)) == "-123");
- assert(to!string(to!(const Int)(6)) == "6");
- }
- debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded.");
- assert(wtext(int.max) == "2147483647"w);
- assert(wtext(int.min) == "-2147483648"w);
- assert(to!string(0L) == "0");
- assertCTFEable!(
- {
- assert(to!string(1uL << 62) == "4611686018427387904");
- assert(to!string(0x100000000) == "4294967296");
- assert(to!string(-138L) == "-138");
- });
- }
- @safe pure unittest
- {
- // Conversion representing dynamic/static array with string
- debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded.");
- long[] b = [ 1, 3, 5 ];
- auto s = to!string(b);
- assert(to!string(b) == "[1, 3, 5]", s);
- }
- /*@safe pure */unittest // sprintf issue
- {
- double[2] a = [ 1.5, 2.5 ];
- assert(to!string(a) == "[1.5, 2.5]");
- }
- /*@safe pure */unittest
- {
- // Conversion representing associative array with string
- int[string] a = ["0":1, "1":2];
- assert(to!string(a) == `["0":1, "1":2]`);
- }
- unittest
- {
- // Conversion representing class object with string
- debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded.");
- class A
- {
- override string toString() const { return "an A"; }
- }
- A a;
- assert(to!string(a) == "null");
- a = new A;
- assert(to!string(a) == "an A");
- // Bug 7660
- class C { override string toString() const { return "C"; } }
- struct S { C c; alias c this; }
- S s; s.c = new C();
- assert(to!string(s) == "C");
- }
- unittest
- {
- // Conversion representing struct object with string
- debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded.");
- struct S1
- {
- string toString() { return "wyda"; }
- }
- assert(to!string(S1()) == "wyda");
- struct S2
- {
- int a = 42;
- float b = 43.5;
- }
- S2 s2;
- assert(to!string(s2) == "S2(42, 43.5)");
- // Test for issue 8080
- struct S8080
- {
- short[4] data;
- alias data this;
- string toString() { return "<S>"; }
- }
- S8080 s8080;
- assert(to!string(s8080) == "<S>");
- }
- unittest
- {
- // Conversion representing enum value with string
- debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded.");
- enum EB : bool { a = true }
- enum EU : uint { a = 0, b = 1, c = 2 } // base type is unsigned
- enum EI : int { a = -1, b = 0, c = 1 } // base type is signed (bug 7909)
- enum EF : real { a = 1.414, b = 1.732, c = 2.236 }
- enum EC : char { a = 'x', b = 'y' }
- enum ES : string { a = "aaa", b = "bbb" }
- foreach (E; TypeTuple!(EB, EU, EI, EF, EC, ES))
- {
- assert(to! string(E.a) == "a"c);
- assert(to!wstring(E.a) == "a"w);
- assert(to!dstring(E.a) == "a"d);
- }
- // Test an value not corresponding to an enum member.
- auto o = cast(EU)5;
- assert(to! string(o) == "cast(EU)5"c);
- assert(to!wstring(o) == "cast(EU)5"w);
- assert(to!dstring(o) == "cast(EU)5"d);
- }
- unittest
- {
- enum E
- {
- foo,
- bar,
- doo = foo, // check duplicate switch statements
- }
- foreach (S; TypeTuple!(string, wstring, dstring, const(char[]), const(wchar[]), const(dchar[])))
- {
- auto s1 = to!S(E.foo);
- auto s2 = to!S(E.foo);
- assert(s1 == s2);
- // ensure we don't allocate when it's unnecessary
- assert(s1 is s2);
- }
- foreach (S; TypeTuple!(char[], wchar[], dchar[]))
- {
- auto s1 = to!S(E.foo);
- auto s2 = to!S(E.foo);
- assert(s1 == s2);
- // ensure each mutable array is unique
- assert(s1 !is s2);
- }
- }
- /// ditto
- @trusted pure T toImpl(T, S)(S value, uint radix, LetterCase letterCase = LetterCase.upper)
- if (isIntegral!S &&
- isExactSomeString!T)
- in
- {
- assert(radix >= 2 && radix <= 36);
- }
- body
- {
- alias EEType = Unqual!(ElementEncodingType!T);
- T toStringRadixConvert(size_t bufLen, uint radix = 0, bool neg = false)(uint runtimeRadix = 0)
- {
- static if (neg)
- ulong div = void, mValue = unsigned(-value);
- else
- Unsigned!(Unqual!S) div = void, mValue = unsigned(value);
- size_t index = bufLen;
- EEType[bufLen] buffer = void;
- char baseChar = letterCase == LetterCase.lower ? 'a' : 'A';
- char mod = void;
- do
- {
- static if (radix == 0)
- {
- div = cast(S)(mValue / runtimeRadix );
- mod = cast(ubyte)(mValue % runtimeRadix);
- mod += mod < 10 ? '0' : baseChar - 10;
- }
- else static if (radix > 10)
- {
- div = cast(S)(mValue / radix );
- mod = cast(ubyte)(mValue % radix);
- mod += mod < 10 ? '0' : baseChar - 10;
- }
- else
- {
- div = cast(S)(mValue / radix);
- mod = mValue % radix + '0';
- }
- buffer[--index] = cast(char)mod;
- mValue = div;
- } while (mValue);
- static if (neg)
- {
- buffer[--index] = '-';
- }
- return cast(T)buffer[index .. $].dup;
- }
- enforce(radix >= 2 && radix <= 36, new ConvException("Radix error"));
- switch(radix)
- {
- case 10:
- if (value < 0)
- return toStringRadixConvert!(S.sizeof * 3 + 1, 10, true)();
- else
- return toStringRadixConvert!(S.sizeof * 3, 10)();
- case 16:
- return toStringRadixConvert!(S.sizeof * 2, 16)();
- case 2:
- return toStringRadixConvert!(S.sizeof * 8, 2)();
- case 8:
- return toStringRadixConvert!(S.sizeof * 3, 8)();
- default:
- return toStringRadixConvert!(S.sizeof * 6)(radix);
- }
- }
- @safe pure unittest
- {
- foreach (Int; TypeTuple!(uint, ulong))
- {
- debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded.");
- debug(conv) printf("string.to!string(%.*s, uint).unittest\n", Int.stringof.length, Int.stringof.ptr);
- assert(to!string(to!Int(16), 16) == "10");
- assert(to!string(to!Int(15), 2u) == "1111");
- assert(to!string(to!Int(1), 2u) == "1");
- assert(to!string(to!Int(0x1234AF), 16u) == "1234AF");
- assert(to!string(to!Int(0x1234BCD), 16u, LetterCase.upper) == "1234BCD");
- assert(to!string(to!Int(0x1234AF), 16u, LetterCase.lower) == "1234af");
- }
- foreach (Int; TypeTuple!(int, long))
- {
- debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded.");
- debug(conv) printf("string.to!string(%.*s, uint).unittest\n", Int.stringof.length, Int.stringof.ptr);
- assert(to!string(to!Int(-10), 10u) == "-10");
- }
- assert(to!string(cast(byte)-10, 16) == "F6");
- assert(to!string(long.min) == "-9223372036854775808");
- assert(to!string(long.max) == "9223372036854775807");
- }
- /**
- Narrowing numeric-numeric conversions throw when the value does not
- fit in the narrower type.
- */
- T toImpl(T, S)(S value)
- if (!isImplicitlyConvertible!(S, T) &&
- (isNumeric!S || isSomeChar!S || isBoolean!S) &&
- (isNumeric!T || isSomeChar!T || isBoolean!T) && !is(T == enum))
- {
- enum sSmallest = mostNegative!S;
- enum tSmallest = mostNegative!T;
- static if (sSmallest < 0)
- {
- // possible underflow converting from a signed
- static if (tSmallest == 0)
- {
- immutable good = value >= 0;
- }
- else
- {
- static assert(tSmallest < 0);
- immutable good = value >= tSmallest;
- }
- if (!good)
- throw new ConvOverflowException("Conversion negative overflow");
- }
- static if (S.max > T.max)
- {
- // possible overflow
- if (value > T.max)
- throw new ConvOverflowException("Conversion positive overflow");
- }
- return (ref value)@trusted{ return cast(T) value; }(value);
- }
- @safe pure unittest
- {
- dchar a = ' ';
- assert(to!char(a) == ' ');
- a = 300;
- assert(collectException(to!char(a)));
- dchar from0 = 'A';
- char to0 = to!char(from0);
- wchar from1 = 'A';
- char to1 = to!char(from1);
- char from2 = 'A';
- char to2 = to!char(from2);
- char from3 = 'A';
- wchar to3 = to!wchar(from3);
- char from4 = 'A';
- dchar to4 = to!dchar(from4);
- }
- unittest
- {
- // Narrowing conversions from enum -> integral should be allowed, but they
- // should throw at runtime if the enum value doesn't fit in the target
- // type.
- enum E1 : ulong { A = 1, B = 1UL<<48, C = 0 }
- assert(to!int(E1.A) == 1);
- assert(to!bool(E1.A) == true);
- assertThrown!ConvOverflowException(to!int(E1.B)); // E1.B overflows int
- assertThrown!ConvOverflowException(to!bool(E1.B)); // E1.B overflows bool
- assert(to!bool(E1.C) == false);
- enum E2 : long { A = -1L<<48, B = -1<<31, C = 1<<31 }
- assertThrown!ConvOverflowException(to!int(E2.A)); // E2.A overflows int
- assertThrown!ConvOverflowException(to!uint(E2.B)); // E2.B overflows uint
- assert(to!int(E2.B) == -1<<31); // but does not overflow int
- assert(to!int(E2.C) == 1<<31); // E2.C does not overflow int
- enum E3 : int { A = -1, B = 1, C = 255, D = 0 }
- assertThrown!ConvOverflowException(to!ubyte(E3.A));
- assertThrown!ConvOverflowException(to!bool(E3.A));
- assert(to!byte(E3.A) == -1);
- assert(to!byte(E3.B) == 1);
- assert(to!ubyte(E3.C) == 255);
- assert(to!bool(E3.B) == true);
- assertThrown!ConvOverflowException(to!byte(E3.C));
- assertThrown!ConvOverflowException(to!bool(E3.C));
- assert(to!bool(E3.D) == false);
- }
- /**
- Array-to-array conversion (except when target is a string type)
- converts each element in turn by using $(D to).
- */
- T toImpl(T, S)(S value)
- if (!isImplicitlyConvertible!(S, T) &&
- !isSomeString!S && isDynamicArray!S &&
- !isExactSomeString!T && isArray!T)
- {
- alias E = typeof(T.init[0]);
- auto w = appender!(E[])();
- w.reserve(value.length);
- foreach (i, ref e; value)
- {
- w.put(to!E(e));
- }
- return w.data;
- }
- @safe pure unittest
- {
- // array to array conversions
- debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded.");
- uint[] a = ([ 1u, 2, 3 ]).dup;
- auto b = to!(float[])(a);
- assert(b == [ 1.0f, 2, 3 ]);
- //auto c = to!(string[])(b);
- //assert(c[0] == "1" && c[1] == "2" && c[2] == "3");
- immutable(int)[3] d = [ 1, 2, 3 ];
- b = to!(float[])(d);
- assert(b == [ 1.0f, 2, 3 ]);
- uint[][] e = [ a, a ];
- auto f = to!(float[][])(e);
- assert(f[0] == b && f[1] == b);
- // Test for bug 8264
- struct Wrap
- {
- string wrap;
- alias wrap this;
- }
- Wrap[] warr = to!(Wrap[])(["foo", "bar"]); // should work
- }
- /*@safe pure */unittest
- {
- auto b = [ 1.0f, 2, 3 ];
- auto c = to!(string[])(b);
- assert(c[0] == "1" && c[1] == "2" && c[2] == "3");
- }
- /**
- Associative array to associative array conversion converts each key
- and each value in turn.
- */
- T toImpl(T, S)(S value)
- if (isAssociativeArray!S &&
- isAssociativeArray!T && !is(T == enum))
- {
- /* This code is potentially unsafe.
- */
- alias KeyType!T K2;
- alias ValueType!T V2;
- // While we are "building" the AA, we need to unqualify its values, and only re-qualify at the end
- Unqual!V2[K2] result;
- foreach (k1, v1; value)
- {
- // Cast values temporarily to Unqual!V2 to store them to result variable
- result[to!K2(k1)] = cast(Unqual!V2) to!V2(v1);
- }
- // Cast back to original type
- return cast(T)result;
- }
- @safe /*pure */unittest
- {
- // hash to hash conversions
- int[string] a;
- a["0"] = 1;
- a["1"] = 2;
- auto b = to!(double[dstring])(a);
- assert(b["0"d] == 1 && b["1"d] == 2);
- }
- @safe /*pure */unittest // Bugzilla 8705, from doc
- {
- int[string][double[int[]]] a;
- auto b = to!(short[wstring][string[double[]]])(a);
- a = [null:["hello":int.max]];
- assertThrown!ConvOverflowException(to!(short[wstring][string[double[]]])(a));
- }
- version(none) // masked by unexpected linker error in posix platforms
- unittest // Extra cases for AA with qualifiers conversion
- {
- int[][int[]] a;// = [[], []];
- auto b = to!(immutable(short[])[immutable short[]])(a);
- double[dstring][int[long[]]] c;
- auto d = to!(immutable(short[immutable wstring])[immutable string[double[]]])(c);
- }
- private void testIntegralToFloating(Integral, Floating)()
- {
- Integral a = 42;
- auto b = to!Floating(a);
- assert(a == b);
- assert(a == to!Integral(b));
- }
- private void testFloatingToIntegral(Floating, Integral)()
- {
- bool convFails(Source, Target, E)(Source src)
- {
- try
- auto t = to!Target(src);
- catch (E)
- return true;
- return false;
- }
- // convert some value
- Floating a = 4.2e1;
- auto b = to!Integral(a);
- assert(is(typeof(b) == Integral) && b == 42);
- // convert some negative value (if applicable)
- a = -4.2e1;
- static if (Integral.min < 0)
- {
- b = to!Integral(a);
- assert(is(typeof(b) == Integral) && b == -42);
- }
- else
- {
- // no go for unsigned types
- assert(convFails!(Floating, Integral, ConvOverflowException)(a));
- }
- // convert to the smallest integral value
- a = 0.0 + Integral.min;
- static if (Integral.min < 0)
- {
- a = -a; // -Integral.min not representable as an Integral
- assert(convFails!(Floating, Integral, ConvOverflowException)(a)
- || Floating.sizeof <= Integral.sizeof);
- }
- a = 0.0 + Integral.min;
- assert(to!Integral(a) == Integral.min);
- --a; // no more representable as an Integral
- assert(convFails!(Floating, Integral, ConvOverflowException)(a)
- || Floating.sizeof <= Integral.sizeof);
- a = 0.0 + Integral.max;
- // fwritefln(stderr, "%s a=%g, %s conv=%s", Floating.stringof, a,
- // Integral.stringof, to!Integral(a));
- assert(to!Integral(a) == Integral.max || Floating.sizeof <= Integral.sizeof);
- ++a; // no more representable as an Integral
- assert(convFails!(Floating, Integral, ConvOverflowException)(a)
- || Floating.sizeof <= Integral.sizeof);
- // convert a value with a fractional part
- a = 3.14;
- assert(to!Integral(a) == 3);
- a = 3.99;
- assert(to!Integral(a) == 3);
- static if (Integral.min < 0)
- {
- a = -3.14;
- assert(to!Integral(a) == -3);
- a = -3.99;
- assert(to!Integral(a) == -3);
- }
- }
- @safe pure unittest
- {
- debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded.");
- alias AllInts = TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong);
- alias AllFloats = TypeTuple!(float, double, real);
- alias AllNumerics = TypeTuple!(AllInts, AllFloats);
- // test with same type
- {
- foreach (T; AllNumerics)
- {
- T a = 42;
- auto b = to!T(a);
- assert(is(typeof(a) == typeof(b)) && a == b);
- }
- }
- // test that floating-point numbers convert properly to largest ints
- // see http://oregonstate.edu/~peterseb/mth351/docs/351s2001_fp80x87.html
- // look for "largest fp integer with a predecessor"
- {
- // float
- int a = 16_777_215; // 2^24 - 1
- assert(to!int(to!float(a)) == a);
- assert(to!int(to!float(-a)) == -a);
- // double
- long b = 9_007_199_254_740_991; // 2^53 - 1
- assert(to!long(to!double(b)) == b);
- assert(to!long(to!double(-b)) == -b);
- // real
- // @@@ BUG IN COMPILER @@@
- // ulong c = 18_446_744_073_709_551_615UL; // 2^64 - 1
- // assert(to!ulong(to!real(c)) == c);
- // assert(to!ulong(-to!real(c)) == c);
- }
- // test conversions floating => integral
- {
- // AllInts[0 .. $ - 1] should be AllInts
- // @@@ BUG IN COMPILER @@@
- foreach (Integral; AllInts[0 .. $ - 1])
- {
- foreach (Floating; AllFloats)
- {
- testFloatingToIntegral!(Floating, Integral)();
- }
- }
- }
- // test conversion integral => floating
- {
- foreach (Integral; AllInts[0 .. $ - 1])
- {
- foreach (Floating; AllFloats)
- {
- testIntegralToFloating!(Integral, Floating)();
- }
- }
- }
- // test parsing
- {
- foreach (T; AllNumerics)
- {
- // from type immutable(char)[2]
- auto a = to!T("42");
- assert(a == 42);
- // from type char[]
- char[] s1 = "42".dup;
- a = to!T(s1);
- assert(a == 42);
- // from type char[2]
- char[2] s2;
- s2[] = "42";
- a = to!T(s2);
- assert(a == 42);
- // from type immutable(wchar)[2]
- a = to!T("42"w);
- assert(a == 42);
- }
- }
- }
- /*@safe pure */unittest
- {
- alias AllInts = TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong);
- alias AllFloats = TypeTuple!(float, double, real);
- alias AllNumerics = TypeTuple!(AllInts, AllFloats);
- // test conversions to string
- {
- foreach (T; AllNumerics)
- {
- T a = 42;
- assert(to!string(a) == "42");
- //assert(to!wstring(a) == "42"w);
- //assert(to!dstring(a) == "42"d);
- // array test
- // T[] b = new T[2];
- // b[0] = 42;
- // b[1] = 33;
- // assert(to!string(b) == "[42,33]");
- }
- }
- // test array to string conversion
- foreach (T ; AllNumerics)
- {
- auto a = [to!T(1), 2, 3];
- assert(to!string(a) == "[1, 2, 3]");
- }
- // test enum to int conversion
- // enum Testing { Test1, Test2 };
- // Testing t;
- // auto a = to!string(t);
- // assert(a == "0");
- }
- /**
- String to non-string conversion runs parsing.
- $(UL
- $(LI When the source is a wide string, it is first converted to a narrow
- string and then parsed.)
- $(LI When the source is a narrow string, normal text parsing occurs.))
- */
- T toImpl(T, S)(S value)
- if ( isExactSomeString!S && isDynamicArray!S &&
- !isExactSomeString!T && is(typeof(parse!T(value))))
- {
- scope(success)
- {
- if (value.length)
- {
- throw convError!(S, T)(value);
- }
- }
- return parse!T(value);
- }
- /// ditto
- T toImpl(T, S)(S value, uint radix)
- if ( isExactSomeString!S && isDynamicArray!S &&
- !isExactSomeString!T && is(typeof(parse!T(value, radix))))
- {
- scope(success)
- {
- if (value.length)
- {
- throw convError!(S, T)(value);
- }
- }
- return parse!T(value, radix);
- }
- @safe pure unittest
- {
- // Issue 6668 - ensure no collaterals thrown
- try { to!uint("-1"); }
- catch (ConvException e) { assert(e.next is null); }
- }
- @safe pure unittest
- {
- debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded.");
- foreach (Str; TypeTuple!(string, wstring, dstring))
- {
- Str a = "123";
- assert(to!int(a) == 123);
- assert(to!double(a) == 123);
- }
- // 6255
- auto n = to!int("FF", 16);
- assert(n == 255);
- }
- /**
- Convert a value that is implicitly convertible to the enum base type
- into an Enum value. If the value does not match any enum member values
- a ConvException is thrown.
- Enums with floating-point or string base types are not…