1 // Written in the D programming language.
4 A one-stop shop for converting values from one type to another.
6 $(SCRIPT inhibitQuickIndex = 1;)
8 $(TR $(TH Category) $(TH Functions))
9 $(TR $(TD Generic) $(TD
10 $(LREF asOriginalType)
17 $(TR $(TD Strings) $(TD
23 $(TR $(TD Numeric) $(TD
29 $(TR $(TD Exceptions) $(TD
31 $(LREF ConvOverflowException)
35 Copyright: Copyright Digital Mars 2007-.
37 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
39 Authors: $(HTTP digitalmars.com, Walter Bright),
40 $(HTTP erdani.org, Andrei Alexandrescu),
45 Source: $(PHOBOSSRC std/_conv.d)
50 public import std
.ascii
: LetterCase
;
53 import std
.range
.primitives
;
56 // Same as std.string.format, but "self-importing".
57 // Helps reduce code and imports, particularly in static asserts.
58 // Also helps with missing imports errors.
59 package template convFormat()
61 import std
.format
: format
;
62 alias convFormat
= format
;
65 /* ************* Exceptions *************** */
68 * Thrown on conversion errors.
70 class ConvException
: Exception
72 import std
.exception
: basicExceptionCtors
;
74 mixin basicExceptionCtors
;
77 private auto convError(S
, T
)(S source
, string fn
= __FILE__
, size_t ln
= __LINE__
)
82 msg
= "Unexpected end of input when converting from type " ~ S
.stringof
~ " to type " ~ T
.stringof
;
85 ElementType
!S el
= source
.front
;
88 msg
= text("Unexpected '\\n' when converting from type " ~ S
.stringof
~ " to type " ~ T
.stringof
);
90 msg
= text("Unexpected '", el
,
91 "' when converting from type " ~ S
.stringof
~ " to type " ~ T
.stringof
);
94 return new ConvException(msg
, fn
, ln
);
97 private auto convError(S
, T
)(S source
, int radix
, string fn
= __FILE__
, size_t ln
= __LINE__
)
102 msg
= text("Unexpected end of input when converting from type " ~ S
.stringof
~ " base ", radix
,
103 " to type " ~ T
.stringof
);
105 msg
= text("Unexpected '", source
.front
,
106 "' when converting from type " ~ S
.stringof
~ " base ", radix
,
107 " to type " ~ T
.stringof
);
109 return new ConvException(msg
, fn
, ln
);
112 @safe pure/* nothrow*/ // lazy parameter bug
113 private auto parseError(lazy string msg
, string fn
= __FILE__
, size_t ln
= __LINE__
)
115 return new ConvException(text("Can't parse string: ", msg
), fn
, ln
);
118 private void parseCheck(alias source
)(dchar c
, string fn
= __FILE__
, size_t ln
= __LINE__
)
121 throw parseError(text("unexpected end of input when expecting", "\"", c
, "\""));
122 if (source
.front
!= c
)
123 throw parseError(text("\"", c
, "\" is missing"), fn
, ln
);
132 // workaround for Bugzilla 14198
133 static if (is(S
== bool) && is(typeof({ T s
= "string"; })))
135 return src ?
"true" : "false";
139 import std
.array
: appender
;
140 import std
.format
: FormatSpec
, formatValue
;
142 auto w
= appender
!T();
143 FormatSpec
!(ElementEncodingType
!T
) f
;
144 formatValue(w
, src
, f
);
149 template isExactSomeString(T
)
151 enum isExactSomeString
= isSomeString
!T
&& !is(T
== enum);
154 template isEnumStrToStr(S
, T
)
156 enum isEnumStrToStr
= isImplicitlyConvertible
!(S
, T
) &&
157 is(S
== enum) && isExactSomeString
!T
;
159 template isNullToStr(S
, T
)
161 enum isNullToStr
= isImplicitlyConvertible
!(S
, T
) &&
162 (is(Unqual
!S
== typeof(null))) && isExactSomeString
!T
;
167 * Thrown on conversion overflow errors.
169 class ConvOverflowException
: ConvException
172 this(string s
, string fn
= __FILE__
, size_t ln
= __LINE__
)
179 The `to` template converts a value from one type _to another.
180 The source type is deduced and the target type must be specified, for example the
181 expression `to!int(42.0)` converts the number 42 from
182 `double` _to `int`. The conversion is "safe", i.e.,
183 it checks for overflow; `to!int(4.2e10)` would throw the
184 `ConvOverflowException` exception. Overflow checks are only
185 inserted when necessary, e.g., `to!double(42)` does not do
186 any checking because any `int` fits in a `double`.
188 Conversions from string _to numeric types differ from the C equivalents
189 `atoi()` and `atol()` by checking for overflow and not allowing whitespace.
191 For conversion of strings _to signed types, the grammar recognized is:
192 $(PRE $(I Integer): $(I Sign UnsignedInteger)
198 For conversion _to unsigned types, the grammar recognized is:
199 $(PRE $(I UnsignedInteger):
201 $(I DecimalDigit) $(I UnsignedInteger))
208 return toImpl
!T(args
);
215 return toImpl
!T(arg
);
220 if (isAggregateType
!S
&& !isCopyable
!S
)
222 return toImpl
!T(arg
);
227 * Converting a value _to its own type (useful mostly for generic code)
228 * simply returns its argument.
234 double c
= to
!double(3.14); // c is double with value 3.14
238 * Converting among numeric types is a safe way _to cast them around.
240 * Conversions from floating-point types _to integral types allow loss of
241 * precision (the fractional part of a floating-point number). The
242 * conversion is truncating towards zero, the same way a cast would
243 * truncate. (_To round a floating point value when casting _to an
244 * integral, use `roundTo`.)
248 import std
.exception
: assertThrown
;
251 assert(to
!long(a
) == a
);
252 assertThrown
!ConvOverflowException(to
!byte(a
));
254 assert(to
!int(4.2e6
) == 4200000);
255 assertThrown
!ConvOverflowException(to
!uint(-3.14));
256 assert(to
!uint(3.14) == 3);
257 assert(to
!uint(3.99) == 3);
258 assert(to
!int(-3.99) == -3);
262 * When converting strings _to numeric types, note that the D hexadecimal and binary
263 * literals are not handled. Neither the prefixes that indicate the base, nor the
264 * horizontal bar used _to separate groups of digits are recognized. This also
265 * applies to the suffixes that indicate the type.
267 * _To work around this, you can specify a radix for conversions involving numbers.
271 auto str = to
!string(42, 16);
273 auto i
= to
!int(str, 16);
278 * Conversions from integral types _to floating-point types always
279 * succeed, but might lose accuracy. The largest integers with a
280 * predecessor representable in floating-point format are `2^24-1` for
281 * `float`, `2^53-1` for `double`, and `2^64-1` for `real` (when
282 * `real` is 80-bit, e.g. on Intel machines).
286 // 2^24 - 1, largest proper integer representable as float
288 assert(to
!int(to
!float(a
)) == a
);
289 assert(to
!int(to
!float(-a
)) == -a
);
293 * Converting an array _to another array type works by converting each
294 * element in turn. Associative arrays can be converted _to associative
295 * arrays as long as keys and values can in turn be converted.
299 import std
.string
: split
;
302 auto b
= to
!(float[])(a
);
303 assert(b
== [1.0f, 2, 3]);
304 string
str = "1 2 3 4 5 6";
305 auto numbers
= to
!(double[])(split(str));
306 assert(numbers
== [1.0, 2, 3, 4, 5, 6]);
310 auto d
= to
!(double[wstring
])(c
);
311 assert(d
["a"w
] == 1 && d
["b"w
] == 2);
315 * Conversions operate transitively, meaning that they work on arrays and
316 * associative arrays of any complexity.
318 * This conversion works because `to!short` applies _to an `int`, `to!wstring`
319 * applies _to a `string`, `to!string` applies _to a `double`, and
320 * `to!(double[])` applies _to an `int[]`. The conversion might throw an
321 * exception because `to!short` might fail the range check.
325 int[string
][double[int[]]] a
;
326 auto b
= to
!(short[wstring
][string
[double[]]])(a
);
330 * Object-to-object conversions by dynamic casting throw exception when
331 * the source is non-null and the target is null.
335 import std
.exception
: assertThrown
;
336 // Testing object conversions
340 A a1
= new A
, a2
= new B
, a3
= new C
;
341 assert(to
!B(a2
) is a2
);
342 assert(to
!C(a3
) is a3
);
343 assertThrown
!ConvException(to
!B(a3
));
347 * Stringize conversion from all types is supported.
349 * $(LI String _to string conversion works for any two string types having
350 * ($(D char), $(D wchar), $(D dchar)) character widths and any
351 * combination of qualifiers (mutable, $(D const), or $(D immutable)).)
352 * $(LI Converts array (other than strings) _to string.
353 * Each element is converted by calling $(D to!T).)
354 * $(LI Associative array _to string conversion.
355 * Each element is printed by calling $(D to!T).)
356 * $(LI Object _to string conversion calls $(D toString) against the object or
357 * returns $(D "null") if the object is null.)
358 * $(LI Struct _to string conversion calls $(D toString) against the struct if
360 * $(LI For structs that do not define $(D toString), the conversion _to string
361 * produces the list of fields.)
362 * $(LI Enumerated types are converted _to strings as their symbolic names.)
363 * $(LI Boolean values are printed as $(D "true") or $(D "false").)
364 * $(LI $(D char), $(D wchar), $(D dchar) _to a string type.)
365 * $(LI Unsigned or signed integers _to strings.
366 * $(DL $(DT [special case])
367 * $(DD Convert integral value _to string in $(D_PARAM radix) radix.
368 * radix must be a value from 2 to 36.
369 * value is treated as a signed value only if radix is 10.
370 * The characters A through Z are used to represent values 10 through 36
371 * and their case is determined by the $(D_PARAM letterCase) parameter.)))
372 * $(LI All floating point types _to all string types.)
373 * $(LI Pointer to string conversions prints the pointer as a $(D size_t) value.
374 * If pointer is $(D char*), treat it as C-style strings.
375 * In that case, this function is $(D @system).))
377 @system pure unittest // @system due to cast and ptr
379 // Conversion representing dynamic/static array with string
380 long[] a
= [ 1, 3, 5 ];
381 assert(to
!string(a
) == "[1, 3, 5]");
383 // Conversion representing associative array with string
384 int[string
] associativeArray
= ["0":1, "1":2];
385 assert(to
!string(associativeArray
) == `["0":1, "1":2]` ||
386 to
!string(associativeArray
) == `["1":2, "0":1]`);
388 // char* to string conversion
389 assert(to
!string(cast(char*) null) == "");
390 assert(to
!string("foo\0".ptr
) == "foo");
392 // Conversion reinterpreting void array to string
395 assert(b
.length
== 8);
397 auto c
= to
!(wchar[])(b
);
401 // Tests for issue 6175
402 @safe pure nothrow unittest
404 char[9] sarr
= "blablabla";
405 auto darr
= to
!(char[])(sarr
);
406 assert(sarr
.ptr
== darr
.ptr
);
407 assert(sarr
.length
== darr
.length
);
410 // Tests for issue 7348
411 @safe pure /+nothrow+/ unittest
413 assert(to
!string(null) == "null");
414 assert(text(null) == "null");
417 // Tests for issue 11390
418 @safe pure /+nothrow+/ unittest
420 const(typeof(null)) ctn
;
421 immutable(typeof(null)) itn
;
422 assert(to
!string(ctn
) == "null");
423 assert(to
!string(itn
) == "null");
426 // Tests for issue 8729: do NOT skip leading WS
429 import std
.exception
;
430 foreach (T
; AliasSeq
!(byte, ubyte, short, ushort, int, uint, long, ulong))
432 assertThrown
!ConvException(to
!T(" 0"));
433 assertThrown
!ConvException(to
!T(" 0", 8));
435 foreach (T
; AliasSeq
!(float, double, real))
437 assertThrown
!ConvException(to
!T(" 0"));
440 assertThrown
!ConvException(to
!bool(" true"));
442 alias NullType
= typeof(null);
443 assertThrown
!ConvException(to
!NullType(" null"));
446 assertThrown
!ConvException(to
!ARR(" [1]"));
449 assertThrown
!ConvException(to
!AA(" [1:1]"));
453 If the source type is implicitly convertible to the target type, $(D
454 to) simply performs the implicit conversion.
456 private T
toImpl(T
, S
)(S value
)
457 if (isImplicitlyConvertible
!(S
, T
) &&
458 !isEnumStrToStr
!(S
, T
) && !isNullToStr
!(S
, T
))
460 template isSignedInt(T
)
462 enum isSignedInt
= isIntegral
!T
&& isSigned
!T
;
464 alias isUnsignedInt
= isUnsigned
;
466 // Conversion from integer to integer, and changing its sign
467 static if (isUnsignedInt
!S
&& isSignedInt
!T
&& S
.sizeof
== T
.sizeof
)
468 { // unsigned to signed & same size
469 import std
.exception
: enforce
;
470 enforce(value
<= cast(S
) T
.max
,
471 new ConvOverflowException("Conversion positive overflow"));
473 else static if (isSignedInt
!S
&& isUnsignedInt
!T
)
474 { // signed to unsigned
475 import std
.exception
: enforce
;
477 new ConvOverflowException("Conversion negative overflow"));
483 @safe pure nothrow unittest
485 enum E
{ a
} // Issue 9523 - Allow identity enum conversion
490 @safe pure nothrow unittest
497 // Tests for issue 6377
500 import std
.exception
;
501 // Conversion between same size
502 foreach (S
; AliasSeq
!(byte, short, int, long))
503 (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
504 alias U
= Unsigned
!S
;
506 foreach (Sint
; AliasSeq
!(S
, const S
, immutable S
))
507 foreach (Uint
; AliasSeq
!(U
, const U
, immutable U
))
511 assertThrown
!ConvOverflowException(to
!Sint(un
),
512 text(Sint
.stringof
, ' ', Uint
.stringof
, ' ', un
));
516 assertThrown
!ConvOverflowException(to
!Uint(sn
),
517 text(Sint
.stringof
, ' ', Uint
.stringof
, ' ', un
));
521 // Conversion between different size
522 foreach (i
, S1
; AliasSeq
!(byte, short, int, long))
523 foreach ( S2
; AliasSeq
!(byte, short, int, long)[i
+1..$])
524 (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
525 alias U1
= Unsigned
!S1
;
526 alias U2
= Unsigned
!S2
;
528 static assert(U1
.sizeof
< S2
.sizeof
);
530 // small unsigned to big signed
531 foreach (Uint
; AliasSeq
!(U1
, const U1
, immutable U1
))
532 foreach (Sint
; AliasSeq
!(S2
, const S2
, immutable S2
))
535 assertNotThrown(to
!Sint(un
));
536 assert(to
!Sint(un
) == un
);
539 // big unsigned to small signed
540 foreach (Uint
; AliasSeq
!(U2
, const U2
, immutable U2
))
541 foreach (Sint
; AliasSeq
!(S1
, const S1
, immutable S1
))
544 assertThrown(to
!Sint(un
));
547 static assert(S1
.sizeof
< U2
.sizeof
);
549 // small signed to big unsigned
550 foreach (Sint
; AliasSeq
!(S1
, const S1
, immutable S1
))
551 foreach (Uint
; AliasSeq
!(U2
, const U2
, immutable U2
))
554 assertThrown
!ConvOverflowException(to
!Uint(sn
));
557 // big signed to small unsigned
558 foreach (Sint
; AliasSeq
!(S2
, const S2
, immutable S2
))
559 foreach (Uint
; AliasSeq
!(U1
, const U1
, immutable U1
))
562 assertThrown
!ConvOverflowException(to
!Uint(sn
));
568 Converting static arrays forwards to their dynamic counterparts.
570 private T
toImpl(T
, S
)(ref S s
)
573 return toImpl
!(T
, typeof(s
[0])[])(s
);
576 @safe pure nothrow unittest
578 char[4] test = ['a', 'b', 'c', 'd'];
579 static assert(!isInputRange
!(Unqual
!(char[4])));
580 assert(to
!string(test) == test);
584 When source type supports member template function opCast, it is used.
586 private T
toImpl(T
, S
)(S value
)
587 if (!isImplicitlyConvertible
!(S
, T
) &&
588 is(typeof(S
.init
.opCast
!T()) : T
) &&
589 !isExactSomeString
!T
&&
590 !is(typeof(T(value
))))
592 return value
.opCast
!T();
601 this(S s
) @safe pure { }
605 T
opCast(U
)() @safe pure { assert(false); }
608 cast(void) to
!(Test
.T
)(Test
.S());
610 // make sure std.conv.to is doing the same thing as initialization
619 T
opCast(T
)() { return 43; }
622 assert(to
!int(b
) == 43);
626 T
opCast(T
)() { return 43; }
629 assert(to
!int(s
) == 43);
633 When target type supports 'converting construction', it is used.
634 $(UL $(LI If target type is struct, $(D T(value)) is used.)
635 $(LI If target type is class, $(D new T(value)) is used.))
637 private T
toImpl(T
, S
)(S value
)
638 if (!isImplicitlyConvertible
!(S
, T
) &&
639 is(T
== struct) && is(typeof(T(value
))))
656 this(int x
) @safe pure { this.x
= x
; }
658 Int2 i2
= to
!Int2(1);
663 static Int3
opCall(int x
) @safe pure
670 Int3 i3
= to
!Int3(1);
676 static struct FakeBigInt
678 this(string s
) @safe pure {}
682 auto i3
= to
!FakeBigInt(s
);
686 private T
toImpl(T
, S
)(S value
)
687 if (!isImplicitlyConvertible
!(S
, T
) &&
688 is(T
== class) && is(typeof(new T(value
))))
702 this(int x
) @safe pure { this.x
= x
; }
708 this(S src
) @safe pure { value
= src
.x
; }
709 this(C src
) @safe pure { value
= src
.x
; }
713 auto b1
= to
!B(s
); // == new B(s)
714 assert(b1
.value
== 1);
717 auto b2
= to
!B(c
); // == new B(c)
718 assert(b2
.value
== 2);
720 auto c2
= to
!C(3); // == new C(3)
730 this(B b
) @safe pure {}
734 this() @safe pure { super(this); }
739 S
.A a
= to
!(S
.A
)(b
); // == cast(S.A) b
740 // (do not run construction conversion like new S.A(b))
743 static class C
: Object
746 this(Object o
) @safe pure {}
750 C a2
= to
!C(oc
); // == new C(a)
751 // Construction conversion overrides down-casting conversion
756 Object-to-object conversions by dynamic casting throw exception when the source is
757 non-null and the target is null.
759 private T
toImpl(T
, S
)(S value
)
760 if (!isImplicitlyConvertible
!(S
, T
) &&
761 (is(S
== class) ||
is(S
== interface)) && !is(typeof(value
.opCast
!T()) : T
) &&
762 (is(T
== class) ||
is(T
== interface)) && !is(typeof(new T(value
))))
764 static if (is(T
== immutable))
766 // immutable <- immutable
767 enum isModConvertible
= is(S
== immutable);
769 else static if (is(T
== const))
771 static if (is(T
== shared))
773 // shared const <- shared
774 // shared const <- shared const
775 // shared const <- immutable
776 enum isModConvertible
= is(S
== shared) ||
is(S
== immutable);
781 // const <- immutable
782 enum isModConvertible
= !is(S
== shared);
787 static if (is(T
== shared))
789 // shared <- shared mutable
790 enum isModConvertible
= is(S
== shared) && !is(S
== const);
794 // (mutable) <- (mutable)
795 enum isModConvertible
= is(Unqual
!S
== S
);
798 static assert(isModConvertible
, "Bad modifier conversion: "~S
.stringof
~" to "~T
.stringof
);
800 auto result
= ()@trusted{ return cast(T
) value
; }();
801 if (!result
&& value
)
803 throw new ConvException("Cannot convert object of static type "
804 ~S
.classinfo
.name
~" and dynamic type "~value
.classinfo
.name
805 ~" to type "~T
.classinfo
.name
);
813 import std
.exception
;
815 alias Identity(T
) = T
;
816 alias toConst(T
) = const T
;
817 alias toShared(T
) = shared T
;
818 alias toSharedConst(T
) = shared const T
;
819 alias toImmutable(T
) = immutable T
;
820 template AddModifier(int n
)
823 static if (n
== 0) alias AddModifier
= Identity
;
824 else static if (n
== 1) alias AddModifier
= toConst
;
825 else static if (n
== 2) alias AddModifier
= toShared
;
826 else static if (n
== 3) alias AddModifier
= toSharedConst
;
827 else static if (n
== 4) alias AddModifier
= toImmutable
;
838 foreach (m1
; AliasSeq
!(0,1,2,3,4)) // enumerate modifiers
839 foreach (m2
; AliasSeq
!(0,1,2,3,4)) // ditto
840 (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
841 alias srcmod
= AddModifier
!m1
;
842 alias tgtmod
= AddModifier
!m2
;
844 // Compile time convertible equals to modifier convertible.
845 static if (isImplicitlyConvertible
!(srcmod
!Object
, tgtmod
!Object
))
847 // Test runtime conversions: class to class, class to interface,
848 // interface to class, and interface to interface
850 // Check that the runtime conversion to succeed
851 srcmod
!A ac
= new srcmod
!C();
852 srcmod
!I ic
= new srcmod
!C();
853 assert(to
!(tgtmod
!C
)(ac
) !is null); // A(c) to C
854 assert(to
!(tgtmod
!I
)(ac
) !is null); // A(c) to I
855 assert(to
!(tgtmod
!C
)(ic
) !is null); // I(c) to C
856 assert(to
!(tgtmod
!J
)(ic
) !is null); // I(c) to J
858 // Check that the runtime conversion fails
859 srcmod
!A ab
= new srcmod
!B();
860 srcmod
!I id
= new srcmod
!D();
861 assertThrown(to
!(tgtmod
!C
)(ab
)); // A(b) to C
862 assertThrown(to
!(tgtmod
!I
)(ab
)); // A(b) to I
863 assertThrown(to
!(tgtmod
!C
)(id
)); // I(d) to C
864 assertThrown(to
!(tgtmod
!J
)(id
)); // I(d) to J
868 // Check that the conversion is rejected statically
869 static assert(!is(typeof(to
!(tgtmod
!C
)(srcmod
!A
.init
)))); // A to C
870 static assert(!is(typeof(to
!(tgtmod
!I
)(srcmod
!A
.init
)))); // A to I
871 static assert(!is(typeof(to
!(tgtmod
!C
)(srcmod
!I
.init
)))); // I to C
872 static assert(!is(typeof(to
!(tgtmod
!J
)(srcmod
!I
.init
)))); // I to J
878 Handles type _to string conversions
880 private T
toImpl(T
, S
)(S value
)
881 if (!(isImplicitlyConvertible
!(S
, T
) &&
882 !isEnumStrToStr
!(S
, T
) && !isNullToStr
!(S
, T
)) &&
883 !isInfinite
!S
&& isExactSomeString
!T
)
885 static if (isExactSomeString
!S
&& value
[0].sizeof
== ElementEncodingType
!T
.sizeof
)
887 // string-to-string with incompatible qualifier conversion
888 static if (is(ElementEncodingType
!T
== immutable))
890 // conversion (mutable|const) -> immutable
895 // conversion (immutable|const) -> mutable
899 else static if (isExactSomeString
!S
)
901 import std
.array
: appender
;
902 // other string-to-string
903 //Use Appender directly instead of toStr, which also uses a formatedWrite
904 auto w
= appender
!T();
908 else static if (isIntegral
!S
&& !is(S
== enum))
910 // other integral-to-string conversions with default radix
911 return toImpl
!(T
, S
)(value
, 10);
913 else static if (is(S
== void[]) ||
is(S
== const(void)[]) ||
is(S
== immutable(void)[]))
915 import core
.stdc
.string
: memcpy
;
916 import std
.exception
: enforce
;
917 // Converting void array to string
918 alias Char
= Unqual
!(ElementEncodingType
!T
);
919 auto raw
= cast(const(ubyte)[]) value
;
920 enforce(raw
.length
% Char
.sizeof
== 0,
921 new ConvException("Alignment mismatch in converting a "
922 ~ S
.stringof
~ " to a "
924 auto result
= new Char
[raw
.length
/ Char
.sizeof
];
925 ()@trusted{ memcpy(result
.ptr
, value
.ptr
, value
.length
); }();
926 return cast(T
) result
;
928 else static if (isPointer
!S
&& isSomeChar
!(PointerTarget
!S
))
930 // This is unsafe because we cannot guarantee that the pointer is null terminated.
932 static if (is(S
: const(char)*))
933 import core
.stdc
.string
: strlen
;
935 size_t
strlen(S s
) nothrow
941 return toImpl
!T(value ? value
[0 .. strlen(value
)].dup
: null);
944 else static if (isSomeString
!T
&& is(S
== enum))
946 static if (isSwitchable
!(OriginalType
!S
) && EnumMembers
!S
.length
<= 50)
950 foreach (member
; NoDuplicates
!(EnumMembers
!S
))
953 return to
!T(enumRep
!(immutable(T
), S
, member
));
960 foreach (member
; EnumMembers
!S
)
963 return to
!T(enumRep
!(immutable(T
), S
, member
));
967 import std
.array
: appender
;
968 import std
.format
: FormatSpec
, formatValue
;
970 //Default case, delegate to format
971 //Note: we don't call toStr directly, to avoid duplicate work.
972 auto app
= appender
!T();
973 app
.put("cast(" ~ S
.stringof
~ ")");
975 formatValue(app
, cast(OriginalType
!S
) value
, f
);
980 // other non-string values runs formatting
981 return toStr
!T(value
);
988 immutable(char)* ptr
= "hello".ptr
;
989 auto result
= ptr
.to
!(char[]);
994 void test1(T
)(T lp
, string
cmp)
996 foreach (e
; AliasSeq
!(char, wchar, dchar))
998 test2
!(e
[])(lp
, cmp);
999 test2
!(const(e
)[])(lp
, cmp);
1000 test2
!(immutable(e
)[])(lp
, cmp);
1004 void test2(D
, S
)(S lp
, string
cmp)
1006 assert(to
!string(to
!D(lp
)) == cmp);
1009 foreach (e
; AliasSeq
!("Hello, world!", "Hello, world!"w
, "Hello, world!"d
))
1011 test1(e
, "Hello, world!");
1012 test1(e
.ptr
, "Hello, world!");
1014 foreach (e
; AliasSeq
!("", ""w
, ""d
))
1022 To string conversion for non copy-able structs
1024 private T
toImpl(T
, S
)(ref S value
)
1025 if (!(isImplicitlyConvertible
!(S
, T
) &&
1026 !isEnumStrToStr
!(S
, T
) && !isNullToStr
!(S
, T
)) &&
1027 !isInfinite
!S
&& isExactSomeString
!T
&& !isCopyable
!S
)
1029 import std
.array
: appender
;
1030 import std
.format
: FormatSpec
, formatValue
;
1032 auto w
= appender
!T();
1033 FormatSpec
!(ElementEncodingType
!T
) f
;
1034 formatValue(w
, value
, f
);
1046 string
toString() { return text(val
, ":", flag
); }
1048 @disable this(this);
1052 assert(to
!string(a
) == "0:false");
1059 @disable this(this);
1063 assert(to
!string(b
) == "B(0, false)");
1067 Check whether type $(D T) can be used in a switch statement.
1068 This is useful for compile-time generation of switch case statements.
1070 private template isSwitchable(E
)
1072 enum bool isSwitchable
= is(typeof({
1073 switch (E
.init
) { default: }
1080 static assert(isSwitchable
!int);
1081 static assert(!isSwitchable
!double);
1082 static assert(!isSwitchable
!real);
1085 //Static representation of the index I of the enum S,
1086 //In representation T.
1087 //T must be an immutable string (avoids un-necessary initializations).
1088 private template enumRep(T
, S
, S value
)
1089 if (is (T
== immutable) && isExactSomeString
!T
&& is(S
== enum))
1091 static T enumRep
= toStr
!T(value
);
1096 import std
.exception
;
1099 // string to string conversion
1100 alias Chars
= AliasSeq
!(char, wchar, dchar);
1101 foreach (LhsC
; Chars
)
1103 alias LhStrings
= AliasSeq
!(LhsC
[], const(LhsC
)[], immutable(LhsC
)[]);
1104 foreach (Lhs
; LhStrings
)
1106 foreach (RhsC
; Chars
)
1108 alias RhStrings
= AliasSeq
!(RhsC
[], const(RhsC
)[], immutable(RhsC
)[]);
1109 foreach (Rhs
; RhStrings
)
1111 Lhs s1
= to
!Lhs("wyda");
1112 Rhs s2
= to
!Rhs(s1
);
1113 //writeln(Lhs.stringof, " -> ", Rhs.stringof);
1114 assert(s1
== to
!Lhs(s2
));
1124 T
[] s1
= to
!(T
[])("Hello, world!");
1125 auto s2
= to
!(U
[])(s1
);
1126 assert(s1
== to
!(T
[])(s2
));
1127 auto s3
= to
!(const(U
)[])(s1
);
1128 assert(s1
== to
!(T
[])(s3
));
1129 auto s4
= to
!(immutable(U
)[])(s1
);
1130 assert(s1
== to
!(T
[])(s4
));
1140 // Conversion representing bool value with string
1142 assert(to
!string(b
) == "false");
1144 assert(to
!string(b
) == "true");
1149 // Conversion representing character value with string
1151 AliasSeq
!( char, const( char), immutable( char),
1152 wchar, const(wchar), immutable(wchar),
1153 dchar, const(dchar), immutable(dchar));
1154 foreach (Char1
; AllChars
)
1156 foreach (Char2
; AllChars
)
1159 assert(to
!(Char2
[])(c
)[0] == c
);
1162 assert(to
!(Char1
[])(x
) == "4");
1171 assert(s2
== "foo");
1174 @safe pure nothrow unittest
1176 import std
.exception
;
1177 // Conversion representing integer values with string
1179 foreach (Int
; AliasSeq
!(ubyte, ushort, uint, ulong))
1181 assert(to
!string(Int(0)) == "0");
1182 assert(to
!string(Int(9)) == "9");
1183 assert(to
!string(Int(123)) == "123");
1186 foreach (Int
; AliasSeq
!(byte, short, int, long))
1188 assert(to
!string(Int(0)) == "0");
1189 assert(to
!string(Int(9)) == "9");
1190 assert(to
!string(Int(123)) == "123");
1191 assert(to
!string(Int(-0)) == "0");
1192 assert(to
!string(Int(-9)) == "-9");
1193 assert(to
!string(Int(-123)) == "-123");
1194 assert(to
!string(const(Int
)(6)) == "6");
1197 assert(wtext(int.max
) == "2147483647"w
);
1198 assert(wtext(int.min
) == "-2147483648"w
);
1199 assert(to
!string(0L) == "0");
1203 assert(to
!string(1uL << 62) == "4611686018427387904");
1204 assert(to
!string(0x100000000) == "4294967296");
1205 assert(to
!string(-138L) == "-138");
1209 @safe unittest // sprintf issue
1211 double[2] a
= [ 1.5, 2.5 ];
1212 assert(to
!string(a
) == "[1.5, 2.5]");
1217 // Conversion representing class object with string
1220 override string
toString() const { return "an A"; }
1223 assert(to
!string(a
) == "null");
1225 assert(to
!string(a
) == "an A");
1228 class C
{ override string
toString() const { return "C"; } }
1229 struct S
{ C c
; alias c
this; }
1231 assert(to
!string(s
) == "C");
1236 // Conversion representing struct object with string
1239 string
toString() { return "wyda"; }
1241 assert(to
!string(S1()) == "wyda");
1249 assert(to
!string(s2
) == "S2(42, 43.5)");
1251 // Test for issue 8080
1256 string
toString() { return "<S>"; }
1259 assert(to
!string(s8080
) == "<S>");
1264 // Conversion representing enum value with string
1265 enum EB
: bool { a
= true }
1266 enum EU
: uint { a
= 0, b
= 1, c
= 2 } // base type is unsigned
1267 enum EI
: int { a
= -1, b
= 0, c
= 1 } // base type is signed (bug 7909)
1268 enum EF
: real { a
= 1.414, b
= 1.732, c
= 2.236 }
1269 enum EC
: char { a
= 'x', b
= 'y' }
1270 enum ES
: string
{ a
= "aaa", b
= "bbb" }
1272 foreach (E
; AliasSeq
!(EB
, EU
, EI
, EF
, EC
, ES
))
1274 assert(to
! string(E
.a
) == "a"c
);
1275 assert(to
!wstring(E
.a
) == "a"w
);
1276 assert(to
!dstring(E
.a
) == "a"d
);
1279 // Test an value not corresponding to an enum member.
1280 auto o
= cast(EU
) 5;
1281 assert(to
! string(o
) == "cast(EU)5"c
);
1282 assert(to
!wstring(o
) == "cast(EU)5"w
);
1283 assert(to
!dstring(o
) == "cast(EU)5"d
);
1291 doo
= foo
, // check duplicate switch statements
1295 //Test regression 12494
1296 assert(to
!string(E
.foo
) == "foo");
1297 assert(to
!string(E
.doo
) == "foo");
1298 assert(to
!string(E
.bar
) == "bar");
1300 foreach (S
; AliasSeq
!(string
, wstring
, dstring
, const(char[]), const(wchar[]), const(dchar[])))
1302 auto s1
= to
!S(E
.foo
);
1303 auto s2
= to
!S(E
.foo
);
1305 // ensure we don't allocate when it's unnecessary
1309 foreach (S
; AliasSeq
!(char[], wchar[], dchar[]))
1311 auto s1
= to
!S(E
.foo
);
1312 auto s2
= to
!S(E
.foo
);
1314 // ensure each mutable array is unique
1320 @trusted pure private T
toImpl(T
, S
)(S value
, uint radix
, LetterCase letterCase
= LetterCase
.upper
)
1322 isExactSomeString
!T
)
1325 assert(radix
>= 2 && radix
<= 36);
1329 alias EEType
= Unqual
!(ElementEncodingType
!T
);
1331 T
toStringRadixConvert(size_t bufLen
)(uint runtimeRadix
= 0)
1333 Unsigned
!(Unqual
!S
) div = void, mValue
= unsigned(value
);
1335 size_t index
= bufLen
;
1336 EEType
[bufLen
] buffer
= void;
1337 char baseChar
= letterCase
== LetterCase
.lower ?
'a' : 'A';
1342 div = cast(S
)(mValue
/ runtimeRadix
);
1343 mod
= cast(ubyte)(mValue
% runtimeRadix
);
1344 mod
+= mod
< 10 ?
'0' : baseChar
- 10;
1345 buffer
[--index
] = cast(char) mod
;
1349 return cast(T
) buffer
[index
.. $].dup
;
1352 import std
.array
: array
;
1356 // The (value+0) is so integral promotions happen to the type
1357 return toChars
!(10, EEType
)(value
+ 0).array
;
1359 // The unsigned(unsigned(value)+0) is so unsigned integral promotions happen to the type
1360 if (letterCase
== letterCase
.upper
)
1361 return toChars
!(16, EEType
, LetterCase
.upper
)(unsigned(unsigned(value
) + 0)).array
;
1363 return toChars
!(16, EEType
, LetterCase
.lower
)(unsigned(unsigned(value
) + 0)).array
;
1365 return toChars
!(2, EEType
)(unsigned(unsigned(value
) + 0)).array
;
1367 return toChars
!(8, EEType
)(unsigned(unsigned(value
) + 0)).array
;
1370 return toStringRadixConvert
!(S
.sizeof
* 6)(radix
);
1374 @safe pure nothrow unittest
1376 foreach (Int
; AliasSeq
!(uint, ulong))
1378 assert(to
!string(Int(16), 16) == "10");
1379 assert(to
!string(Int(15), 2u) == "1111");
1380 assert(to
!string(Int(1), 2u) == "1");
1381 assert(to
!string(Int(0x1234AF), 16u) == "1234AF");
1382 assert(to
!string(Int(0x1234BCD), 16u, LetterCase
.upper
) == "1234BCD");
1383 assert(to
!string(Int(0x1234AF), 16u, LetterCase
.lower
) == "1234af");
1386 foreach (Int
; AliasSeq
!(int, long))
1388 assert(to
!string(Int(-10), 10u) == "-10");
1391 assert(to
!string(byte(-10), 16) == "F6");
1392 assert(to
!string(long.min
) == "-9223372036854775808");
1393 assert(to
!string(long.max
) == "9223372036854775807");
1397 Narrowing numeric-numeric conversions throw when the value does not
1398 fit in the narrower type.
1400 private T
toImpl(T
, S
)(S value
)
1401 if (!isImplicitlyConvertible
!(S
, T
) &&
1402 (isNumeric
!S || isSomeChar
!S || isBoolean
!S
) &&
1403 (isNumeric
!T || isSomeChar
!T || isBoolean
!T
) && !is(T
== enum))
1405 enum sSmallest
= mostNegative
!S
;
1406 enum tSmallest
= mostNegative
!T
;
1407 static if (sSmallest
< 0)
1409 // possible underflow converting from a signed
1410 static if (tSmallest
== 0)
1412 immutable good
= value
>= 0;
1416 static assert(tSmallest
< 0);
1417 immutable good
= value
>= tSmallest
;
1420 throw new ConvOverflowException("Conversion negative overflow");
1422 static if (S
.max
> T
.max
)
1424 // possible overflow
1426 throw new ConvOverflowException("Conversion positive overflow");
1428 return (ref value
)@trusted{ return cast(T
) value
; }(value
);
1433 import std
.exception
;
1436 assert(to
!char(a
) == ' ');
1438 assert(collectException(to
!char(a
)));
1441 char to0
= to
!char(from0
);
1444 char to1
= to
!char(from1
);
1447 char to2
= to
!char(from2
);
1450 wchar to3
= to
!wchar(from3
);
1453 dchar to4
= to
!dchar(from4
);
1458 import std
.exception
;
1460 // Narrowing conversions from enum -> integral should be allowed, but they
1461 // should throw at runtime if the enum value doesn't fit in the target
1463 enum E1
: ulong { A
= 1, B
= 1UL << 48, C
= 0 }
1464 assert(to
!int(E1
.A
) == 1);
1465 assert(to
!bool(E1
.A
) == true);
1466 assertThrown
!ConvOverflowException(to
!int(E1
.B
)); // E1.B overflows int
1467 assertThrown
!ConvOverflowException(to
!bool(E1
.B
)); // E1.B overflows bool
1468 assert(to
!bool(E1
.C
) == false);
1470 enum E2
: long { A
= -1L << 48, B
= -1 << 31, C
= 1 << 31 }
1471 assertThrown
!ConvOverflowException(to
!int(E2
.A
)); // E2.A overflows int
1472 assertThrown
!ConvOverflowException(to
!uint(E2
.B
)); // E2.B overflows uint
1473 assert(to
!int(E2
.B
) == -1 << 31); // but does not overflow int
1474 assert(to
!int(E2
.C
) == 1 << 31); // E2.C does not overflow int
1476 enum E3
: int { A
= -1, B
= 1, C
= 255, D
= 0 }
1477 assertThrown
!ConvOverflowException(to
!ubyte(E3
.A
));
1478 assertThrown
!ConvOverflowException(to
!bool(E3
.A
));
1479 assert(to
!byte(E3
.A
) == -1);
1480 assert(to
!byte(E3
.B
) == 1);
1481 assert(to
!ubyte(E3
.C
) == 255);
1482 assert(to
!bool(E3
.B
) == true);
1483 assertThrown
!ConvOverflowException(to
!byte(E3
.C
));
1484 assertThrown
!ConvOverflowException(to
!bool(E3
.C
));
1485 assert(to
!bool(E3
.D
) == false);
1490 Array-to-array conversion (except when target is a string type)
1491 converts each element in turn by using $(D to).
1493 private T
toImpl(T
, S
)(S value
)
1494 if (!isImplicitlyConvertible
!(S
, T
) &&
1495 !isSomeString
!S
&& isDynamicArray
!S
&&
1496 !isExactSomeString
!T
&& isArray
!T
)
1498 alias E
= typeof(T
.init
[0]);
1500 static if (isStaticArray
!T
)
1502 import std
.exception
: enforce
;
1503 auto res
= to
!(E
[])(value
);
1504 enforce
!ConvException(T
.length
== res
.length
,
1505 convFormat("Length mismatch when converting to static array: %s vs %s", T
.length
, res
.length
));
1506 return res
[0 .. T
.length
];
1510 import std
.array
: appender
;
1511 auto w
= appender
!(E
[])();
1512 w
.reserve(value
.length
);
1513 foreach (i
, ref e
; value
)
1523 import std
.exception
;
1525 // array to array conversions
1526 uint[] a
= [ 1u, 2, 3 ];
1527 auto b
= to
!(float[])(a
);
1528 assert(b
== [ 1.0f, 2, 3 ]);
1530 immutable(int)[3] d
= [ 1, 2, 3 ];
1531 b
= to
!(float[])(d
);
1532 assert(b
== [ 1.0f, 2, 3 ]);
1534 uint[][] e
= [ a
, a
];
1535 auto f
= to
!(float[][])(e
);
1536 assert(f
[0] == b
&& f
[1] == b
);
1538 // Test for bug 8264
1544 Wrap
[] warr
= to
!(Wrap
[])(["foo", "bar"]); // should work
1547 import std
.conv
: to
;
1548 const s2
= ["10", "20"];
1550 immutable int[2] a3
= s2
.to
!(int[2]);
1551 assert(a3
== [10, 20]);
1553 // verify length mismatches are caught
1554 immutable s4
= [1, 2, 3, 4];
1557 auto ex
= collectException(s4
[0 .. i
].to
!(int[2]));
1558 assert(ex
&& ex
.msg
== "Length mismatch when converting to static array: 2 vs " ~ [cast(char)(i
+ '0')],
1559 ex ? ex
.msg
: "Exception was not thrown!");
1565 auto b
= [ 1.0f, 2, 3 ];
1567 auto c
= to
!(string
[])(b
);
1568 assert(c
[0] == "1" && c
[1] == "2" && c
[2] == "3");
1572 Associative array to associative array conversion converts each key
1573 and each value in turn.
1575 private T
toImpl(T
, S
)(S value
)
1576 if (isAssociativeArray
!S
&&
1577 isAssociativeArray
!T
&& !is(T
== enum))
1579 /* This code is potentially unsafe.
1581 alias K2
= KeyType
!T
;
1582 alias V2
= ValueType
!T
;
1584 // While we are "building" the AA, we need to unqualify its values, and only re-qualify at the end
1585 Unqual
!V2
[K2
] result
;
1587 foreach (k1
, v1
; value
)
1589 // Cast values temporarily to Unqual!V2 to store them to result variable
1590 result
[to
!K2(k1
)] = cast(Unqual
!V2
) to
!V2(v1
);
1592 // Cast back to original type
1593 return cast(T
) result
;
1598 // hash to hash conversions
1602 auto b
= to
!(double[dstring
])(a
);
1603 assert(b
["0"d
] == 1 && b
["1"d
] == 2);
1605 @safe unittest // Bugzilla 8705, from doc
1607 import std
.exception
;
1608 int[string
][double[int[]]] a
;
1609 auto b
= to
!(short[wstring
][string
[double[]]])(a
);
1610 a
= [null:["hello":int.max
]];
1611 assertThrown
!ConvOverflowException(to
!(short[wstring
][string
[double[]]])(a
));
1613 @system unittest // Extra cases for AA with qualifiers conversion
1615 int[][int[]] a
;// = [[], []];
1616 auto b
= to
!(immutable(short[])[immutable short[]])(a
);
1618 double[dstring
][int[long[]]] c
;
1619 auto d
= to
!(immutable(short[immutable wstring
])[immutable string
[double[]]])(c
);
1622 private void testIntegralToFloating(Integral
, Floating
)()
1625 auto b
= to
!Floating(a
);
1627 assert(a
== to
!Integral(b
));
1630 private void testFloatingToIntegral(Floating
, Integral
)()
1632 import std
.math
: floatTraits
, RealFormat
;
1634 bool convFails(Source
, Target
, E
)(Source src
)
1637 auto t
= to
!Target(src
);
1643 // convert some value
1645 auto b
= to
!Integral(a
);
1646 assert(is(typeof(b
) == Integral
) && b
== 42);
1647 // convert some negative value (if applicable)
1649 static if (Integral
.min
< 0)
1652 assert(is(typeof(b
) == Integral
) && b
== -42);
1656 // no go for unsigned types
1657 assert(convFails
!(Floating
, Integral
, ConvOverflowException
)(a
));
1659 // convert to the smallest integral value
1660 a
= 0.0 + Integral
.min
;
1661 static if (Integral
.min
< 0)
1663 a
= -a
; // -Integral.min not representable as an Integral
1664 assert(convFails
!(Floating
, Integral
, ConvOverflowException
)(a
)
1665 || Floating
.sizeof
<= Integral
.sizeof
1666 || floatTraits
!Floating
.realFormat
== RealFormat
.ieeeExtended53
);
1668 a
= 0.0 + Integral
.min
;
1669 assert(to
!Integral(a
) == Integral
.min
);
1670 --a
; // no more representable as an Integral
1671 assert(convFails
!(Floating
, Integral
, ConvOverflowException
)(a
)
1672 || Floating
.sizeof
<= Integral
.sizeof
1673 || floatTraits
!Floating
.realFormat
== RealFormat
.ieeeExtended53
);
1674 a
= 0.0 + Integral
.max
;
1675 assert(to
!Integral(a
) == Integral
.max
1676 || Floating
.sizeof
<= Integral
.sizeof
1677 || floatTraits
!Floating
.realFormat
== RealFormat
.ieeeExtended53
);
1678 ++a
; // no more representable as an Integral
1679 assert(convFails
!(Floating
, Integral
, ConvOverflowException
)(a
)
1680 || Floating
.sizeof
<= Integral
.sizeof
1681 || floatTraits
!Floating
.realFormat
== RealFormat
.ieeeExtended53
);
1682 // convert a value with a fractional part
1684 assert(to
!Integral(a
) == 3);
1686 assert(to
!Integral(a
) == 3);
1687 static if (Integral
.min
< 0)
1690 assert(to
!Integral(a
) == -3);
1692 assert(to
!Integral(a
) == -3);
1698 alias AllInts
= AliasSeq
!(byte, ubyte, short, ushort, int, uint, long, ulong);
1699 alias AllFloats
= AliasSeq
!(float, double, real);
1700 alias AllNumerics
= AliasSeq
!(AllInts
, AllFloats
);
1701 // test with same type
1703 foreach (T
; AllNumerics
)
1707 assert(is(typeof(a
) == typeof(b
)) && a
== b
);
1710 // test that floating-point numbers convert properly to largest ints
1711 // see http://oregonstate.edu/~peterseb/mth351/docs/351s2001_fp80x87.html
1712 // look for "largest fp integer with a predecessor"
1715 int a
= 16_777_215; // 2^24 - 1
1716 assert(to
!int(to
!float(a
)) == a
);
1717 assert(to
!int(to
!float(-a
)) == -a
);
1719 long b
= 9_007_199_254_740_991; // 2^53 - 1
1720 assert(to
!long(to
!double(b
)) == b
);
1721 assert(to
!long(to
!double(-b
)) == -b
);
1723 static if (real.mant_dig
>= 64)
1725 ulong c
= 18_446_744_073_709_551_615UL; // 2^64 - 1
1726 assert(to
!ulong(to
!real(c
)) == c
);
1729 // test conversions floating => integral
1731 // AllInts[0 .. $ - 1] should be AllInts
1732 // @@@ BUG IN COMPILER @@@
1733 foreach (Integral
; AllInts
[0 .. $ - 1])
1735 foreach (Floating
; AllFloats
)
1737 testFloatingToIntegral
!(Floating
, Integral
)();
1741 // test conversion integral => floating
1743 foreach (Integral
; AllInts
[0 .. $ - 1])
1745 foreach (Floating
; AllFloats
)
1747 testIntegralToFloating
!(Integral
, Floating
)();
1753 foreach (T
; AllNumerics
)
1755 // from type immutable(char)[2]
1756 auto a
= to
!T("42");
1759 char[] s1
= "42".dup
;
1762 // from type char[2]
1767 // from type immutable(wchar)[2]
1776 alias AllInts
= AliasSeq
!(byte, ubyte, short, ushort, int, uint, long, ulong);
1777 alias AllFloats
= AliasSeq
!(float, double, real);
1778 alias AllNumerics
= AliasSeq
!(AllInts
, AllFloats
);
1779 // test conversions to string
1781 foreach (T
; AllNumerics
)
1784 assert(to
!string(a
) == "42");
1785 assert(to
!wstring(a
) == "42"w
);
1786 assert(to
!dstring(a
) == "42"d
);
1791 assert(to
!string(b
) == "[42, 33]");
1794 // test array to string conversion
1795 foreach (T
; AllNumerics
)
1797 auto a
= [to
!T(1), 2, 3];
1798 assert(to
!string(a
) == "[1, 2, 3]");
1800 // test enum to int conversion
1801 enum Testing
{ Test1
, Test2
}
1803 auto a
= to
!string(t
);
1804 assert(a
== "Test1");
1809 String, or string-like input range, to non-string conversion runs parsing.
1811 $(LI When the source is a wide string, it is first converted to a narrow
1812 string and then parsed.)
1813 $(LI When the source is a narrow string, normal text parsing occurs.))
1815 private T
toImpl(T
, S
)(S value
)
1816 if (isInputRange
!S
&& isSomeChar
!(ElementEncodingType
!S
) &&
1817 !isExactSomeString
!T
&& is(typeof(parse
!T(value
))))
1823 throw convError
!(S
, T
)(value
);
1826 return parse
!T(value
);
1830 private T
toImpl(T
, S
)(S value
, uint radix
)
1831 if (isInputRange
!S
&& !isInfinite
!S
&& isSomeChar
!(ElementEncodingType
!S
) &&
1832 isIntegral
!T
&& is(typeof(parse
!T(value
, radix
))))
1838 throw convError
!(S
, T
)(value
);
1841 return parse
!T(value
, radix
);
1846 // Issue 6668 - ensure no collaterals thrown
1847 try { to
!uint("-1"); }
1848 catch (ConvException e
) { assert(e
.next
is null); }
1853 foreach (Str
; AliasSeq
!(string
, wstring
, dstring
))
1856 assert(to
!int(a
) == 123);
1857 assert(to
!double(a
) == 123);
1861 auto n
= to
!int("FF", 16);
1868 import std
.utf
: byCodeUnit
, byChar
, byWchar
, byDchar
;
1870 assert(to
!int(byCodeUnit("10")) == 10);
1871 assert(to
!int(byCodeUnit("10"), 10) == 10);
1872 assert(to
!int(byCodeUnit("10"w
)) == 10);
1873 assert(to
!int(byCodeUnit("10"w
), 10) == 10);
1875 assert(to
!int(byChar("10")) == 10);
1876 assert(to
!int(byChar("10"), 10) == 10);
1877 assert(to
!int(byWchar("10")) == 10);
1878 assert(to
!int(byWchar("10"), 10) == 10);
1879 assert(to
!int(byDchar("10")) == 10);
1880 assert(to
!int(byDchar("10"), 10) == 10);
1884 Convert a value that is implicitly convertible to the enum base type
1885 into an Enum value. If the value does not match any enum member values
1886 a ConvException is thrown.
1887 Enums with floating-point or string base types are not supported.
1889 private T
toImpl(T
, S
)(S value
)
1890 if (is(T
== enum) && !is(S
== enum)
1891 && is(typeof(value
== OriginalType
!T
.init
))
1892 && !isFloatingPoint
!(OriginalType
!T
) && !isSomeString
!(OriginalType
!T
))
1894 foreach (Member
; EnumMembers
!T
)
1896 if (Member
== value
)
1899 throw new ConvException(convFormat("Value (%s) does not match any member value of enum '%s'", value
, T
.stringof
));
1904 import std
.exception
;
1905 enum En8143
: int { A
= 10, B
= 20, C
= 30, D
= 20 }
1906 enum En8143
[][] m3
= to
!(En8143
[][])([[10, 30], [30, 10]]);
1907 static assert(m3
== [[En8143
.A
, En8143
.C
], [En8143
.C
, En8143
.A
]]);
1909 En8143 en1
= to
!En8143(10);
1910 assert(en1
== En8143
.A
);
1911 assertThrown
!ConvException(to
!En8143(5)); // matches none
1912 En8143
[][] m1
= to
!(En8143
[][])([[10, 30], [30, 10]]);
1913 assert(m1
== [[En8143
.A
, En8143
.C
], [En8143
.C
, En8143
.A
]]);
1916 /***************************************************************
1917 Rounded conversion from floating point to integral.
1919 Rounded conversions do not work with non-integral target types.
1922 template roundTo(Target
)
1924 Target
roundTo(Source
)(Source value
)
1926 import std
.math
: trunc
;
1928 static assert(isFloatingPoint
!Source
);
1929 static assert(isIntegral
!Target
);
1930 return to
!Target(trunc(value
+ (value
< 0 ?
-0.5L : 0.5L)));
1937 assert(roundTo
!int(3.14) == 3);
1938 assert(roundTo
!int(3.49) == 3);
1939 assert(roundTo
!int(3.5) == 4);
1940 assert(roundTo
!int(3.999) == 4);
1941 assert(roundTo
!int(-3.14) == -3);
1942 assert(roundTo
!int(-3.49) == -3);
1943 assert(roundTo
!int(-3.5) == -4);
1944 assert(roundTo
!int(-3.999) == -4);
1945 assert(roundTo
!(const int)(to
!(const double)(-3.999)) == -4);
1950 import std
.exception
;
1952 foreach (Int
; AliasSeq
!(byte, ubyte, short, ushort, int, uint))
1954 assert(roundTo
!Int(Int
.min
- 0.4L) == Int
.min
);
1955 assert(roundTo
!Int(Int
.max
+ 0.4L) == Int
.max
);
1956 assertThrown
!ConvOverflowException(roundTo
!Int(Int
.min
- 0.5L));
1957 assertThrown
!ConvOverflowException(roundTo
!Int(Int
.max
+ 0.5L));
1962 The $(D parse) family of functions works quite like the $(D to)
1963 family, except that:
1965 $(LI It only works with character ranges as input.)
1966 $(LI It takes the input by reference. (This means that rvalues - such
1967 as string literals - are not accepted: use $(D to) instead.))
1968 $(LI It advances the input to the position following the conversion.)
1969 $(LI It does not throw if it could not convert the entire input.))
1971 This overload converts an character input range to a `bool`.
1974 Target = the type to convert to
1975 source = the lvalue of an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
1981 A $(LREF ConvException) if the range does not represent a `bool`.
1984 All character input range conversions using $(LREF to) are forwarded
1985 to `parse` and do not require lvalues.
1987 Target
parse(Target
, Source
)(ref Source source
)
1988 if (isInputRange
!Source
&&
1989 isSomeChar
!(ElementType
!Source
) &&
1990 is(Unqual
!Target
== bool))
1992 import std
.ascii
: toLower
;
1994 static if (isNarrowString
!Source
)
1996 import std
.string
: representation
;
1997 auto s
= source
.representation
;
2006 auto c1
= toLower(s
.front
);
2007 bool result
= c1
== 't';
2008 if (result || c1
== 'f')
2011 foreach (c
; result ?
"rue" : "alse")
2013 if (s
.empty ||
toLower(s
.front
) != c
)
2018 static if (isNarrowString
!Source
)
2019 source
= cast(Source
) s
;
2025 throw parseError("bool should be case-insensitive 'true' or 'false'");
2032 bool b
= parse
!bool(s
);
2038 import std
.algorithm
.comparison
: equal
;
2039 import std
.exception
;
2043 @property auto front() { return _s
.front
; }
2044 @property bool empty() { return _s
.empty
; }
2045 void popFront() { _s
.popFront(); }
2048 auto s
= InputString("trueFALSETrueFalsetRUEfALSE");
2049 assert(parse
!bool(s
) == true);
2050 assert(s
.equal("FALSETrueFalsetRUEfALSE"));
2051 assert(parse
!bool(s
) == false);
2052 assert(s
.equal("TrueFalsetRUEfALSE"));
2053 assert(parse
!bool(s
) == true);
2054 assert(s
.equal("FalsetRUEfALSE"));
2055 assert(parse
!bool(s
) == false);
2056 assert(s
.equal("tRUEfALSE"));
2057 assert(parse
!bool(s
) == true);
2058 assert(s
.equal("fALSE"));
2059 assert(parse
!bool(s
) == false);
2062 foreach (ss
; ["tfalse", "ftrue", "t", "f", "tru", "fals", ""])
2064 s
= InputString(ss
);
2065 assertThrown
!ConvException(parse
!bool(s
));
2070 Parses a character $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
2071 to an integral value.
2074 Target = the integral type to convert to
2075 s = the lvalue of an input range
2078 A number of type `Target`
2081 A $(LREF ConvException) If an overflow occurred during conversion or
2082 if no character of the input was meaningfully converted.
2084 Target
parse(Target
, Source
)(ref Source s
)
2085 if (isSomeChar
!(ElementType
!Source
) &&
2086 isIntegral
!Target
&& !is(Target
== enum))
2088 static if (Target
.sizeof
< int.sizeof
)
2090 // smaller types are handled like integers
2091 auto v
= .parse
!(Select
!(Target
.min
< 0, int, uint))(s
);
2092 auto result
= ()@trusted{ return cast(Target
) v
; }();
2095 throw new ConvOverflowException("Overflow in integral conversion");
2099 // int or larger types
2101 static if (Target
.min
< 0)
2104 enum bool sign
= false;
2106 enum char maxLastDigit
= Target
.min
< 0 ?
7 : 5;
2109 static if (isNarrowString
!Source
)
2111 import std
.string
: representation
;
2112 auto source
= s
.representation
;
2124 static if (Target
.min
< 0)
2148 Target v
= cast(Target
) c
;
2152 while (!source
.empty
)
2154 c
= cast(typeof(c
)) (source
.front
- '0');
2159 if (v
>= 0 && (v
< Target
.max
/10 ||
2160 (v
== Target
.max
/10 && c
<= maxLastDigit
+ sign
)))
2162 // Note: `v` can become negative here in case of parsing
2163 // the most negative value:
2164 v
= cast(Target
) (v
* 10 + c
);
2169 throw new ConvOverflowException("Overflow in integral conversion");
2175 static if (isNarrowString
!Source
)
2176 s
= cast(Source
) source
;
2181 static if (isNarrowString
!Source
)
2182 throw convError
!(Source
, Target
)(cast(Source
) source
);
2184 throw convError
!(Source
, Target
)(source
);
2192 auto a
= parse
!int(s
);
2195 // parse only accepts lvalues
2196 static assert(!__traits(compiles
, parse
!int("123")));
2202 import std
.string
: tr
;
2203 string
test = "123 \t 76.14";
2204 auto a
= parse
!uint(test);
2206 assert(test == " \t 76.14"); // parse bumps string
2207 test = tr(test, " \t\n\r", "", "d"); // skip ws
2208 assert(test == "76.14");
2209 auto b
= parse
!double(test);
2216 foreach (Int
; AliasSeq
!(byte, ubyte, short, ushort, int, uint, long, ulong))
2219 assert(to
!Int("0") == 0);
2221 static if (isSigned
!Int
)
2223 assert(to
!Int("+0") == 0);
2224 assert(to
!Int("-0") == 0);
2228 static if (Int
.sizeof
>= byte.sizeof
)
2230 assert(to
!Int("6") == 6);
2231 assert(to
!Int("23") == 23);
2232 assert(to
!Int("68") == 68);
2233 assert(to
!Int("127") == 0x7F);
2235 static if (isUnsigned
!Int
)
2237 assert(to
!Int("255") == 0xFF);
2239 static if (isSigned
!Int
)
2241 assert(to
!Int("+6") == 6);
2242 assert(to
!Int("+23") == 23);
2243 assert(to
!Int("+68") == 68);
2244 assert(to
!Int("+127") == 0x7F);
2246 assert(to
!Int("-6") == -6);
2247 assert(to
!Int("-23") == -23);
2248 assert(to
!Int("-68") == -68);
2249 assert(to
!Int("-128") == -128);
2253 static if (Int
.sizeof
>= short.sizeof
)
2255 assert(to
!Int("468") == 468);
2256 assert(to
!Int("32767") == 0x7FFF);
2258 static if (isUnsigned
!Int
)
2260 assert(to
!Int("65535") == 0xFFFF);
2262 static if (isSigned
!Int
)
2264 assert(to
!Int("+468") == 468);
2265 assert(to
!Int("+32767") == 0x7FFF);
2267 assert(to
!Int("-468") == -468);
2268 assert(to
!Int("-32768") == -32768);
2272 static if (Int
.sizeof
>= int.sizeof
)
2274 assert(to
!Int("2147483647") == 0x7FFFFFFF);
2276 static if (isUnsigned
!Int
)
2278 assert(to
!Int("4294967295") == 0xFFFFFFFF);
2281 static if (isSigned
!Int
)
2283 assert(to
!Int("+2147483647") == 0x7FFFFFFF);
2285 assert(to
!Int("-2147483648") == -2147483648);
2289 static if (Int
.sizeof
>= long.sizeof
)
2291 assert(to
!Int("9223372036854775807") == 0x7FFFFFFFFFFFFFFF);
2293 static if (isUnsigned
!Int
)
2295 assert(to
!Int("18446744073709551615") == 0xFFFFFFFFFFFFFFFF);
2298 static if (isSigned
!Int
)
2300 assert(to
!Int("+9223372036854775807") == 0x7FFFFFFFFFFFFFFF);
2302 assert(to
!Int("-9223372036854775808") == 0x8000000000000000);
2310 import std
.exception
;
2311 // parsing error check
2312 foreach (Int
; AliasSeq
!(byte, ubyte, short, ushort, int, uint, long, ulong))
2315 immutable string
[] errors1
=
2333 foreach (j
, s
; errors1
)
2334 assertThrown
!ConvException(to
!Int(s
));
2337 // parse!SomeUnsigned cannot parse head sign.
2338 static if (isUnsigned
!Int
)
2340 immutable string
[] errors2
=
2345 foreach (j
, s
; errors2
)
2346 assertThrown
!ConvException(to
!Int(s
));
2350 // positive overflow check
2351 foreach (i
, Int
; AliasSeq
!(byte, ubyte, short, ushort, int, uint, long, ulong))
2353 immutable string
[] errors
=
2355 "128", // > byte.max
2356 "256", // > ubyte.max
2357 "32768", // > short.max
2358 "65536", // > ushort.max
2359 "2147483648", // > int.max
2360 "4294967296", // > uint.max
2361 "9223372036854775808", // > long.max
2362 "18446744073709551616", // > ulong.max
2364 foreach (j
, s
; errors
[i
..$])
2365 assertThrown
!ConvOverflowException(to
!Int(s
));
2368 // negative overflow check
2369 foreach (i
, Int
; AliasSeq
!(byte, short, int, long))
2371 immutable string
[] errors
=
2373 "-129", // < byte.min
2374 "-32769", // < short.min
2375 "-2147483649", // < int.min
2376 "-9223372036854775809", // < long.min
2378 foreach (j
, s
; errors
[i
..$])
2379 assertThrown
!ConvOverflowException(to
!Int(s
));
2385 void checkErrMsg(string input
, dchar charInMsg
, dchar charNotInMsg
)
2389 int x
= input
.to
!int();
2390 assert(false, "Invalid conversion did not throw");
2392 catch (ConvException e
)
2394 // Ensure error message contains failing character, not the character
2396 import std
.algorithm
.searching
: canFind
;
2397 assert( e
.msg
.canFind(charInMsg
) &&
2398 !e
.msg
.canFind(charNotInMsg
));
2402 assert(false, "Did not throw ConvException");
2405 checkErrMsg("@$", '@', '$');
2406 checkErrMsg("@$123", '@', '$');
2407 checkErrMsg("1@$23", '@', '$');
2408 checkErrMsg("1@$", '@', '$');
2409 checkErrMsg("1@$2", '@', '$');
2410 checkErrMsg("12@$", '@', '$');
2415 import std
.exception
;
2416 assertCTFEable
!({ string s
= "1234abc"; assert(parse
! int(s
) == 1234 && s
== "abc"); });
2417 assertCTFEable
!({ string s
= "-1234abc"; assert(parse
! int(s
) == -1234 && s
== "abc"); });
2418 assertCTFEable
!({ string s
= "1234abc"; assert(parse
!uint(s
) == 1234 && s
== "abc"); });
2424 import std
.exception
;
2426 assertThrown
!ConvOverflowException("-21474836480".to
!int());
2427 assertThrown
!ConvOverflowException("-92233720368547758080".to
!long());
2433 struct StrInputRange
2435 this (string s
) { str = s
; }
2436 char front() const @property { return str[front_index
]; }
2437 char popFront() { return str[front_index
++]; }
2438 bool empty() const @property { return str.length
<= front_index
; }
2440 size_t front_index
= 0;
2442 auto input
= StrInputRange("777");
2443 assert(parse
!int(input
) == 777);
2447 Target
parse(Target
, Source
)(ref Source source
, uint radix
)
2448 if (isSomeChar
!(ElementType
!Source
) &&
2449 isIntegral
!Target
&& !is(Target
== enum))
2452 assert(radix
>= 2 && radix
<= 36);
2456 import core
.checkedint
: mulu
, addu
;
2457 import std
.exception
: enforce
;
2460 return parse
!Target(source
);
2462 enforce
!ConvException(!source
.empty
, "s must not be empty in integral parse");
2464 immutable uint beyond
= (radix
< 10 ?
'0' : 'a'-10) + radix
;
2467 static if (isNarrowString
!Source
)
2469 import std
.string
: representation
;
2470 auto s
= source
.representation
;
2491 c |
= 0x20;//poorman's tolower
2492 if (c
< 'a' || c
>= beyond
)
2498 bool overflow
= false;
2499 auto nextv
= v
.mulu(radix
, overflow
).addu(c
- '0', overflow
);
2500 enforce
!ConvOverflowException(!overflow
&& nextv
<= Target
.max
, "Overflow in integral conversion");
2501 v
= cast(Target
) nextv
;
2505 static if (isNarrowString
!Source
)
2506 source
= cast(Source
) s
;
2513 string s
; // parse doesn't accept rvalues
2514 foreach (i
; 2 .. 37)
2516 assert(parse
!int(s
= "0", i
) == 0);
2517 assert(parse
!int(s
= "1", i
) == 1);
2518 assert(parse
!byte(s
= "10", i
) == i
);
2521 assert(parse
!int(s
= "0011001101101", 2) == 0b0011001101101);
2522 assert(parse
!int(s
= "765", 8) == octal
!765);
2523 assert(parse
!int(s
= "fCDe", 16) == 0xfcde);
2526 assert(parse
!int(s
= "-42", 10) == -42);
2528 assert(parse
!ubyte(s
= "ff", 16) == 0xFF);
2531 @safe pure unittest // bugzilla 7302
2533 import std
.range
: cycle
;
2534 auto r
= cycle("2A!");
2535 auto u
= parse
!uint(r
, 16);
2537 assert(r
.front
== '!');
2540 @safe pure unittest // bugzilla 13163
2542 import std
.exception
;
2543 foreach (s
; ["fff", "123"])
2544 assertThrown
!ConvOverflowException(s
.parse
!ubyte(16));
2547 @safe pure unittest // bugzilla 17282
2549 auto str = "0=\x00\x02\x55\x40&\xff\xf0\n\x00\x04\x55\x40\xff\xf0~4+10\n";
2550 assert(parse
!uint(str) == 0);
2554 * Takes a string representing an `enum` type and returns that type.
2557 * Target = the `enum` type to convert to
2558 * s = the lvalue of the range to _parse
2561 * An `enum` of type `Target`
2564 * A $(LREF ConvException) if type `Target` does not have a member
2565 * represented by `s`.
2567 Target
parse(Target
, Source
)(ref Source s
)
2568 if (isSomeString
!Source
&& !is(Source
== enum) &&
2571 import std
.algorithm
.searching
: startsWith
;
2573 size_t longest_match
= 0;
2575 foreach (i
, e
; EnumMembers
!Target
)
2577 auto ident
= __traits(allMembers
, Target
)[i
];
2578 if (longest_match
< ident
.length
&& s
.startsWith(ident
))
2581 longest_match
= ident
.length
;
2585 if (longest_match
> 0)
2587 s
= s
[longest_match
.. $];
2591 throw new ConvException(
2592 Target
.stringof
~ " does not have a member named '"
2593 ~ to
!string(s
) ~ "'");
2599 enum EnumType
: bool { a
= true, b
= false, c
= a
}
2602 assert(parse
!EnumType(str) == EnumType
.a
);
2607 import std
.exception
;
2609 enum EB
: bool { a
= true, b
= false, c
= a
}
2611 enum EI
{ a
= -1, b
= 0, c
= 1 }
2612 enum EF
: real { a
= 1.414, b
= 1.732, c
= 2.236 }
2613 enum EC
: char { a
= 'a', b
= 'b', c
= 'c' }
2614 enum ES
: string
{ a
= "aaa", b
= "bbb", c
= "ccc" }
2616 foreach (E
; AliasSeq
!(EB
, EU
, EI
, EF
, EC
, ES
))
2618 assert(to
!E("a"c
) == E
.a
);
2619 assert(to
!E("b"w
) == E
.b
);
2620 assert(to
!E("c"d
) == E
.c
);
2622 assertThrown
!ConvException(to
!E("d"));
2626 @safe pure unittest // bugzilla 4744
2628 enum A
{ member1
, member11
, member111
}
2629 assert(to
!A("member1" ) == A
.member1
);
2630 assert(to
!A("member11" ) == A
.member11
);
2631 assert(to
!A("member111") == A
.member111
);
2632 auto s
= "member1111";
2633 assert(parse
!A(s
) == A
.member111
&& s
== "1");
2637 * Parses a character range to a floating point number.
2640 * Target = a floating point type
2641 * source = the lvalue of the range to _parse
2644 * A floating point number of type `Target`
2647 * A $(LREF ConvException) if `p` is empty, if no number could be
2648 * parsed, or if an overflow occurred.
2650 Target
parse(Target
, Source
)(ref Source source
)
2651 if (isInputRange
!Source
&& isSomeChar
!(ElementType
!Source
) && !is(Source
== enum) &&
2652 isFloatingPoint
!Target
&& !is(Target
== enum))
2654 import std
.ascii
: isDigit
, isAlpha
, toLower
, toUpper
, isHexDigit
;
2655 import std
.exception
: enforce
;
2657 static if (isNarrowString
!Source
)
2659 import std
.string
: representation
;
2660 auto p
= source
.representation
;
2667 static immutable real[14] negtab
=
2668 [ 1e-4096L,1e-2048L,1e-1024L,1e-512L,1e-256L,1e-128L,1e-64L,1e-32L,
2669 1e-16L,1e-8L,1e-4L,1e-2L,1e-1L,1.0L ];
2670 static immutable real[13] postab
=
2671 [ 1e+4096L,1e+2048L,1e+1024L,1e+512L,1e+256L,1e+128L,1e+64L,1e+32L,
2672 1e+16L,1e+8L,1e+4L,1e+2L,1e+1L ];
2674 ConvException
bailOut()(string msg
= null, string fn
= __FILE__
, size_t ln
= __LINE__
)
2677 msg
= "Floating point conversion error";
2678 return new ConvException(text(msg
, " for input \"", source
, "\"."), fn
, ln
);
2682 enforce(!p
.empty
, bailOut());
2690 enforce(!p
.empty
, bailOut());
2691 if (toLower(p
.front
) == 'i')
2696 enforce(!p
.empty
, bailOut());
2701 enforce(!p
.empty
&& toUpper(p
.front
) == 'N',
2702 bailOut("error converting input to floating point"));
2704 enforce(!p
.empty
&& toUpper(p
.front
) == 'F',
2705 bailOut("error converting input to floating point"));
2706 // skip past the last 'f'
2708 static if (isNarrowString
!Source
)
2709 source
= cast(Source
) p
;
2710 return sign ?
-Target
.infinity
: Target
.infinity
;
2715 bool startsWithZero
= p
.front
== '0';
2721 static if (isNarrowString
!Source
)
2722 source
= cast(Source
) p
;
2723 return sign ?
-0.0 : 0.0;
2726 isHex
= p
.front
== 'x' || p
.front
== 'X';
2727 if (isHex
) p
.popFront();
2729 else if (toLower(p
.front
) == 'n')
2733 enforce(!p
.empty
&& toUpper(p
.front
) == 'A',
2734 bailOut("error converting input to floating point"));
2736 enforce(!p
.empty
&& toUpper(p
.front
) == 'N',
2737 bailOut("error converting input to floating point"));
2738 // skip past the last 'n'
2740 static if (isNarrowString
!Source
)
2741 source
= cast(Source
) p
;
2742 return typeof(return).nan
;
2746 * The following algorithm consists of 2 steps:
2747 * 1) parseDigits processes the textual input into msdec and possibly
2748 * lsdec/msscale variables, followed by the exponent parser which sets
2750 * Hex: input is 0xaaaaa...p+000... where aaaa is the mantissa in hex
2751 * and 000 is the exponent in decimal format with base 2.
2752 * Decimal: input is 0.00333...p+000... where 0.0033 is the mantissa
2753 * in decimal and 000 is the exponent in decimal format with base 10.
2754 * 2) Convert msdec/lsdec and exp into native real format
2758 char dot
= 0; /* if decimal point has been seen */
2760 ulong msdec
= 0, lsdec
= 0;
2764 enum { hex
, decimal
}
2766 // sets msdec, lsdec/msscale, and sawDigits by parsing the mantissa digits
2767 void parseDigits(alias FloatFormat
)()
2769 static if (FloatFormat
== hex
)
2771 enum uint base
= 16;
2772 enum ulong msscaleMax
= 0x1000_0000_0000_0000UL; // largest power of 16 a ulong holds
2773 enum ubyte expIter
= 4; // iterate the base-2 exponent by 4 for every hex digit
2774 alias checkDigit
= isHexDigit
;
2776 * convert letter to binary representation: First clear bit
2777 * to convert lower space chars to upperspace, then -('A'-10)
2778 * converts letter A to 10, letter B to 11, ...
2780 alias convertDigit
= (int x
) => isAlpha(x
) ?
((x
& ~0x20) - ('A' - 10)) : x
- '0';
2783 else static if (FloatFormat
== decimal
)
2785 enum uint base
= 10;
2786 enum ulong msscaleMax
= 10_000_000_000_000_000_000UL; // largest power of 10 a ulong holds
2787 enum ubyte expIter
= 1; // iterate the base-10 exponent once for every decimal digit
2788 alias checkDigit
= isDigit
;
2789 alias convertDigit
= (int x
) => x
- '0';
2790 // Used to enforce that any mantissa digits are present
2791 sawDigits
= startsWithZero
;
2794 static assert(false, "Unrecognized floating-point format used.");
2799 while (checkDigit(i
))
2801 sawDigits
= true; /* must have at least 1 digit */
2803 i
= convertDigit(i
);
2805 if (msdec
< (ulong.max
- base
)/base
)
2807 // For base 16: Y = ... + y3*16^3 + y2*16^2 + y1*16^1 + y0*16^0
2808 msdec
= msdec
* base
+ i
;
2810 else if (msscale
< msscaleMax
)
2812 lsdec
= lsdec
* base
+ i
;
2832 if (i
== '.' && !dot
)
2841 // Have we seen any mantissa digits so far?
2842 enforce(sawDigits
, bailOut("no digits seen"));
2843 static if (FloatFormat
== hex
)
2844 enforce(!p
.empty
&& (p
.front
== 'p' || p
.front
== 'P'),
2845 bailOut("Floating point parsing: exponent is required"));
2851 parseDigits
!decimal
;
2853 if (isHex ||
(!p
.empty
&& (p
.front
== 'e' || p
.front
== 'E')))
2859 enforce(!p
.empty
, new ConvException("Unexpected end of input"));
2864 case '+': p
.popFront();
2869 while (!p
.empty
&& isDigit(p
.front
))
2871 if (e
< 0x7FFFFFFF / 10 - 10) // prevent integer overflow
2873 e
= e
* 10 + p
.front
- '0';
2878 exp
+= (sexp
) ?
-e
: e
;
2879 enforce(sawDigits
, new ConvException("No digits seen."));
2883 if (msscale
!= 1) /* if stuff was accumulated in lsdec */
2884 ldval
= ldval
* msscale
+ lsdec
;
2887 import std
.math
: ldexp
;
2889 // Exponent is power of 2, not power of 10
2890 ldval
= ldexp(ldval
,exp
);
2912 enforce(ldval
!= 0, new ConvException("Range error"));
2920 // if overflow occurred
2921 enforce(ldval
!= real.infinity
, new ConvException("Range error"));
2923 static if (isNarrowString
!Source
)
2924 source
= cast(Source
) p
;
2925 return sign ?
-ldval
: ldval
;
2931 import std
.math
: approxEqual
;
2932 auto str = "123.456";
2934 assert(parse
!double(str).approxEqual(123.456));
2939 import std
.exception
;
2940 import std
.math
: isNaN
, fabs;
2942 // Compare reals with given precision
2943 bool feq(in real rx
, in real ry
, in real precision
= 0.000001L)
2949 return cast(bool) isNaN(ry
);
2954 return cast(bool)(fabs(rx
- ry
) <= precision
);
2957 // Make given typed literal
2963 foreach (Float
; AliasSeq
!(float, double, real))
2965 assert(to
!Float("123") == Literal
!Float(123));
2966 assert(to
!Float("+123") == Literal
!Float(+123));
2967 assert(to
!Float("-123") == Literal
!Float(-123));
2968 assert(to
!Float("123e2") == Literal
!Float(123e2
));
2969 assert(to
!Float("123e+2") == Literal
!Float(123e+2));
2970 assert(to
!Float("123e-2") == Literal
!Float(123e-2));
2971 assert(to
!Float("123.") == Literal
!Float(123.0));
2972 assert(to
!Float(".375") == Literal
!Float(.375));
2974 assert(to
!Float("1.23375E+2") == Literal
!Float(1.23375E+2));
2976 assert(to
!Float("0") is 0.0);
2977 assert(to
!Float("-0") is -0.0);
2979 assert(isNaN(to
!Float("nan")));
2981 assertThrown
!ConvException(to
!Float("\x00"));
2985 float f
= to
!float("1.17549e-38");
2986 assert(feq(cast(real) f
, cast(real) 1.17549e-38));
2987 assert(feq(cast(real) f
, cast(real) float.min_normal
));
2988 f
= to
!float("3.40282e+38");
2989 assert(to
!string(f
) == to
!string(3.40282e+38));
2992 double d
= to
!double("2.22508e-308");
2993 assert(feq(cast(real) d
, cast(real) 2.22508e-308));
2994 assert(feq(cast(real) d
, cast(real) double.min_normal
));
2995 d
= to
!double("1.79769e+308");
2996 assert(to
!string(d
) == to
!string(1.79769e+308));
2997 assert(to
!string(d
) == to
!string(double.max
));
2999 assert(to
!string(to
!real(to
!string(real.max
/ 2L))) == to
!string(real.max
/ 2L));
3002 real r
= to
!real(to
!string(real.min_normal
));
3006 // to!string returns 3.3621e-4932L. It is less than real.min_normal and it is subnormal value
3008 // long double rd = 3.3621e-4932L;
3009 // printf("%Le\n", rd);
3010 // has unexpected result: 1.681050e-4932
3012 // Bug report: http://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=50937
3016 assert(to
!string(r
) == to
!string(real.min_normal
));
3018 r
= to
!real(to
!string(real.max
));
3019 assert(to
!string(r
) == to
!string(real.max
));
3022 // Tests for the double implementation
3025 // @system because strtod is not @safe.
3026 import std
.math
: floatTraits
, RealFormat
;
3028 static if (floatTraits
!real.realFormat
== RealFormat
.ieeeDouble
)
3030 import core
.stdc
.stdlib
, std
.exception
, std
.math
;
3032 //Should be parsed exactly: 53 bit mantissa
3033 string s
= "0x1A_BCDE_F012_3456p10";
3034 auto x
= parse
!real(s
);
3035 assert(x
== 0x1A_BCDE_F
012_3456p
10L);
3037 assert(((*cast(ulong*)&x
) & 0x000F_FFFF_FFFF_FFFF
) == 0xA_BCDE_F
012_3456);
3038 assert(strtod("0x1ABCDEF0123456p10", null) == x
);
3040 //Should be parsed exactly: 10 bit mantissa
3043 assert(x
== 0x03FFp
10);
3045 assert(((*cast(ulong*)&x
) & 0x000F_FFFF_FFFF_FFFF
) == 0x000F_F
800_0000_0000);
3046 assert(strtod("0x3FFp10", null) == x
);
3048 //60 bit mantissa, round up
3049 s
= "0xFFF_FFFF_FFFF_FFFFp10";
3051 assert(approxEqual(x
, 0xFFF_FFFF_FFFF_FFFFp
10));
3053 assert(((*cast(ulong*)&x
) & 0x000F_FFFF_FFFF_FFFF
) == 0x0000_0000_0000_0000);
3054 assert(strtod("0xFFFFFFFFFFFFFFFp10", null) == x
);
3056 //60 bit mantissa, round down
3057 s
= "0xFFF_FFFF_FFFF_FF90p10";
3059 assert(approxEqual(x
, 0xFFF_FFFF_FFFF_FF
90p
10));
3061 assert(((*cast(ulong*)&x
) & 0x000F_FFFF_FFFF_FFFF
) == 0x000F_FFFF_FFFF_FFFF
);
3062 assert(strtod("0xFFFFFFFFFFFFF90p10", null) == x
);
3064 //61 bit mantissa, round up 2
3065 s
= "0x1F0F_FFFF_FFFF_FFFFp10";
3067 assert(approxEqual(x
, 0x1F0F_FFFF_FFFF_FFFFp
10));
3069 assert(((*cast(ulong*)&x
) & 0x000F_FFFF_FFFF_FFFF
) == 0x000F_1000_0000_0000);
3070 assert(strtod("0x1F0FFFFFFFFFFFFFp10", null) == x
);
3072 //61 bit mantissa, round down 2
3073 s
= "0x1F0F_FFFF_FFFF_FF10p10";
3075 assert(approxEqual(x
, 0x1F0F_FFFF_FFFF_FF
10p
10));
3077 assert(((*cast(ulong*)&x
) & 0x000F_FFFF_FFFF_FFFF
) == 0x000F_0FFF_FFFF_FFFF
);
3078 assert(strtod("0x1F0FFFFFFFFFFF10p10", null) == x
);
3081 s
= "0x1F_FFFF_FFFF_FFFFp900";
3083 assert(strtod("0x1FFFFFFFFFFFFFp900", null) == x
);
3085 //exponent too big -> converror
3087 assertThrown
!ConvException(x
= parse
!real(s
));
3088 assert(strtod("0x1FFFFFFFFFFFFFp1024", null) == real.infinity
);
3090 //-exponent too big -> 0
3091 s
= "0x1FFFFFFFFFFFFFp-2000";
3094 assert(strtod("0x1FFFFFFFFFFFFFp-2000", null) == x
);
3100 import core
.stdc
.errno
;
3101 import core
.stdc
.stdlib
;
3102 import std
.math
: floatTraits
, RealFormat
;
3104 errno
= 0; // In case it was set by another unittest in a different module.
3107 static if (floatTraits
!real.realFormat
== RealFormat
.ieeeQuadruple
)
3111 else static if (floatTraits
!real.realFormat
== RealFormat
.ieeeExtended ||
3112 floatTraits
!real.realFormat
== RealFormat
.ieeeExtended53
)
3116 else static if (floatTraits
!real.realFormat
== RealFormat
.ieeeDouble
)
3121 static assert(false, "Not implemented");
3130 static if (floatTraits
!real.realFormat
== RealFormat
.ieeeQuadruple
)
3131 // Our parser is currently limited to ieeeExtended precision
3132 enum s
= "0x1.FFFFFFFFFFFFFFFEp-16382";
3133 else static if (floatTraits
!real.realFormat
== RealFormat
.ieeeExtended
)
3134 enum s
= "0x1.FFFFFFFFFFFFFFFEp-16382";
3135 else static if (floatTraits
!real.realFormat
== RealFormat
.ieeeExtended53
)
3136 enum s
= "0x1.FFFFFFFFFFFFFFFEp-16382";
3137 else static if (floatTraits
!real.realFormat
== RealFormat
.ieeeDouble
)
3138 enum s
= "0x1.FFFFFFFFFFFFFFFEp-1000";
3140 static assert(false, "Floating point format for real not supported");
3143 ld
= parse
!real(s2
);
3145 x
= *cast(longdouble
*)&ld
;
3147 static if (floatTraits
!real.realFormat
== RealFormat
.ieeeExtended
)
3149 version (CRuntime_Microsoft
)
3150 ld1
= 0x1.FFFFFFFFFFFFFFFEp
-16382L; // strtold currently mapped to strtod
3152 ld1
= strtold(s
.ptr
, null);
3154 else static if (floatTraits
!real.realFormat
== RealFormat
.ieeeExtended53
)
3155 ld1
= 0x1.FFFFFFFFFFFFFFFEp
-16382L; // strtold rounds to 53 bits.
3157 ld1
= strtold(s
.ptr
, null);
3159 x1
= *cast(longdouble
*)&ld1
;
3160 assert(x1
== x
&& ld1
== ld
);
3165 ld
= parse
!real(s2
);
3167 x
= *cast(longdouble
*)&ld
;
3168 ld1
= strtold("1.0e5", null);
3169 x1
= *cast(longdouble
*)&ld1
;
3174 import std
.exception
;
3179 auto x
= parse
!double(s
);
3185 assert(to
!float("inf") == float.infinity
);
3186 assert(to
!float("-inf") == -float.infinity
);
3189 assert(6_5.536e3L
== to
!real("6_5.536e3")); // 2^16
3190 assert(0x1000_000_000_p
10 == to
!real("0x1000_000_000_p10")); // 7.03687e+13
3193 assertThrown
!ConvException(to
!real("-"));
3194 assertThrown
!ConvException(to
!real("in"));
3197 assertThrown
!ConvException(to
!float("INF2"));
3199 //extra stress testing
3200 auto ssOK
= ["1.", "1.1.1", "1.e5", "2e1e", "2a", "2e1_1",
3201 "inf", "-inf", "infa", "-infa", "inf2e2", "-inf2e2"];
3202 auto ssKO
= ["", " ", "2e", "2e+", "2e-", "2ee", "2e++1", "2e--1", "2e_1", "+inf"];
3206 assertThrown
!ConvException(parse
!double(s
));
3210 Parsing one character off a range returns the first element and calls `popFront`.
3213 Target = the type to convert to
3214 s = the lvalue of an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
3217 A character of type `Target`
3220 A $(LREF ConvException) if the range is empty.
3222 Target
parse(Target
, Source
)(ref Source s
)
3223 if (isSomeString
!Source
&& !is(Source
== enum) &&
3224 staticIndexOf
!(Unqual
!Target
, dchar, Unqual
!(ElementEncodingType
!Source
)) >= 0)
3227 throw convError
!(Source
, Target
)(s
);
3228 static if (is(Unqual
!Target
== dchar))
3230 Target result
= s
.front
;
3236 // Special case: okay so parse a Char off a Char[]
3237 Target result
= s
[0];
3245 foreach (Str
; AliasSeq
!(string
, wstring
, dstring
))
3247 foreach (Char
; AliasSeq
!(char, wchar, dchar))
3249 static if (is(Unqual
!Char
== dchar) ||
3250 Char
.sizeof
== ElementEncodingType
!Str
.sizeof
)
3253 assert(parse
!Char(s
) == 'a');
3261 Target
parse(Target
, Source
)(ref Source s
)
3262 if (!isSomeString
!Source
&& isInputRange
!Source
&& isSomeChar
!(ElementType
!Source
) &&
3263 isSomeChar
!Target
&& Target
.sizeof
>= ElementType
!Source
.sizeof
&& !is(Target
== enum))
3266 throw convError
!(Source
, Target
)(s
);
3267 Target result
= s
.front
;
3275 auto s
= "Hello, World!";
3276 char first
= parse
!char(s
);
3277 assert(first
== 'H');
3278 assert(s
== "ello, World!");
3283 Tests for to!bool and parse!bool
3287 import std
.exception
;
3289 assert(to
!bool("TruE") == true);
3290 assert(to
!bool("faLse"d
) == false);
3291 assertThrown
!ConvException(to
!bool("maybe"));
3293 auto t
= "TrueType";
3294 assert(parse
!bool(t
) == true);
3295 assert(t
== "Type");
3297 auto f
= "False killer whale"d
;
3298 assert(parse
!bool(f
) == false);
3299 assert(f
== " killer whale"d
);
3302 assertThrown
!ConvException(parse
!bool(m
));
3303 assert(m
== "maybe"); // m shouldn't change on failure
3306 auto b
= parse
!(const(bool))(s
);
3311 Parsing a character range to `typeof(null)` returns `null` if the range
3312 spells `"null"`. This function is case insensitive.
3315 Target = the type to convert to
3316 s = the lvalue of an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
3322 A $(LREF ConvException) if the range doesn't represent `null`.
3324 Target
parse(Target
, Source
)(ref Source s
)
3325 if (isInputRange
!Source
&&
3326 isSomeChar
!(ElementType
!Source
) &&
3327 is(Unqual
!Target
== typeof(null)))
3329 import std
.ascii
: toLower
;
3332 if (s
.empty ||
toLower(s
.front
) != c
)
3333 throw parseError("null should be case-insensitive 'null'");
3342 import std
.exception
: assertThrown
;
3344 alias NullType
= typeof(null);
3346 assert(parse
!NullType(s1
) is null);
3350 assert(parse
!NullType(s2
) is null);
3354 assertThrown
!ConvException(parse
!NullType(m
));
3355 assert(m
== "maybe"); // m shouldn't change on failure
3358 assert(parse
!(const NullType
)(s
) is null);
3361 //Used internally by parse Array/AA, to remove ascii whites
3362 package void skipWS(R
)(ref R r
)
3364 import std
.ascii
: isWhite
;
3365 static if (isSomeString
!R
)
3367 //Implementation inspired from stripLeft.
3376 r
= r
[0 .. 0]; //Empty string with correct type.
3381 for (; !r
.empty
&& isWhite(r
.front
); r
.popFront())
3387 * Parses an array from a string given the left bracket (default $(D
3388 * '[')), right bracket (default $(D ']')), and element separator (by
3389 * default $(D ',')). A trailing separator is allowed.
3392 * s = The string to parse
3393 * lbracket = the character that starts the array
3394 * rbracket = the character that ends the array
3395 * comma = the character that separates the elements of the array
3398 * An array of type `Target`
3400 Target
parse(Target
, Source
)(ref Source s
, dchar lbracket
= '[', dchar rbracket
= ']', dchar comma
= ',')
3401 if (isSomeString
!Source
&& !is(Source
== enum) &&
3402 isDynamicArray
!Target
&& !is(Target
== enum))
3404 import std
.array
: appender
;
3406 auto result
= appender
!Target();
3408 parseCheck
!s(lbracket
);
3411 throw convError
!(Source
, Target
)(s
);
3412 if (s
.front
== rbracket
)
3417 for (;; s
.popFront(), skipWS(s
))
3419 if (!s
.empty
&& s
.front
== rbracket
)
3421 result
~= parseElement
!(ElementType
!Target
)(s
);
3424 throw convError
!(Source
, Target
)(s
);
3425 if (s
.front
!= comma
)
3428 parseCheck
!s(rbracket
);
3436 auto s1
= `[['h', 'e', 'l', 'l', 'o'], "world"]`;
3437 auto a1
= parse
!(string
[])(s1
);
3438 assert(a1
== ["hello", "world"]);
3440 auto s2
= `["aaa", "bbb", "ccc"]`;
3441 auto a2
= parse
!(string
[])(s2
);
3442 assert(a2
== ["aaa", "bbb", "ccc"]);
3445 @safe unittest // Bugzilla 9615
3447 string s0
= "[1,2, ]";
3448 string s1
= "[1,2, \t\v\r\n]";
3449 string s2
= "[1,2]";
3450 assert(s0
.parse
!(int[]) == [1,2]);
3451 assert(s1
.parse
!(int[]) == [1,2]);
3452 assert(s2
.parse
!(int[]) == [1,2]);
3454 string s3
= `["a","b",]`;
3455 string s4
= `["a","b"]`;
3456 assert(s3
.parse
!(string
[]) == ["a","b"]);
3457 assert(s4
.parse
!(string
[]) == ["a","b"]);
3459 import std
.exception
: assertThrown
;
3461 string s6
= "[, \t,]";
3462 assertThrown
!ConvException(parse
!(string
[])(s5
));
3463 assertThrown
!ConvException(parse
!(int[])(s6
));
3468 int[] a
= [1, 2, 3, 4, 5];
3469 auto s
= to
!string(a
);
3470 assert(to
!(int[])(s
) == a
);
3475 int[][] a
= [ [1, 2] , [3], [4, 5] ];
3476 auto s
= to
!string(a
);
3477 assert(to
!(int[][])(s
) == a
);
3482 int[][][] ia
= [ [[1,2],[3,4],[5]] , [[6],[],[7,8,9]] , [[]] ];
3484 char[] s
= to
!(char[])(ia
);
3487 ia2
= to
!(typeof(ia2
))(s
);
3493 import std
.exception
;
3495 //Check proper failure
3496 auto s
= "[ 1 , 2 , 3 ]";
3497 foreach (i
; 0 .. s
.length
-1)
3499 auto ss
= s
[0 .. i
];
3500 assertThrown
!ConvException(parse
!(int[])(ss
));
3502 int[] arr
= parse
!(int[])(s
);
3507 //Checks parsing of strings with escaped characters
3509 "Contains a\0null!",
3512 "backslash \\ slash / question \?",
3514 "unicode \u65E5 sun",
3515 "very long \U000065E5 sun"
3518 //Note: escaped characters purposefully replaced and isolated to guarantee
3519 //there are no typos in the escape syntax
3521 "Contains a" ~ '\0' ~ "null!",
3522 "tab" ~ '\t' ~ "here",
3523 "line" ~ '\n' ~ "break",
3524 "backslash " ~ '\\' ~ " slash / question ?",
3529 assert(s2
== parse
!(string
[])(s1
));
3534 Target
parse(Target
, Source
)(ref Source s
, dchar lbracket
= '[', dchar rbracket
= ']', dchar comma
= ',')
3535 if (isExactSomeString
!Source
&&
3536 isStaticArray
!Target
&& !is(Target
== enum))
3538 static if (hasIndirections
!Target
)
3539 Target result
= Target
.init
[0].init
;
3541 Target result
= void;
3543 parseCheck
!s(lbracket
);
3546 throw convError
!(Source
, Target
)(s
);
3547 if (s
.front
== rbracket
)
3549 static if (result
.length
!= 0)
3557 for (size_t i
= 0; ; s
.popFront(), skipWS(s
))
3559 if (i
== result
.length
)
3561 result
[i
++] = parseElement
!(ElementType
!Target
)(s
);
3564 throw convError
!(Source
, Target
)(s
);
3565 if (s
.front
!= comma
)
3567 if (i
!= result
.length
)
3572 parseCheck
!s(rbracket
);
3577 throw parseError(text("Too many elements in input, ", result
.length
, " elements expected."));
3580 throw parseError(text("Too few elements in input, ", result
.length
, " elements expected."));
3585 import std
.exception
;
3587 auto s1
= "[1,2,3,4]";
3588 auto sa1
= parse
!(int[4])(s1
);
3589 assert(sa1
== [1,2,3,4]);
3591 auto s2
= "[[1],[2,3],[4]]";
3592 auto sa2
= parse
!(int[][3])(s2
);
3593 assert(sa2
== [[1],[2,3],[4]]);
3595 auto s3
= "[1,2,3]";
3596 assertThrown
!ConvException(parse
!(int[4])(s3
));
3598 auto s4
= "[1,2,3,4,5]";
3599 assertThrown
!ConvException(parse
!(int[4])(s4
));
3603 * Parses an associative array from a string given the left bracket (default $(D
3604 * '[')), right bracket (default $(D ']')), key-value separator (default $(D
3605 * ':')), and element seprator (by default $(D ',')).
3608 * s = the string to parse
3609 * lbracket = the character that starts the associative array
3610 * rbracket = the character that ends the associative array
3611 * keyval = the character that associates the key with the value
3612 * comma = the character that separates the elements of the associative array
3615 * An associative array of type `Target`
3617 Target
parse(Target
, Source
)(ref Source s
, dchar lbracket
= '[',
3618 dchar rbracket
= ']', dchar keyval
= ':', dchar comma
= ',')
3619 if (isSomeString
!Source
&& !is(Source
== enum) &&
3620 isAssociativeArray
!Target
&& !is(Target
== enum))
3622 alias KeyType
= typeof(Target
.init
.keys
[0]);
3623 alias ValType
= typeof(Target
.init
.values
[0]);
3627 parseCheck
!s(lbracket
);
3630 throw convError
!(Source
, Target
)(s
);
3631 if (s
.front
== rbracket
)
3636 for (;; s
.popFront(), skipWS(s
))
3638 auto key
= parseElement
!KeyType(s
);
3640 parseCheck
!s(keyval
);
3642 auto val
= parseElement
!ValType(s
);
3646 throw convError
!(Source
, Target
)(s
);
3647 if (s
.front
!= comma
)
3650 parseCheck
!s(rbracket
);
3658 auto s1
= "[1:10, 2:20, 3:30]";
3659 auto aa1
= parse
!(int[int])(s1
);
3660 assert(aa1
== [1:10, 2:20, 3:30]);
3662 auto s2
= `["aaa":10, "bbb":20, "ccc":30]`;
3663 auto aa2
= parse
!(int[string
])(s2
);
3664 assert(aa2
== ["aaa":10, "bbb":20, "ccc":30]);
3666 auto s3
= `["aaa":[1], "bbb":[2,3], "ccc":[4,5,6]]`;
3667 auto aa3
= parse
!(int[][string
])(s3
);
3668 assert(aa3
== ["aaa":[1], "bbb":[2,3], "ccc":[4,5,6]]);
3673 import std
.exception
;
3675 //Check proper failure
3676 auto s
= "[1:10, 2:20, 3:30]";
3677 foreach (i
; 0 .. s
.length
-1)
3679 auto ss
= s
[0 .. i
];
3680 assertThrown
!ConvException(parse
!(int[int])(ss
));
3682 int[int] aa
= parse
!(int[int])(s
);
3685 private dchar parseEscape(Source
)(ref Source s
)
3686 if (isInputRange
!Source
&& isSomeChar
!(ElementType
!Source
))
3690 throw parseError("Unterminated escape sequence");
3692 dchar getHexDigit()(ref Source s_
= s
) // workaround
3694 import std
.ascii
: isAlpha
, isHexDigit
;
3696 throw parseError("Unterminated escape sequence");
3699 throw parseError("Unterminated escape sequence");
3702 throw parseError("Hex digit is missing");
3703 return isAlpha(c
) ?
((c
& ~0x20) - ('A' - 10)) : c
- '0';
3710 case '"': result
= '\"'; break;
3711 case '\'': result
= '\''; break;
3712 case '0': result
= '\0'; break;
3713 case '?': result
= '\?'; break;
3714 case '\\': result
= '\\'; break;
3715 case 'a': result
= '\a'; break;
3716 case 'b': result
= '\b'; break;
3717 case 'f': result
= '\f'; break;
3718 case 'n': result
= '\n'; break;
3719 case 'r': result
= '\r'; break;
3720 case 't': result
= '\t'; break;
3721 case 'v': result
= '\v'; break;
3723 result
= getHexDigit() << 4;
3724 result |
= getHexDigit();
3727 result
= getHexDigit() << 12;
3728 result |
= getHexDigit() << 8;
3729 result |
= getHexDigit() << 4;
3730 result |
= getHexDigit();
3733 result
= getHexDigit() << 28;
3734 result |
= getHexDigit() << 24;
3735 result |
= getHexDigit() << 20;
3736 result |
= getHexDigit() << 16;
3737 result |
= getHexDigit() << 12;
3738 result |
= getHexDigit() << 8;
3739 result |
= getHexDigit() << 4;
3740 result |
= getHexDigit();
3743 throw parseError("Unknown escape character " ~ to
!string(s
.front
));
3746 throw parseError("Unterminated escape sequence");
3756 `\"`, `\'`, `\?`, `\\`, `\a`, `\b`, `\f`, `\n`, `\r`, `\t`, `\v`, //Normal escapes
3757 //`\141`, //@@@9621@@@ Octal escapes.
3759 `\u65E5`, `\U00012456`
3760 //`\&`, `\"`, //@@@9621@@@ Named Character Entities.
3763 const(dchar)[] s2
= [
3764 '\"', '\'', '\?', '\\', '\a', '\b', '\f', '\n', '\r', '\t', '\v', //Normal escapes
3765 //'\141', //@@@9621@@@ Octal escapes.
3767 '\u65E5', '\U00012456'
3768 //'\&', '\"', //@@@9621@@@ Named Character Entities.
3771 foreach (i
; 0 .. s1
.length
)
3773 assert(s2
[i
] == parseEscape(s1
[i
]));
3774 assert(s1
[i
].empty
);
3780 import std
.exception
;
3783 `hello!`, //Not an escape
3784 `\`, //Premature termination
3785 `\/`, //Not an escape
3786 `\gggg`, //Not an escape
3787 `\xzz`, //Not an hex
3788 `\x0`, //Premature hex end
3789 `\XB9`, //Not legal hex syntax
3790 `\u!!`, //Not a unicode hex
3791 `\777`, //Octal is larger than a byte //Note: Throws, but simply because octals are unsupported
3792 `\u123`, //Premature hex end
3793 `\U123123` //Premature hex end
3796 assertThrown
!ConvException(parseEscape(s
));
3800 Target
parseElement(Target
, Source
)(ref Source s
)
3801 if (isInputRange
!Source
&& isSomeChar
!(ElementType
!Source
) && !is(Source
== enum) &&
3802 isExactSomeString
!Target
)
3804 import std
.array
: appender
;
3805 auto result
= appender
!Target();
3807 // parse array of chars
3809 throw convError
!(Source
, Target
)(s
);
3811 return parse
!Target(s
);
3815 throw convError
!(Source
, Target
)(s
);
3816 if (s
.front
== '\"')
3824 throw parseError("Unterminated quoted string");
3831 result
.put(parseEscape(s
));
3834 result
.put(s
.front
);
3843 Target
parseElement(Target
, Source
)(ref Source s
)
3844 if (isInputRange
!Source
&& isSomeChar
!(ElementType
!Source
) && !is(Source
== enum) &&
3845 isSomeChar
!Target
&& !is(Target
== enum))
3851 throw convError
!(Source
, Target
)(s
);
3852 if (s
.front
!= '\\')
3865 Target
parseElement(Target
, Source
)(ref Source s
)
3866 if (isInputRange
!Source
&& isSomeChar
!(ElementType
!Source
) &&
3867 !isSomeString
!Target
&& !isSomeChar
!Target
)
3869 return parse
!Target(s
);
3873 /***************************************************************
3874 * Convenience functions for converting one or more arguments
3875 * of any type into _text (the three character widths).
3877 string
text(T
...)(T args
)
3878 if (T
.length
> 0) { return textImpl
!string(args
); }
3881 wstring
wtext(T
...)(T args
)
3882 if (T
.length
> 0) { return textImpl
!wstring(args
); }
3885 dstring
dtext(T
...)(T args
)
3886 if (T
.length
> 0) { return textImpl
!dstring(args
); }
3891 assert( text(42, ' ', 1.5, ": xyz") == "42 1.5: xyz"c
);
3892 assert(wtext(42, ' ', 1.5, ": xyz") == "42 1.5: xyz"w
);
3893 assert(dtext(42, ' ', 1.5, ": xyz") == "42 1.5: xyz"d
);
3902 assert( text(c
, "ello", ' ', w
, "好 ", d
, "ው ሰላም ነው") == "hello 你好 እው ሰላም ነው"c
);
3903 assert(wtext(c
, "ello", ' ', w
, "好 ", d
, "ው ሰላም ነው") == "hello 你好 እው ሰላም ነው"w
);
3904 assert(dtext(c
, "ello", ' ', w
, "好 ", d
, "ው ሰላም ነው") == "hello 你好 እው ሰላም ነው"d
);
3907 wstring ws
= "여보세요";
3908 dstring
ds = "Здравствуйте";
3910 assert( text(cs
, ' ', ws
, " ", ds) == "今日は 여보세요 Здравствуйте"c
);
3911 assert(wtext(cs
, ' ', ws
, " ", ds) == "今日は 여보세요 Здравствуйте"w
);
3912 assert(dtext(cs
, ' ', ws
, " ", ds) == "今日は 여보세요 Здравствуйте"d
);
3915 private S
textImpl(S
, U
...)(U args
)
3917 static if (U
.length
== 0)
3921 else static if (U
.length
== 1)
3923 return to
!S(args
[0]);
3927 import std
.array
: appender
;
3929 auto app
= appender
!S();
3938 /***************************************************************
3939 The $(D octal) facility provides a means to declare a number in base 8.
3940 Using $(D octal!177) or $(D octal!"177") for 127 represented in octal
3941 (same as 0177 in C).
3943 The rules for strings are the usual for literals: If it can fit in an
3944 $(D int), it is an $(D int). Otherwise, it is a $(D long). But, if the
3945 user specifically asks for a $(D long) with the $(D L) suffix, always
3946 give the $(D long). Give an unsigned iff it is asked for with the $(D
3947 U) or $(D u) suffix. _Octals created from integers preserve the type
3948 of the passed-in integral.
3951 $(LREF parse) for parsing octal strings at runtime.
3953 template octal(string num
)
3954 if (isOctalLiteral(num
))
3956 static if ((octalFitsInInt
!num
&& !literalIsLong
!num
) && !literalIsUnsigned
!num
)
3957 enum octal
= octal
!int(num
);
3958 else static if ((!octalFitsInInt
!num || literalIsLong
!num
) && !literalIsUnsigned
!num
)
3959 enum octal
= octal
!long(num
);
3960 else static if ((octalFitsInInt
!num
&& !literalIsLong
!num
) && literalIsUnsigned
!num
)
3961 enum octal
= octal
!uint(num
);
3962 else static if ((!octalFitsInInt
!(num
) || literalIsLong
!(num
)) && literalIsUnsigned
!(num
))
3963 enum octal
= octal
!ulong(num
);
3965 static assert(false);
3969 template octal(alias decimalInteger
)
3970 if (is(typeof(decimalInteger
)) && isIntegral
!(typeof(decimalInteger
)))
3972 enum octal
= octal
!(typeof(decimalInteger
))(to
!string(decimalInteger
));
3980 // octal is a compile-time device
3982 // Create an unsigned octal
3983 auto z
= octal
!"1_000_000u";
3987 Takes a string, num, which is an octal literal, and returns its
3988 value, in the type T specified.
3990 private T
octal(T
)(const string num
)
3992 assert(isOctalLiteral(num
));
3996 foreach (const char s
; num
)
3998 if (s
< '0' || s
> '7') // we only care about digits; skip the rest
3999 // safe to skip - this is checked out in the assert so these
4000 // are just suffixes
4012 int a
= octal
!int("10");
4017 Take a look at int.max and int.max+1 in octal and the logic for this
4018 function follows directly.
4020 private template octalFitsInInt(string octalNum
)
4022 // note it is important to strip the literal of all
4023 // non-numbers. kill the suffix and underscores lest they mess up
4024 // the number of digits here that we depend on.
4025 enum bool octalFitsInInt
= strippedOctalLiteral(octalNum
).length
< 11 ||
4026 strippedOctalLiteral(octalNum
).length
== 11 &&
4027 strippedOctalLiteral(octalNum
)[0] == '1';
4030 private string
strippedOctalLiteral(string original
)
4032 string stripped
= "";
4033 foreach (c
; original
)
4034 if (c
>= '0' && c
<= '7')
4039 private template literalIsLong(string num
)
4041 static if (num
.length
> 1)
4042 // can be xxL or xxLu according to spec
4043 enum literalIsLong
= (num
[$-1] == 'L' || num
[$-2] == 'L');
4045 enum literalIsLong
= false;
4048 private template literalIsUnsigned(string num
)
4050 static if (num
.length
> 1)
4051 // can be xxU or xxUL according to spec
4052 enum literalIsUnsigned
= (num
[$-1] == 'u' || num
[$-2] == 'u')
4053 // both cases are allowed too
4054 ||
(num
[$-1] == 'U' || num
[$-2] == 'U');
4056 enum literalIsUnsigned
= false;
4060 Returns if the given string is a correctly formatted octal literal.
4062 The format is specified in spec/lex.html. The leading zero is allowed, but
4065 @safe pure nothrow @nogc
4066 private bool isOctalLiteral(const string num
)
4068 if (num
.length
== 0)
4071 // Must start with a number. To avoid confusion, literals that
4072 // start with a '0' are not allowed
4073 if (num
[0] == '0' && num
.length
> 1)
4075 if (num
[0] < '0' || num
[0] > '7')
4080 if ((c
< '0' || c
> '7') && c
!= '_') // not a legal character
4082 if (i
< num
.length
- 2)
4084 else // gotta check for those suffixes
4086 if (c
!= 'U' && c
!= 'u' && c
!= 'L')
4088 if (i
!= num
.length
- 1)
4090 // if we're not the last one, the next one must
4091 // also be a suffix to be valid
4093 if (c2
!= 'U' && c2
!= 'u' && c2
!= 'L')
4094 return false; // spam at the end of the string
4096 return false; // repeats are disallowed
4107 // ensure that you get the right types, even with embedded underscores
4108 auto w
= octal
!"100_000_000_000";
4109 static assert(!is(typeof(w
) == int));
4110 auto w2
= octal
!"1_000_000_000";
4111 static assert(is(typeof(w2
) == int));
4113 static assert(octal
!"45" == 37);
4114 static assert(octal
!"0" == 0);
4115 static assert(octal
!"7" == 7);
4116 static assert(octal
!"10" == 8);
4117 static assert(octal
!"666" == 438);
4119 static assert(octal
!45 == 37);
4120 static assert(octal
!0 == 0);
4121 static assert(octal
!7 == 7);
4122 static assert(octal
!10 == 8);
4123 static assert(octal
!666 == 438);
4125 static assert(octal
!"66_6" == 438);
4127 static assert(octal
!2520046213 == 356535435);
4128 static assert(octal
!"2520046213" == 356535435);
4130 static assert(octal
!17777777777 == int.max
);
4132 static assert(!__traits(compiles
, octal
!823));
4134 static assert(!__traits(compiles
, octal
!"823"));
4136 static assert(!__traits(compiles
, octal
!"_823"));
4137 static assert(!__traits(compiles
, octal
!"spam"));
4138 static assert(!__traits(compiles
, octal
!"77%"));
4140 static assert(is(typeof(octal
!"17777777777") == int));
4141 static assert(octal
!"17777777777" == int.max
);
4143 static assert(is(typeof(octal
!"20000000000U") == ulong)); // Shouldn't this be uint?
4144 static assert(octal
!"20000000000" == uint(int.max
) + 1);
4146 static assert(is(typeof(octal
!"777777777777777777777") == long));
4147 static assert(octal
!"777777777777777777777" == long.max
);
4149 static assert(is(typeof(octal
!"1000000000000000000000U") == ulong));
4150 static assert(octal
!"1000000000000000000000" == ulong(long.max
) + 1);
4155 // biggest value that should fit in an it
4156 a
= octal
!"17777777777";
4157 assert(a
== int.max
);
4158 // should not fit in the int
4159 static assert(!__traits(compiles
, a
= octal
!"20000000000"));
4160 // ... but should fit in a long
4161 b
= octal
!"20000000000";
4162 assert(b
== 1L + int.max
);
4171 emplaceRef is a package function for phobos internal use. It works like
4172 emplace, but takes its argument by ref (as opposed to "by pointer").
4174 This makes it easier to use, easier to be safe, and faster in a non-inline
4177 Furthermore, emplaceRef optionally takes a type paremeter, which specifies
4178 the type we want to build. This helps to build qualified objects on mutable
4179 buffer, without breaking the type system with unsafe casts.
4181 package void emplaceRef(T
, UT
, Args
...)(ref UT chunk
, auto ref Args args
)
4183 static if (args
.length
== 0)
4185 static assert(is(typeof({static T i
;})),
4186 convFormat("Cannot emplace a %1$s because %1$s.this() is annotated with @disable.", T
.stringof
));
4187 static if (is(T
== class)) static assert(!isAbstractClass
!T
,
4188 T
.stringof
~ " is abstract and it can't be emplaced");
4189 emplaceInitializer(chunk
);
4192 !is(T
== struct) && Args
.length
== 1 /* primitives, enums, arrays */
4194 Args
.length
== 1 && is(typeof({T t
= args
[0];})) /* conversions */
4196 is(typeof(T(args
))) /* general constructors */)
4203 static if (Args
.length
== 1)
4204 static if (is(typeof(payload
= x
[0])))
4214 static if (is(typeof(chunk
= T(args
))))
4216 else static if (args
.length
== 1 && is(typeof(chunk
= args
[0])))
4218 else assert(0, "CTFE emplace doesn't support "
4219 ~ T
.stringof
~ " from " ~ Args
.stringof
);
4223 S
* p
= () @trusted { return cast(S
*) &chunk
; }();
4224 emplaceInitializer(*p
);
4228 else static if (is(typeof(chunk
.__ctor(args
))))
4230 // This catches the rare case of local types that keep a frame pointer
4231 emplaceInitializer(chunk
);
4236 //We can't emplace. Try to diagnose a disabled postblit.
4237 static assert(!(Args
.length
== 1 && is(Args
[0] : T
)),
4238 convFormat("Cannot emplace a %1$s because %1$s.this(this) is annotated with @disable.", T
.stringof
));
4241 static assert(false,
4242 convFormat("%s cannot be emplaced from %s.", T
.stringof
, Args
[].stringof
));
4246 package void emplaceRef(UT
, Args
...)(ref UT chunk
, auto ref Args args
)
4247 if (is(UT
== Unqual
!UT
))
4249 emplaceRef
!(UT
, UT
)(chunk
, args
);
4252 //emplace helper functions
4253 private void emplaceInitializer(T
)(ref T chunk
) @trusted pure nothrow
4255 static if (!hasElaborateAssign
!T
&& isAssignable
!T
)
4259 import core
.stdc
.string
: memcpy
;
4260 static immutable T init
= T
.init
;
4261 memcpy(&chunk
, &init
, T
.sizeof
);
4267 Given a pointer $(D chunk) to uninitialized memory (but already typed
4268 as $(D T)), constructs an object of non-$(D class) type $(D T) at that
4269 address. If `T` is a class, initializes the class reference to null.
4271 Returns: A pointer to the newly constructed object (which is the same
4274 T
* emplace(T
)(T
* chunk
) @safe pure nothrow
4276 emplaceRef
!T(*chunk
);
4289 assert(s2
[0].i
== 42 && s2
[1].i
== 42);
4308 Given a pointer $(D chunk) to uninitialized memory (but already typed
4309 as a non-class type $(D T)), constructs an object of type $(D T) at
4310 that address from arguments $(D args). If `T` is a class, initializes
4311 the class reference to `args[0]`.
4313 This function can be $(D @trusted) if the corresponding constructor of
4314 $(D T) is $(D @safe).
4316 Returns: A pointer to the newly constructed object (which is the same
4319 T
* emplace(T
, Args
...)(T
* chunk
, auto ref Args args
)
4320 if (is(T
== struct) || Args
.length
== 1)
4322 emplaceRef
!T(*chunk
, args
);
4331 assert(*emplace
!int(&a
, b
) == 42);
4341 private void testEmplaceChunk(void[] chunk
, size_t typeSize
, size_t typeAlignment
, string typeName
) @nogc pure nothrow
4343 assert(chunk
.length
>= typeSize
, "emplace: Chunk size too small.");
4344 assert((cast(size_t
) chunk
.ptr
) % typeAlignment
== 0, "emplace: Chunk is not aligned.");
4348 Given a raw memory area $(D chunk), constructs an object of $(D class)
4349 type $(D T) at that address. The constructor is passed the arguments
4352 If `T` is an inner class whose `outer` field can be used to access an instance
4353 of the enclosing class, then `Args` must not be empty, and the first member of it
4354 must be a valid initializer for that `outer` field. Correct initialization of
4355 this field is essential to access members of the outer class inside `T` methods.
4358 $(D chunk) must be at least as large as $(D T) needs
4359 and should have an alignment multiple of $(D T)'s alignment. (The size
4360 of a $(D class) instance is obtained by using $(D
4361 __traits(classInstanceSize, T))).
4364 This function can be $(D @trusted) if the corresponding constructor of
4365 $(D T) is $(D @safe).
4367 Returns: The newly constructed object.
4369 T
emplace(T
, Args
...)(void[] chunk
, auto ref Args args
)
4372 static assert(!isAbstractClass
!T
, T
.stringof
~
4373 " is abstract and it can't be emplaced");
4375 enum classSize
= __traits(classInstanceSize
, T
);
4376 testEmplaceChunk(chunk
, classSize
, classInstanceAlignment
!T
, T
.stringof
);
4377 auto result
= cast(T
) chunk
.ptr
;
4379 // Initialize the object in its pre-ctor state
4380 chunk
[0 .. classSize
] = typeid(T
).initializer
[];
4382 static if (isInnerClass
!T
)
4384 static assert(Args
.length
> 0,
4385 "Initializing an inner class requires a pointer to the outer class");
4386 static assert(is(Args
[0] : typeof(T
.outer
)),
4387 "The first argument must be a pointer to the outer class");
4389 result
.outer
= args
[0];
4390 alias args1
= args
[1..$];
4392 else alias args1
= args
;
4394 // Call the ctor if any
4395 static if (is(typeof(result
.__ctor(args1
))))
4397 // T defines a genuine constructor accepting args
4398 // Go the classic route: write .init first, then call ctor
4399 result
.__ctor(args1
);
4403 static assert(args1
.length
== 0 && !is(typeof(&T
.__ctor
)),
4404 "Don't know how to initialize an object of type "
4405 ~ T
.stringof
~ " with arguments " ~ typeof(args1
).stringof
);
4416 this(int i
){this.i
= i
;}
4418 auto buf
= new void[__traits(classInstanceSize
, C
)];
4419 auto c
= emplace
!C(buf
, 5);
4430 auto getI() { return i
; }
4433 auto outerBuf
= new void[__traits(classInstanceSize
, Outer
)];
4434 auto innerBuf
= new void[__traits(classInstanceSize
, Outer
.Inner
)];
4435 auto inner
= innerBuf
.emplace
!(Outer
.Inner
)(outerBuf
.emplace
!Outer
);
4436 assert(inner
.getI
== 3);
4439 @nogc pure nothrow @system unittest
4442 align(__conv_EmplaceTestClass
.alignof
) ubyte[__traits(classInstanceSize
, __conv_EmplaceTestClass
)] buf
;
4443 auto k
= emplace
!__conv_EmplaceTestClass(buf
, 5, var
);
4449 Given a raw memory area $(D chunk), constructs an object of non-$(D
4450 class) type $(D T) at that address. The constructor is passed the
4451 arguments $(D args), if any.
4454 $(D chunk) must be at least as large
4455 as $(D T) needs and should have an alignment multiple of $(D T)'s
4459 This function can be $(D @trusted) if the corresponding constructor of
4460 $(D T) is $(D @safe).
4462 Returns: A pointer to the newly constructed object.
4464 T
* emplace(T
, Args
...)(void[] chunk
, auto ref Args args
)
4465 if (!is(T
== class))
4467 testEmplaceChunk(chunk
, T
.sizeof
, T
.alignof
, T
.stringof
);
4468 emplaceRef
!(T
, Unqual
!T
)(*cast(Unqual
!T
*) chunk
.ptr
, args
);
4469 return cast(T
*) chunk
.ptr
;
4479 auto buf
= new void[S
.sizeof
];
4483 auto s1
= emplace
!S(buf
, s
);
4484 assert(s1
.a
== 42 && s1
.b
== 43);
4487 // Bulk of emplace unittests starts here
4489 @system unittest /* unions */
4504 assert(u1
.a
== "hello");
4507 version (unittest) private struct __conv_EmplaceTest
4512 assert(this.i
== 3 && i
== 5);
4515 this(int i
, ref int j
)
4517 assert(i
== 5 && j
== 6);
4528 version (unittest) private class __conv_EmplaceTestClass
4531 this(int i
) @nogc @safe pure nothrow
4533 assert(this.i
== 3 && i
== 5);
4536 this(int i
, ref int j
) @nogc @safe pure nothrow
4538 assert(i
== 5 && j
== 6);
4544 @system unittest // bugzilla 15772
4546 abstract class Foo
{}
4549 // test in emplaceInitializer
4550 static assert(!is(typeof(emplace
!Foo(cast(Foo
*) memory
.ptr
))));
4551 static assert( is(typeof(emplace
!Bar(cast(Bar
*) memory
.ptr
))));
4552 // test in the emplace overload that takes void[]
4553 static assert(!is(typeof(emplace
!Foo(memory
))));
4554 static assert( is(typeof(emplace
!Bar(memory
))));
4559 struct S
{ @disable this(); }
4561 static assert(!__traits(compiles
, emplace(&s
)));
4562 emplace(&s
, S
.init
);
4589 this(this) @disable;
4598 static assert(!__traits(compiles
, emplace(&ss2
)));
4601 static assert(!__traits(compiles
, emplace(&ss1
, s1
)));
4617 assert(ss1
[0].i
== 5 && ss1
[1].i
== 5);
4622 //Start testing emplace-args here
4629 K k
= null, k2
= new K
;
4645 void opAssign(S
){assert(0);}
4650 assert(sa
[0].i
== 5 && sa
[1].i
== 5);
4653 //Start testing emplace-struct here
4655 // Test constructor branch
4660 double x
= 5, y
= 6;
4663 assert(x
== 5 && y
== 6);
4669 auto s1
= new void[S
.sizeof
];
4670 auto s2
= S(42, 43);
4671 assert(*emplace
!S(cast(S
*) s1
.ptr
, s2
) == s2
);
4672 assert(*emplace
!S(cast(S
*) s1
, 44, 45) == S(44, 45));
4677 __conv_EmplaceTest k
= void;
4685 __conv_EmplaceTest k
= void;
4686 emplace(&k
, 5, var
);
4691 // Test matching fields branch
4694 struct S
{ uint n
; }
4702 struct S
{ int a
, b
; this(int){} }
4704 static assert(!__traits(compiles
, emplace
!S(&s
, 2, 3)));
4709 struct S
{ int a
, b
= 7; }
4710 S s1
= void, s2
= void;
4713 assert(s1
.a
== 2 && s1
.b
== 7);
4715 emplace
!S(&s2
, 2, 3);
4716 assert(s2
.a
== 2 && s2
.b
== 3);
4725 void opAssign(int){assert(0);}
4726 void opAssign(S
){assert(0);}
4737 //postblit precedence
4740 //Works, but breaks in "-w -O" because of @@@9332@@@.
4741 //Uncomment test when 9332 is fixed.
4746 this(S other
){assert(false);}
4747 this(int i
){this.i
= i
;}
4751 assert(is(typeof({S b
= a
;}))); //Postblit
4752 assert(is(typeof({S b
= S(a
);}))); //Constructor
4762 static assert(!is(immutable S2
: S2
));
4764 immutable is2
= (immutable S2
).init
;
4768 //nested structs and postblit
4774 this(int i
){p
= [i
].ptr
;}
4784 void opAssign(const SS
)
4792 assert(*ssa
.s
.p
== 5);
4793 assert(ssa
.s
.p
!= ssb
.s
.p
);
4802 @disable this(this);
4807 static assert(!__traits(compiles
, emplace(&s1
, S1
.init
)));
4812 @disable this(this);
4816 static assert(!__traits(compiles
, emplace(&s2
, 1)));
4817 emplace(&s2
, S2
.init
);
4825 static assert(!__traits(compiles
, emplace(&ss1
, SS1
.init
)));
4833 static assert(!__traits(compiles
, emplace(&ss2
, SS2
.init
)));
4836 // SS1 sss1 = s1; //This doesn't compile
4837 // SS1 sss1 = SS1(s1); //This doesn't compile
4838 // So emplace shouldn't compile either
4839 static assert(!__traits(compiles
, emplace(&sss1
, s1
)));
4840 static assert(!__traits(compiles
, emplace(&sss2
, s2
)));
4846 //Castable immutability
4852 static assert(is( immutable(S1
) : S1
));
4854 auto sb
= immutable(S1
)(5);
4858 //Un-castable immutability
4864 static assert(!is(immutable(S2
) : S2
));
4866 auto sb
= immutable(S2
)(null);
4867 assert(!__traits(compiles
, emplace(&sa
, sb
)));
4879 emplace(&s
, 1, null);
4880 emplace(&s
, 2, &s
.i
);
4881 assert(s
is S(2, &s
.i
));
4929 SS1 ss
= SS1(1, S(2));
4939 S
foo() @property{return s
;}
4943 SS2 ss
= SS2(1, S(2));
4954 this(__std_conv_SS ss
) {assert(0);}
4955 static opCall(__std_conv_SS ss
)
4957 __std_conv_S s
; s
.i
= ss
.j
;
4961 struct __std_conv_SS
4965 ref __std_conv_S
foo() return @property {s
.i
= j
; return s
;}
4968 static assert(is(__std_conv_SS
: __std_conv_S
));
4971 __std_conv_S s
= void;
4972 __std_conv_SS ss
= __std_conv_SS(1);
4974 __std_conv_S sTest1
= ss
; //this calls "SS alias this" (and not "S.this(SS)")
4975 emplace(&s
, ss
); //"alias this" should take precedence in emplace over "opCall"
4991 assert(s1
.a
is s2
.a
);
4994 //safety & nothrow & CTFE
4997 //emplace should be safe for anything with no elaborate opassign
5005 this(int j
)@safe nothrow{i
= j
;}
5016 void foo() @safe nothrow
5022 emplace(ps1
, S1
.init
);
5025 emplace(ps2
, S2
.init
);
5029 T
bar(T
)() @property
5031 T t
/+ = void+/; //CTFE void illegal
5037 static assert(a
== 5);
5039 static assert(b
.i
== 5);
5041 static assert(c
.i
== 5);
5056 int[2] get(){return [1, 2];}
5072 assert(ss
.ii
== [1, 2]);
5073 assert(iss
.ii
== [1, 2]);
5081 @disable void opAssign(S
);
5084 emplace(&s
, S
.init
);
5091 //Without constructor
5096 static S1
opCall(int*){assert(0);}
5099 static assert(!__traits(compiles
, emplace(&s
, 1)));
5106 static S2
opCall(int*){assert(0);}
5107 static S2
opCall(int){assert(0);}
5108 this(int i
){this.i
= i
;}
5114 //With postblit ambiguity
5119 static S3
opCall(ref S3
){assert(0);}
5122 emplace(&s
, S3
.init
);
5126 @safe unittest //@@@9559@@@
5128 import std
.algorithm
.iteration
: map
;
5129 import std
.array
: array
;
5130 import std
.typecons
: Nullable
;
5131 alias I
= Nullable
!int;
5132 auto ints
= [0, 1, 2].map
!(i
=> i
& 1 ? I
.init
: I(i
))();
5133 auto asArray
= array(ints
);
5136 @system unittest //http://forum.dlang.org/post/nxbdgtdlmwscocbiypjs@forum.dlang.org
5138 import std
.array
: array
;
5139 import std
.datetime
: SysTime
, UTC
;
5140 import std
.math
: isNaN
;
5152 assert(a
.i
.isNaN(), "why is 'j' zero?? and i is not NaN?");
5154 assert(!a
.i
.isNaN());
5156 SysTime when
; // comment this line avoid the breakage
5162 assert(&b1
); // verify that default eyes invariants are ok;
5164 auto b2
= B(SysTime(0, UTC()), 1, A(1));
5166 auto b3
= B(SysTime(0, UTC()), 1, A(1));
5169 auto arr
= [b2
, b3
];
5171 assert(arr
[0].j
== 1);
5172 assert(arr
[1].j
== 1);
5173 auto a2
= arr
.array(); // << bang, invariant is raised, also if b2 and b3 are good
5185 immutable int[2] ii
;
5196 static assert(!__traits(compiles
, {S ss
= S(uu
);}));
5197 static assert(!__traits(compiles
, emplace(&s
, uu
)));
5210 emplace(&sii
, sii2
);
5211 //emplace(&sii, uii2); //Sorry, this implementation doesn't know how to...
5212 //emplace(&uii, sii2); //Sorry, this implementation doesn't know how to...
5213 emplace(&uii
, uii2
);
5214 emplace(&sii
, sii2
[]);
5215 //emplace(&sii, uii2[]); //Sorry, this implementation doesn't know how to...
5216 //emplace(&uii, sii2[]); //Sorry, this implementation doesn't know how to...
5217 emplace(&uii
, uii2
[]);
5222 bool allowDestruction
= false;
5227 ~this(){assert(allowDestruction
);}
5235 emplace(&ss3
, ss2
[]);
5236 assert(ss1
[1] == s
);
5237 assert(ss2
[1] == s
);
5238 assert(ss3
[1] == s
);
5239 allowDestruction
= true;
5244 //Checks postblit, construction, and context pointer
5274 S
[2][2][2] sss
= void;
5278 @system unittest //Constness
5283 emplaceRef
!(const int)(a
, 5);
5286 const(int)* p
= void;
5287 emplaceRef
!(const int*)(p
, &i
);
5293 alias IS
= immutable(S
);
5295 emplaceRef
!IS(s
, IS());
5297 emplaceRef
!(IS
[2])(ss
, IS());
5299 IS
[2] iss
= IS
.init
;
5300 emplaceRef
!(IS
[2])(ss
, iss
);
5301 emplaceRef
!(IS
[2])(ss
, iss
[]);
5304 pure nothrow @safe @nogc unittest
5310 emplaceRef
!int(i
, 5);
5313 // Test attribute propagation for UDTs
5314 pure nothrow @safe /* @nogc */ unittest
5318 this(this) pure nothrow @safe @nogc {}
5322 emplaceRef(safe
, Safe());
5324 Safe
[1] safeArr
= [Safe()];
5325 Safe
[1] uninitializedSafeArr
= void;
5326 emplaceRef(uninitializedSafeArr
, safe
);
5327 emplaceRef(uninitializedSafeArr
, safeArr
);
5329 static struct Unsafe
5331 this(this) @system {}
5334 Unsafe unsafe
= void;
5335 static assert(!__traits(compiles
, emplaceRef(unsafe
, Unsafe())));
5337 Unsafe
[1] unsafeArr
= [Unsafe()];
5338 Unsafe
[1] uninitializedUnsafeArr
= void;
5339 static assert(!__traits(compiles
, emplaceRef(uninitializedUnsafeArr
, unsafe
)));
5340 static assert(!__traits(compiles
, emplaceRef(uninitializedUnsafeArr
, unsafeArr
)));
5353 import core
.stdc
.stdlib
: malloc
;
5354 void[] buf
= malloc(Node
.sizeof
)[0 .. Node
.sizeof
];
5356 import std
.conv
: emplace
;
5357 const Node
* n
= emplace
!(const Node
)(buf
, 42, null, 10);
5358 assert(n
.payload
== 42);
5359 assert(n
.next
== null);
5360 assert(n
.refs
== 10);
5366 auto k
= emplace
!__conv_EmplaceTest(new void[__conv_EmplaceTest
.sizeof
], 5, var
);
5379 assert(x
== 5 && y
== 42);
5385 static align(A
.alignof
) byte[__traits(classInstanceSize
, A
)] sbuf
;
5387 auto a
= emplace
!A(buf
, 55);
5388 assert(a
.x
== 55 && a
.y
== 55);
5390 // emplace in bigger buffer
5391 buf
= new byte[](__traits(classInstanceSize
, A
) + 10);
5392 a
= emplace
!A(buf
, 55);
5393 assert(a
.x
== 55 && a
.y
== 55);
5396 static assert(!is(typeof(emplace
!A(buf
))));
5398 // Bulk of emplace unittests ends here
5402 import std
.algorithm
.comparison
: equal
;
5403 import std
.algorithm
.iteration
: map
;
5404 // Check fix for http://d.puremagic.com/issues/show_bug.cgi?id=2971
5405 assert(equal(map
!(to
!int)(["42", "34", "345"]), [42, 34, 345]));
5408 // Undocumented for the time being
5409 void toTextRange(T
, W
)(T value
, W writer
)
5410 if (isIntegral
!T
&& isOutputRange
!(W
, char))
5412 import core
.internal
.string
: SignedStringBuf
, signedToTempString
,
5413 UnsignedStringBuf
, unsignedToTempString
;
5417 SignedStringBuf buf
= void;
5418 put(writer
, signedToTempString(value
, buf
, 10));
5422 UnsignedStringBuf buf
= void;
5423 put(writer
, unsignedToTempString(value
, buf
, 10));
5429 import std
.array
: appender
;
5430 auto result
= appender
!(char[])();
5431 toTextRange(-1, result
);
5432 assert(result
.data
== "-1");
5437 Returns the corresponding _unsigned value for $(D x) (e.g. if $(D x) has type
5438 $(D int), it returns $(D cast(uint) x)). The advantage compared to the cast
5439 is that you do not need to rewrite the cast if $(D x) later changes type
5440 (e.g from $(D int) to $(D long)).
5442 Note that the result is always mutable even if the original type was const
5443 or immutable. In order to retain the constness, use $(REF Unsigned, std,traits).
5445 auto unsigned(T
)(T x
)
5448 return cast(Unqual
!(Unsigned
!T
))x
;
5454 import std
.traits
: Unsigned
;
5455 immutable int s
= 42;
5456 auto u1
= unsigned(s
); //not qualified
5457 static assert(is(typeof(u1
) == uint));
5458 Unsigned
!(typeof(s
)) u2
= unsigned(s
); //same qualification
5459 static assert(is(typeof(u2
) == immutable uint));
5460 immutable u3
= unsigned(s
); //explicitly qualified
5465 foreach (T
; AliasSeq
!(byte, ubyte))
5467 static assert(is(typeof(unsigned(cast(T
) 1)) == ubyte));
5468 static assert(is(typeof(unsigned(cast(const T
) 1)) == ubyte));
5469 static assert(is(typeof(unsigned(cast(immutable T
) 1)) == ubyte));
5472 foreach (T
; AliasSeq
!(short, ushort))
5474 static assert(is(typeof(unsigned(cast(T
) 1)) == ushort));
5475 static assert(is(typeof(unsigned(cast(const T
) 1)) == ushort));
5476 static assert(is(typeof(unsigned(cast(immutable T
) 1)) == ushort));
5479 foreach (T
; AliasSeq
!(int, uint))
5481 static assert(is(typeof(unsigned(cast(T
) 1)) == uint));
5482 static assert(is(typeof(unsigned(cast(const T
) 1)) == uint));
5483 static assert(is(typeof(unsigned(cast(immutable T
) 1)) == uint));
5486 foreach (T
; AliasSeq
!(long, ulong))
5488 static assert(is(typeof(unsigned(cast(T
) 1)) == ulong));
5489 static assert(is(typeof(unsigned(cast(const T
) 1)) == ulong));
5490 static assert(is(typeof(unsigned(cast(immutable T
) 1)) == ulong));
5494 auto unsigned(T
)(T x
)
5497 // All characters are unsigned
5498 static assert(T
.min
== 0);
5499 return cast(Unqual
!T
) x
;
5504 foreach (T
; AliasSeq
!(char, wchar, dchar))
5506 static assert(is(typeof(unsigned(cast(T
)'A')) == T
));
5507 static assert(is(typeof(unsigned(cast(const T
)'A')) == T
));
5508 static assert(is(typeof(unsigned(cast(immutable T
)'A')) == T
));
5514 Returns the corresponding _signed value for $(D x) (e.g. if $(D x) has type
5515 $(D uint), it returns $(D cast(int) x)). The advantage compared to the cast
5516 is that you do not need to rewrite the cast if $(D x) later changes type
5517 (e.g from $(D uint) to $(D ulong)).
5519 Note that the result is always mutable even if the original type was const
5520 or immutable. In order to retain the constness, use $(REF Signed, std,traits).
5525 return cast(Unqual
!(Signed
!T
))x
;
5531 import std
.traits
: Signed
;
5533 immutable uint u
= 42;
5534 auto s1
= signed(u
); //not qualified
5535 static assert(is(typeof(s1
) == int));
5536 Signed
!(typeof(u
)) s2
= signed(u
); //same qualification
5537 static assert(is(typeof(s2
) == immutable int));
5538 immutable s3
= signed(u
); //explicitly qualified
5543 foreach (T
; AliasSeq
!(byte, ubyte))
5545 static assert(is(typeof(signed(cast(T
) 1)) == byte));
5546 static assert(is(typeof(signed(cast(const T
) 1)) == byte));
5547 static assert(is(typeof(signed(cast(immutable T
) 1)) == byte));
5550 foreach (T
; AliasSeq
!(short, ushort))
5552 static assert(is(typeof(signed(cast(T
) 1)) == short));
5553 static assert(is(typeof(signed(cast(const T
) 1)) == short));
5554 static assert(is(typeof(signed(cast(immutable T
) 1)) == short));
5557 foreach (T
; AliasSeq
!(int, uint))
5559 static assert(is(typeof(signed(cast(T
) 1)) == int));
5560 static assert(is(typeof(signed(cast(const T
) 1)) == int));
5561 static assert(is(typeof(signed(cast(immutable T
) 1)) == int));
5564 foreach (T
; AliasSeq
!(long, ulong))
5566 static assert(is(typeof(signed(cast(T
) 1)) == long));
5567 static assert(is(typeof(signed(cast(const T
) 1)) == long));
5568 static assert(is(typeof(signed(cast(immutable T
) 1)) == long));
5582 Returns the representation of an enumerated value, i.e. the value converted to
5583 the base type of the enumeration.
5585 OriginalType
!E
asOriginalType(E
)(E value
) if (is(E
== enum))
5594 static assert(is(typeof(A
.a
.asOriginalType
) == int));
5595 assert(A
.a
.asOriginalType
== 42);
5596 enum B
: double { a
= 43 }
5597 static assert(is(typeof(B
.a
.asOriginalType
) == double));
5598 assert(B
.a
.asOriginalType
== 43);
5602 A wrapper on top of the built-in cast operator that allows one to restrict
5603 casting of the original type of the value.
5605 A common issue with using a raw cast is that it may silently continue to
5606 compile even if the value's type has changed during refactoring,
5607 which breaks the initial assumption about the cast.
5610 From = The type to cast from. The programmer must ensure it is legal
5613 template castFrom(From
)
5617 To = The type _to cast _to.
5618 value = The value _to cast. It must be of type $(D From),
5619 otherwise a compile-time error is emitted.
5622 the value after the cast, returned by reference if possible.
5624 auto ref to(To
, T
)(auto ref T value
) @system
5628 "the value to cast is not of specified type '" ~ From
.stringof
~
5629 "', it is of type '" ~ T
.stringof
~ "'"
5633 is(typeof(cast(To
) value
)),
5634 "can't cast from '" ~ From
.stringof
~ "' to '" ~ To
.stringof
~ "'"
5637 return cast(To
) value
;
5644 // Regular cast, which has been verified to be legal by the programmer:
5647 auto y
= cast(int) x
;
5650 // However this will still compile if 'x' is changed to be a pointer:
5653 auto y
= cast(int) x
;
5656 // castFrom provides a more reliable alternative to casting:
5659 auto y
= castFrom
!long.to
!int(x
);
5662 // Changing the type of 'x' will now issue a compiler error,
5663 // allowing bad casts to be caught before it's too late:
5667 !__traits(compiles
, castFrom
!long.to
!int(x
))
5670 // if cast is still needed, must be changed to:
5671 auto y
= castFrom
!(long*).to
!int(x
);
5675 // https://issues.dlang.org/show_bug.cgi?id=16667
5678 ubyte[] a
= ['a', 'b', 'c'];
5679 assert(castFrom
!(ubyte[]).to
!(string
)(a
) == "abc");
5683 Check the correctness of a string for $(D hexString).
5684 The result is true if and only if the input string is composed of whitespace
5685 characters (\f\n\r\t\v lineSep paraSep nelSep) and
5686 an even number of hexadecimal digits (regardless of the case).
5689 private bool isHexLiteral(String
)(scope const String hexData
)
5691 import std
.ascii
: isHexDigit
;
5692 import std
.uni
: lineSep
, paraSep
, nelSep
;
5694 foreach (const dchar c
; hexData
)
5722 // test all the hex digits
5723 static assert( ("0123456789abcdefABCDEF").isHexLiteral
);
5724 // empty or white strings are not valid
5725 static assert( "\r\n\t".isHexLiteral
);
5726 // but are accepted if the count of hex digits is even
5727 static assert( "A\r\n\tB".isHexLiteral
);
5734 static assert( "".isHexLiteral
);
5735 static assert( " \r".isHexLiteral
);
5736 static assert( whitespace
.isHexLiteral
);
5737 static assert( ""w
.isHexLiteral
);
5738 static assert( " \r"w
.isHexLiteral
);
5739 static assert( ""d
.isHexLiteral
);
5740 static assert( " \r"d
.isHexLiteral
);
5741 static assert( "\u2028\u2029\u0085"d
.isHexLiteral
);
5743 static assert( !("5" ~ whitespace
).isHexLiteral
);
5744 static assert( !"123".isHexLiteral
);
5745 static assert( !"1A3".isHexLiteral
);
5746 static assert( !"1 23".isHexLiteral
);
5747 static assert( !"\r\n\tC".isHexLiteral
);
5748 static assert( !"123"w
.isHexLiteral
);
5749 static assert( !"1A3"w
.isHexLiteral
);
5750 static assert( !"1 23"w
.isHexLiteral
);
5751 static assert( !"\r\n\tC"w
.isHexLiteral
);
5752 static assert( !"123"d
.isHexLiteral
);
5753 static assert( !"1A3"d
.isHexLiteral
);
5754 static assert( !"1 23"d
.isHexLiteral
);
5755 static assert( !"\r\n\tC"d
.isHexLiteral
);
5756 // even x strings with invalid charset
5757 static assert( !"12gG".isHexLiteral
);
5758 static assert( !"2A 3q".isHexLiteral
);
5759 static assert( !"12gG"w
.isHexLiteral
);
5760 static assert( !"2A 3q"w
.isHexLiteral
);
5761 static assert( !"12gG"d
.isHexLiteral
);
5762 static assert( !"2A 3q"d
.isHexLiteral
);
5764 static assert( ("5A" ~ whitespace
).isHexLiteral
);
5765 static assert( ("5A 01A C FF de 1b").isHexLiteral
);
5766 static assert( ("0123456789abcdefABCDEF").isHexLiteral
);
5767 static assert( (" 012 34 5 6789 abcd ef\rAB\nCDEF").isHexLiteral
);
5768 static assert( ("5A 01A C FF de 1b"w
).isHexLiteral
);
5769 static assert( ("0123456789abcdefABCDEF"w
).isHexLiteral
);
5770 static assert( (" 012 34 5 6789 abcd ef\rAB\nCDEF"w
).isHexLiteral
);
5771 static assert( ("5A 01A C FF de 1b"d
).isHexLiteral
);
5772 static assert( ("0123456789abcdefABCDEF"d
).isHexLiteral
);
5773 static assert( (" 012 34 5 6789 abcd ef\rAB\nCDEF"d
).isHexLiteral
);
5774 // library version allows what's pointed by issue 10454
5775 static assert( ("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").isHexLiteral
);
5779 Converts a hex literal to a string at compile time.
5781 Takes a string made of hexadecimal digits and returns
5782 the matching string by converting each pair of digits to a character.
5783 The input string can also include white characters, which can be used
5784 to keep the literal string readable in the source code.
5786 The function is intended to replace the hexadecimal literal strings
5787 starting with $(D 'x'), which could be removed to simplify the core language.
5790 hexData = string to be converted.
5793 a $(D string), a $(D wstring) or a $(D dstring), according to the type of hexData.
5795 template hexString(string hexData
)
5796 if (hexData
.isHexLiteral
)
5798 immutable hexString
= hexStrImpl(hexData
);
5802 template hexString(wstring hexData
)
5803 if (hexData
.isHexLiteral
)
5805 immutable hexString
= hexStrImpl(hexData
);
5809 template hexString(dstring hexData
)
5810 if (hexData
.isHexLiteral
)
5812 immutable hexString
= hexStrImpl(hexData
);
5818 // conversion at compile time
5819 auto string1
= hexString
!"304A314B";
5820 assert(string1
== "0J1K");
5821 auto string2
= hexString
!"304A314B"w
;
5822 assert(string2
== "0J1K"w
);
5823 auto string3
= hexString
!"304A314B"d
;
5824 assert(string3
== "0J1K"d
);
5828 Takes a hexadecimal string literal and returns its representation.
5829 hexData is granted to be a valid string by the caller.
5830 C is granted to be a valid char type by the caller.
5833 private auto hexStrImpl(String
)(scope String hexData
)
5835 import std
.ascii
: isHexDigit
;
5836 alias C
= Unqual
!(ElementEncodingType
!String
);
5838 result
.length
= hexData
.length
/ 2;
5841 foreach (c
; hexData
)
5846 if (c
>= '0' && c
<= '9')
5847 x
= cast(ubyte)(c
- '0');
5848 else if (c
>= 'a' && c
<= 'f')
5849 x
= cast(ubyte)(c
- ('a' - 10));
5850 else if (c
>= 'A' && c
<= 'F')
5851 x
= cast(ubyte)(c
- ('A' - 10));
5854 v
= cast(ubyte)((v
<< 4) | x
);
5855 result
[cnt
/ 2] = v
;
5862 result
.length
= cnt
/ 2;
5869 assert(hexString
!"46 47 48 49 4A 4B" == "FGHIJK");
5870 assert(hexString
!"30\r\n\t\f\v31 32 33 32 31 30" == "0123210");
5871 assert(hexString
!"ab cd" == hexString
!"ABCD");
5876 * Convert integer to a range of characters.
5877 * Intended to be lightweight and fast.
5880 * radix = 2, 8, 10, 16
5881 * Char = character type for output
5882 * letterCase = lower for deadbeef, upper for DEADBEEF
5883 * value = integer to convert. Can be uint or ulong. If radix is 10, can also be
5886 * Random access range with slicing and everything
5889 auto toChars(ubyte radix
= 10, Char
= char, LetterCase letterCase
= LetterCase
.lower
, T
)(T value
)
5890 pure nothrow @nogc @safe
5891 if ((radix
== 2 || radix
== 8 || radix
== 10 || radix
== 16) &&
5892 (is(Unqual
!T
== uint) ||
is(Unqual
!T
== ulong) ||
5893 radix
== 10 && (is(Unqual
!T
== int) ||
is(Unqual
!T
== long))))
5895 alias UT
= Unqual
!T
;
5897 static if (radix
== 10)
5899 /* uint.max is 42_9496_7295
5900 * int.max is 21_4748_3647
5901 * ulong.max is 1844_6744_0737_0955_1615
5902 * long.max is 922_3372_0368_5477_5807
5904 static struct Result
5906 void initialize(UT value
)
5915 buf
[0] = cast(char)(cast(uint) value
+ '0');
5921 auto i
= cast(uint) buf
.length
- 1;
5922 while (cast(Unsigned
!UT
) value
>= 10)
5924 buf
[i
] = cast(ubyte)('0' + cast(Unsigned
!UT
) value
% 10);
5925 value
= unsigned(value
) / 10;
5928 buf
[i
] = cast(char)(cast(uint) value
+ '0');
5935 upr
= cast(uint) buf
.length
;
5938 @property size_t
length() { return upr
- lwr
; }
5940 alias opDollar
= length
;
5942 @property bool empty() { return upr
== lwr
; }
5944 @property Char
front() { return buf
[lwr
]; }
5946 void popFront() { ++lwr
; }
5948 @property Char
back() { return buf
[upr
- 1]; }
5950 void popBack() { --upr
; }
5952 @property Result
save() { return this; }
5954 Char
opIndex(size_t i
) { return buf
[lwr
+ i
]; }
5956 Result
opSlice(size_t lwr
, size_t upr
)
5958 Result result
= void;
5960 result
.lwr
= cast(uint)(this.lwr
+ lwr
);
5961 result
.upr
= cast(uint)(this.lwr
+ upr
);
5966 uint lwr
= void, upr
= void;
5967 char[(UT
.sizeof
== 4) ?
10 + isSigned
!T
: 20] buf
= void;
5970 Result result
= void;
5971 result
.initialize(value
);
5976 static if (radix
== 2)
5978 else static if (radix
== 8)
5980 else static if (radix
== 16)
5984 static struct Result
5991 while (value
>>>= SHIFT
)
5996 @property size_t
length() { return len
; }
5998 @property bool empty() { return len
== 0; }
6000 @property Char
front() { return opIndex(0); }
6002 void popFront() { --len
; }
6004 @property Char
back() { return opIndex(len
- 1); }
6012 @property Result
save() { return this; }
6014 Char
opIndex(size_t i
)
6016 Char c
= (value
>>> ((len
- i
- 1) * SHIFT
)) & ((1 << SHIFT
) - 1);
6017 return cast(Char
)((radix
< 10 || c
< 10) ? c
+ '0'
6018 : (letterCase
== LetterCase
.upper ? c
+ 'A' - 10
6022 Result
opSlice(size_t lwr
, size_t upr
)
6024 Result result
= void;
6025 result
.value
= value
>>> ((len
- upr
) * SHIFT
);
6026 result
.len
= cast(ubyte)(upr
- lwr
);
6035 return Result(value
);
6046 assert(toChars
!2(0u).array
== "0");
6047 assert(toChars
!2(0Lu).array
== "0");
6048 assert(toChars
!2(1u).array
== "1");
6049 assert(toChars
!2(1Lu).array
== "1");
6051 auto r
= toChars
!2(2u);
6052 assert(r
.length
== 2);
6053 assert(r
[0] == '1');
6054 assert(r
[1 .. 2].array
== "0");
6056 assert(r
.array
== "10");
6057 assert(s
.retro
.array
== "01");
6060 assert(toChars
!8(0u).array
== "0");
6061 assert(toChars
!8(0Lu).array
== "0");
6062 assert(toChars
!8(1u).array
== "1");
6063 assert(toChars
!8(1234567Lu).array
== "4553207");
6065 auto r
= toChars
!8(8u);
6066 assert(r
.length
== 2);
6067 assert(r
[0] == '1');
6068 assert(r
[1 .. 2].array
== "0");
6070 assert(r
.array
== "10");
6071 assert(s
.retro
.array
== "01");
6074 assert(toChars
!10(0u).array
== "0");
6075 assert(toChars
!10(0Lu).array
== "0");
6076 assert(toChars
!10(1u).array
== "1");
6077 assert(toChars
!10(1234567Lu).array
== "1234567");
6078 assert(toChars
!10(uint.max
).array
== "4294967295");
6079 assert(toChars
!10(ulong.max
).array
== "18446744073709551615");
6081 auto r
= toChars(10u);
6082 assert(r
.length
== 2);
6083 assert(r
[0] == '1');
6084 assert(r
[1 .. 2].array
== "0");
6086 assert(r
.array
== "10");
6087 assert(s
.retro
.array
== "01");
6090 assert(toChars
!10(0).array
== "0");
6091 assert(toChars
!10(0L).array
== "0");
6092 assert(toChars
!10(1).array
== "1");
6093 assert(toChars
!10(1234567L).array
== "1234567");
6094 assert(toChars
!10(int.max
).array
== "2147483647");
6095 assert(toChars
!10(long.max
).array
== "9223372036854775807");
6096 assert(toChars
!10(-int.max
).array
== "-2147483647");
6097 assert(toChars
!10(-long.max
).array
== "-9223372036854775807");
6098 assert(toChars
!10(int.min
).array
== "-2147483648");
6099 assert(toChars
!10(long.min
).array
== "-9223372036854775808");
6101 auto r
= toChars
!10(10);
6102 assert(r
.length
== 2);
6103 assert(r
[0] == '1');
6104 assert(r
[1 .. 2].array
== "0");
6106 assert(r
.array
== "10");
6107 assert(s
.retro
.array
== "01");
6110 assert(toChars
!(16)(0u).array
== "0");
6111 assert(toChars
!(16)(0Lu).array
== "0");
6112 assert(toChars
!(16)(10u).array
== "a");
6113 assert(toChars
!(16, char, LetterCase
.upper
)(0x12AF34567Lu
).array
== "12AF34567");
6115 auto r
= toChars
!(16)(16u);
6116 assert(r
.length
== 2);
6117 assert(r
[0] == '1');
6118 assert(r
[1 .. 2].array
== "0");
6120 assert(r
.array
== "10");
6121 assert(s
.retro
.array
== "01");
6125 @safe unittest // opSlice (issue 16192)
6127 import std
.meta
: AliasSeq
;
6129 static struct Test
{ ubyte radix
; uint number
; }
6131 alias tests
= AliasSeq
!(
6132 Test(2, 0b1_0110_0111u),
6133 Test(2, 0b10_1100_1110u),
6134 Test(8, octal
!123456701u),
6135 Test(8, octal
!1234567012u),
6136 Test(10, 123456789u),
6137 Test(10, 1234567890u),
6138 Test(16, 0x789ABCDu
),
6139 Test(16, 0x789ABCDEu
),
6142 foreach (test; tests
)
6144 enum ubyte radix
= test.radix
;
6145 auto original
= toChars
!radix(test.number
);
6147 // opSlice vs popFront
6148 auto r
= original
.save
;
6150 for (; !r
.empty
; r
.popFront(), ++i
)
6152 assert(original
[i
.. original
.length
].tupleof
== r
.tupleof
);
6153 // tupleof is used to work around issue 16216.
6156 // opSlice vs popBack
6159 for (; !r
.empty
; r
.popBack(), ++i
)
6161 assert(original
[0 .. original
.length
- i
].tupleof
== r
.tupleof
);
6164 // opSlice vs both popFront and popBack
6167 for (; r
.length
>= 2; r
.popFront(), r
.popBack(), ++i
)
6169 assert(original
[i
.. original
.length
- i
].tupleof
== r
.tupleof
);