1 // Written in the D programming language.
4 A one-stop shop for converting values from one type to another.
6 $(SCRIPT inhibitQuickIndex = 1;)
9 $(TR $(TH Category) $(TH Functions))
10 $(TR $(TD Generic) $(TD
11 $(LREF asOriginalType)
17 $(TR $(TD Strings) $(TD
23 $(TR $(TD Numeric) $(TD
29 $(TR $(TD Exceptions) $(TD
31 $(LREF ConvOverflowException)
35 Copyright: Copyright The D Language Foundation 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
;
55 import std
.typecons
: Flag
, Yes
, No
, tuple
, isTuple
;
57 // Same as std.string.format, but "self-importing".
58 // Helps reduce code and imports, particularly in static asserts.
59 // Also helps with missing imports errors.
60 package template convFormat()
62 import std
.format
: format
;
63 alias convFormat
= format
;
66 /* ************* Exceptions *************** */
69 * Thrown on conversion errors.
71 class ConvException
: Exception
73 import std
.exception
: basicExceptionCtors
;
75 mixin basicExceptionCtors
;
81 import std
.exception
: assertThrown
;
82 assertThrown
!ConvException(to
!int("abc"));
85 private auto convError(S
, T
)(S source
, string fn
= __FILE__
, size_t ln
= __LINE__
)
90 msg
= "Unexpected end of input when converting from type " ~ S
.stringof
~ " to type " ~ T
.stringof
;
93 ElementType
!S el
= source
.front
;
96 msg
= text("Unexpected '\\n' when converting from type " ~ S
.stringof
~ " to type " ~ T
.stringof
);
98 msg
= text("Unexpected '", el
,
99 "' when converting from type " ~ S
.stringof
~ " to type " ~ T
.stringof
);
102 return new ConvException(msg
, fn
, ln
);
105 @safe pure/* nothrow*/ // lazy parameter bug
106 private auto parseError(lazy string msg
, string fn
= __FILE__
, size_t ln
= __LINE__
)
108 return new ConvException(text("Can't parse string: ", msg
), fn
, ln
);
111 private void parseCheck(alias source
)(dchar c
, string fn
= __FILE__
, size_t ln
= __LINE__
)
114 throw parseError(text("unexpected end of input when expecting \"", c
, "\""));
115 if (source
.front
!= c
)
116 throw parseError(text("\"", c
, "\" is missing"), fn
, ln
);
125 // workaround for https://issues.dlang.org/show_bug.cgi?id=14198
126 static if (is(S
== bool) && is(typeof({ T s
= "string"; })))
128 return src ?
"true" : "false";
132 import std
.array
: appender
;
133 import std
.format
.spec
: FormatSpec
;
134 import std
.format
.write
: formatValue
;
136 auto w
= appender
!T();
137 FormatSpec
!(ElementEncodingType
!T
) f
;
138 formatValue(w
, src
, f
);
143 template isExactSomeString(T
)
145 enum isExactSomeString
= isSomeString
!T
&& !is(T
== enum);
148 template isEnumStrToStr(S
, T
)
150 enum isEnumStrToStr
= is(S
: T
) &&
151 is(S
== enum) && isExactSomeString
!T
;
153 template isNullToStr(S
, T
)
155 enum isNullToStr
= is(S
: T
) &&
156 (is(immutable S
== immutable typeof(null))) && isExactSomeString
!T
;
161 * Thrown on conversion overflow errors.
163 class ConvOverflowException
: ConvException
166 this(string s
, string fn
= __FILE__
, size_t ln
= __LINE__
)
175 import std
.exception
: assertThrown
;
176 assertThrown
!ConvOverflowException(to
!ubyte(1_000_000));
180 The `to` template converts a value from one type _to another.
181 The source type is deduced and the target type must be specified, for example the
182 expression `to!int(42.0)` converts the number 42 from
183 `double` _to `int`. The conversion is "safe", i.e.,
184 it checks for overflow; `to!int(4.2e10)` would throw the
185 `ConvOverflowException` exception. Overflow checks are only
186 inserted when necessary, e.g., `to!double(42)` does not do
187 any checking because any `int` fits in a `double`.
189 Conversions from string _to numeric types differ from the C equivalents
190 `atoi()` and `atol()` by checking for overflow and not allowing whitespace.
192 For conversion of strings _to signed types, the grammar recognized is:
194 $(I Sign UnsignedInteger)
200 For conversion _to unsigned types, the grammar recognized is:
201 $(PRE $(I UnsignedInteger):
203 $(I DecimalDigit) $(I UnsignedInteger))
210 return toImpl
!T(args
);
213 // Fix https://issues.dlang.org/show_bug.cgi?id=6175
217 return toImpl
!T(arg
);
220 // Fix https://issues.dlang.org/show_bug.cgi?id=16108
222 if (isAggregateType
!S
&& !isCopyable
!S
)
224 return toImpl
!T(arg
);
229 * Converting a value _to its own type (useful mostly for generic code)
230 * simply returns its argument.
236 double c
= to
!double(3.14); // c is double with value 3.14
240 * Converting among numeric types is a safe way _to cast them around.
242 * Conversions from floating-point types _to integral types allow loss of
243 * precision (the fractional part of a floating-point number). The
244 * conversion is truncating towards zero, the same way a cast would
245 * truncate. (_To round a floating point value when casting _to an
246 * integral, use `roundTo`.)
250 import std
.exception
: assertThrown
;
253 assert(to
!long(a
) == a
);
254 assertThrown
!ConvOverflowException(to
!byte(a
));
256 assert(to
!int(4.2e6
) == 4200000);
257 assertThrown
!ConvOverflowException(to
!uint(-3.14));
258 assert(to
!uint(3.14) == 3);
259 assert(to
!uint(3.99) == 3);
260 assert(to
!int(-3.99) == -3);
264 * When converting strings _to numeric types, note that D hexadecimal and binary
265 * literals are not handled. Neither the prefixes that indicate the base, nor the
266 * horizontal bar used _to separate groups of digits are recognized. This also
267 * applies to the suffixes that indicate the type.
269 * _To work around this, you can specify a radix for conversions involving numbers.
273 auto str = to
!string(42, 16);
275 auto i
= to
!int(str, 16);
280 * Conversions from integral types _to floating-point types always
281 * succeed, but might lose accuracy. The largest integers with a
282 * predecessor representable in floating-point format are `2^24-1` for
283 * `float`, `2^53-1` for `double`, and `2^64-1` for `real` (when
284 * `real` is 80-bit, e.g. on Intel machines).
288 // 2^24 - 1, largest proper integer representable as float
290 assert(to
!int(to
!float(a
)) == a
);
291 assert(to
!int(to
!float(-a
)) == -a
);
295 Conversion from string types to char types enforces the input
296 to consist of a single code point, and said code point must
297 fit in the target type. Otherwise, $(LREF ConvException) is thrown.
301 import std
.exception
: assertThrown
;
303 assert(to
!char("a") == 'a');
304 assertThrown(to
!char("ñ")); // 'ñ' does not fit into a char
305 assert(to
!wchar("ñ") == 'ñ');
306 assertThrown(to
!wchar("😃")); // '😃' does not fit into a wchar
307 assert(to
!dchar("😃") == '😃');
309 // Using wstring or dstring as source type does not affect the result
310 assert(to
!char("a"w
) == 'a');
311 assert(to
!char("a"d
) == 'a');
313 // Two code points cannot be converted to a single one
314 assertThrown(to
!char("ab"));
318 * Converting an array _to another array type works by converting each
319 * element in turn. Associative arrays can be converted _to associative
320 * arrays as long as keys and values can in turn be converted.
324 import std
.string
: split
;
327 auto b
= to
!(float[])(a
);
328 assert(b
== [1.0f, 2, 3]);
329 string
str = "1 2 3 4 5 6";
330 auto numbers
= to
!(double[])(split(str));
331 assert(numbers
== [1.0, 2, 3, 4, 5, 6]);
335 auto d
= to
!(double[wstring
])(c
);
336 assert(d
["a"w
] == 1 && d
["b"w
] == 2);
340 * Conversions operate transitively, meaning that they work on arrays and
341 * associative arrays of any complexity.
343 * This conversion works because `to!short` applies _to an `int`, `to!wstring`
344 * applies _to a `string`, `to!string` applies _to a `double`, and
345 * `to!(double[])` applies _to an `int[]`. The conversion might throw an
346 * exception because `to!short` might fail the range check.
350 int[string
][double[int[]]] a
;
351 auto b
= to
!(short[wstring
][string
[double[]]])(a
);
355 * Object-to-object conversions by dynamic casting throw exception when
356 * the source is non-null and the target is null.
360 import std
.exception
: assertThrown
;
361 // Testing object conversions
365 A a1
= new A
, a2
= new B
, a3
= new C
;
366 assert(to
!B(a2
) is a2
);
367 assert(to
!C(a3
) is a3
);
368 assertThrown
!ConvException(to
!B(a3
));
372 * Stringize conversion from all types is supported.
374 * $(LI String _to string conversion works for any two string types having
375 * (`char`, `wchar`, `dchar`) character widths and any
376 * combination of qualifiers (mutable, `const`, or `immutable`).)
377 * $(LI Converts array (other than strings) _to string.
378 * Each element is converted by calling `to!T`.)
379 * $(LI Associative array _to string conversion.
380 * Each element is converted by calling `to!T`.)
381 * $(LI Object _to string conversion calls `toString` against the object or
382 * returns `"null"` if the object is null.)
383 * $(LI Struct _to string conversion calls `toString` against the struct if
385 * $(LI For structs that do not define `toString`, the conversion _to string
386 * produces the list of fields.)
387 * $(LI Enumerated types are converted _to strings as their symbolic names.)
388 * $(LI Boolean values are converted to `"true"` or `"false"`.)
389 * $(LI `char`, `wchar`, `dchar` _to a string type.)
390 * $(LI Unsigned or signed integers _to strings.
391 * $(DL $(DT [special case])
392 * $(DD Convert integral value _to string in $(D_PARAM radix) radix.
393 * radix must be a value from 2 to 36.
394 * value is treated as a signed value only if radix is 10.
395 * The characters A through Z are used to represent values 10 through 36
396 * and their case is determined by the $(D_PARAM letterCase) parameter.)))
397 * $(LI All floating point types _to all string types.)
398 * $(LI Pointer to string conversions convert the pointer to a `size_t` value.
399 * If pointer is `char*`, treat it as C-style strings.
400 * In that case, this function is `@system`.))
401 * See $(REF formatValue, std,format) on how `toString` should be defined.
403 @system pure unittest // @system due to cast and ptr
405 // Conversion representing dynamic/static array with string
406 long[] a
= [ 1, 3, 5 ];
407 assert(to
!string(a
) == "[1, 3, 5]");
409 // Conversion representing associative array with string
410 int[string
] associativeArray
= ["0":1, "1":2];
411 assert(to
!string(associativeArray
) == `["0":1, "1":2]` ||
412 to
!string(associativeArray
) == `["1":2, "0":1]`);
414 // char* to string conversion
415 assert(to
!string(cast(char*) null) == "");
416 assert(to
!string("foo\0".ptr
) == "foo");
418 // Conversion reinterpreting void array to string
421 assert(b
.length
== 8);
423 auto c
= to
!(wchar[])(b
);
428 * Strings can be converted to enum types. The enum member with the same name as the
429 * input string is returned. The comparison is case-sensitive.
431 * A $(LREF ConvException) is thrown if the enum does not have the specified member.
435 import std
.exception
: assertThrown
;
438 assert(to
!E("a") == E
.a
);
439 assert(to
!E("b") == E
.b
);
440 assertThrown
!ConvException(to
!E("A"));
443 // Tests for https://issues.dlang.org/show_bug.cgi?id=6175
444 @safe pure nothrow unittest
446 char[9] sarr
= "blablabla";
447 auto darr
= to
!(char[])(sarr
);
448 assert(sarr
.ptr
== darr
.ptr
);
449 assert(sarr
.length
== darr
.length
);
452 // Tests for https://issues.dlang.org/show_bug.cgi?id=7348
453 @safe pure /+nothrow+/ unittest
455 assert(to
!string(null) == "null");
456 assert(text(null) == "null");
459 // Test `scope` inference of parameters of `text`
464 int* x
; // make S a type with pointers
465 string
toString() const scope
471 assert(text("a", s
) == "aS");
474 // Tests for https://issues.dlang.org/show_bug.cgi?id=11390
475 @safe pure /+nothrow+/ unittest
477 const(typeof(null)) ctn
;
478 immutable(typeof(null)) itn
;
479 assert(to
!string(ctn
) == "null");
480 assert(to
!string(itn
) == "null");
483 // Tests for https://issues.dlang.org/show_bug.cgi?id=8729: do NOT skip leading WS
486 import std
.exception
;
487 static foreach (T
; AliasSeq
!(byte, ubyte, short, ushort, int, uint, long, ulong))
489 assertThrown
!ConvException(to
!T(" 0"));
490 assertThrown
!ConvException(to
!T(" 0", 8));
492 static foreach (T
; AliasSeq
!(float, double, real))
494 assertThrown
!ConvException(to
!T(" 0"));
497 assertThrown
!ConvException(to
!bool(" true"));
499 alias NullType
= typeof(null);
500 assertThrown
!ConvException(to
!NullType(" null"));
503 assertThrown
!ConvException(to
!ARR(" [1]"));
506 assertThrown
!ConvException(to
!AA(" [1:1]"));
509 // https://issues.dlang.org/show_bug.cgi?id=20623
510 @safe pure nothrow unittest
514 // override string toString() const
528 // C c; // TODO: Fails because of hasToString
532 static foreach (const idx
; 0 .. this.tupleof
.length
)
535 const _
= this.tupleof
[idx
].to
!string();
543 If the source type is implicitly convertible to the target type, $(D
544 to) simply performs the implicit conversion.
546 private T
toImpl(T
, S
)(S value
)
548 !isEnumStrToStr
!(S
, T
) && !isNullToStr
!(S
, T
))
550 template isSignedInt(T
)
552 enum isSignedInt
= isIntegral
!T
&& isSigned
!T
;
554 alias isUnsignedInt
= isUnsigned
;
556 // Conversion from integer to integer, and changing its sign
557 static if (isUnsignedInt
!S
&& isSignedInt
!T
&& S
.sizeof
== T
.sizeof
)
558 { // unsigned to signed & same size
559 import std
.exception
: enforce
;
560 enforce(value
<= cast(S
) T
.max
,
561 new ConvOverflowException("Conversion positive overflow"));
563 else static if (isSignedInt
!S
&& isUnsignedInt
!T
)
564 { // signed to unsigned
565 import std
.exception
: enforce
;
567 new ConvOverflowException("Conversion negative overflow"));
573 // https://issues.dlang.org/show_bug.cgi?id=9523: Allow identity enum conversion
574 @safe pure nothrow unittest
581 @safe pure nothrow unittest
588 // https://issues.dlang.org/show_bug.cgi?id=6377
591 import std
.exception
;
592 // Conversion between same size
593 static foreach (S
; AliasSeq
!(byte, short, int, long))
595 alias U
= Unsigned
!S
;
597 static foreach (Sint
; AliasSeq
!(S
, const S
, immutable S
))
598 static foreach (Uint
; AliasSeq
!(U
, const U
, immutable U
))
602 assertThrown
!ConvOverflowException(to
!Sint(un
),
603 text(Sint
.stringof
, ' ', Uint
.stringof
, ' ', un
));
607 assertThrown
!ConvOverflowException(to
!Uint(sn
),
608 text(Sint
.stringof
, ' ', Uint
.stringof
, ' ', un
));
612 // Conversion between different size
613 static foreach (i
, S1
; AliasSeq
!(byte, short, int, long))
614 static foreach ( S2
; AliasSeq
!(byte, short, int, long)[i
+1..$])
616 alias U1
= Unsigned
!S1
;
617 alias U2
= Unsigned
!S2
;
619 static assert(U1
.sizeof
< S2
.sizeof
);
621 // small unsigned to big signed
622 static foreach (Uint
; AliasSeq
!(U1
, const U1
, immutable U1
))
623 static foreach (Sint
; AliasSeq
!(S2
, const S2
, immutable S2
))
626 assertNotThrown(to
!Sint(un
));
627 assert(to
!Sint(un
) == un
);
630 // big unsigned to small signed
631 static foreach (Uint
; AliasSeq
!(U2
, const U2
, immutable U2
))
632 static foreach (Sint
; AliasSeq
!(S1
, const S1
, immutable S1
))
635 assertThrown(to
!Sint(un
));
638 static assert(S1
.sizeof
< U2
.sizeof
);
640 // small signed to big unsigned
641 static foreach (Sint
; AliasSeq
!(S1
, const S1
, immutable S1
))
642 static foreach (Uint
; AliasSeq
!(U2
, const U2
, immutable U2
))
645 assertThrown
!ConvOverflowException(to
!Uint(sn
));
648 // big signed to small unsigned
649 static foreach (Sint
; AliasSeq
!(S2
, const S2
, immutable S2
))
650 static foreach (Uint
; AliasSeq
!(U1
, const U1
, immutable U1
))
653 assertThrown
!ConvOverflowException(to
!Uint(sn
));
658 // https://issues.dlang.org/show_bug.cgi?id=13551
659 private T
toImpl(T
, S
)(S value
)
663 static foreach (i
; 0 .. T
.length
)
665 t
[i
] = value
[i
].to
!(typeof(T
[i
]));
672 import std
.typecons
: Tuple
;
674 auto test = ["10", "20", "30"];
675 assert(test.to
!(Tuple
!(int, int, int)) == Tuple
!(int, int, int)(10, 20, 30));
678 assert(test1
.to
!(Tuple
!(int, int)) == Tuple
!(int, int)(1, 2));
680 auto test2
= [1.0, 2.0, 3.0];
681 assert(test2
.to
!(Tuple
!(int, int, int)) == Tuple
!(int, int, int)(1, 2, 3));
685 Converting static arrays forwards to their dynamic counterparts.
687 private T
toImpl(T
, S
)(ref S s
)
690 return toImpl
!(T
, typeof(s
[0])[])(s
);
693 @safe pure nothrow unittest
695 char[4] test = ['a', 'b', 'c', 'd'];
696 static assert(!isInputRange
!(Unqual
!(char[4])));
697 assert(to
!string(test) == test);
701 When source type supports member template function opCast, it is used.
703 private T
toImpl(T
, S
)(S value
)
705 is(typeof(S
.init
.opCast
!T()) : T
) &&
706 !isExactSomeString
!T
&&
707 !is(typeof(T(value
))))
709 return value
.opCast
!T();
718 this(S s
) @safe pure { }
722 T
opCast(U
)() @safe pure { assert(false); }
725 cast(void) to
!(Test
.T
)(Test
.S());
727 // make sure std.conv.to is doing the same thing as initialization
736 T
opCast(T
)() { return 43; }
739 assert(to
!int(b
) == 43);
743 T
opCast(T
)() { return 43; }
746 assert(to
!int(s
) == 43);
750 When target type supports 'converting construction', it is used.
751 $(UL $(LI If target type is struct, `T(value)` is used.)
752 $(LI If target type is class, $(D new T(value)) is used.))
754 private T
toImpl(T
, S
)(S value
)
756 is(T
== struct) && is(typeof(T(value
))))
761 // https://issues.dlang.org/show_bug.cgi?id=3961
773 this(int x
) @safe pure { this.x
= x
; }
775 Int2 i2
= to
!Int2(1);
780 static Int3
opCall(int x
) @safe pure
787 Int3 i3
= to
!Int3(1);
790 // https://issues.dlang.org/show_bug.cgi?id=6808
793 static struct FakeBigInt
795 this(string s
) @safe pure {}
799 auto i3
= to
!FakeBigInt(s
);
803 private T
toImpl(T
, S
)(S value
)
805 is(T
== class) && is(typeof(new T(value
))))
819 this(int x
) @safe pure { this.x
= x
; }
825 this(S src
) @safe pure { value
= src
.x
; }
826 this(C src
) @safe pure { value
= src
.x
; }
830 auto b1
= to
!B(s
); // == new B(s)
831 assert(b1
.value
== 1);
834 auto b2
= to
!B(c
); // == new B(c)
835 assert(b2
.value
== 2);
837 auto c2
= to
!C(3); // == new C(3)
847 this(B b
) @safe pure {}
851 this() @safe pure { super(this); }
856 S
.A a
= to
!(S
.A
)(b
); // == cast(S.A) b
857 // (do not run construction conversion like new S.A(b))
860 static class C
: Object
863 this(Object o
) @safe pure {}
867 C a2
= to
!C(oc
); // == new C(a)
868 // Construction conversion overrides down-casting conversion
873 Object-to-object conversions by dynamic casting throw exception when the source is
874 non-null and the target is null.
876 private T
toImpl(T
, S
)(S value
)
878 (is(S
== class) ||
is(S
== interface)) && !is(typeof(value
.opCast
!T()) : T
) &&
879 (is(T
== class) ||
is(T
== interface)) && !is(typeof(new T(value
))))
881 static if (is(T
== immutable))
883 // immutable <- immutable
884 enum isModConvertible
= is(S
== immutable);
886 else static if (is(T
== const))
888 static if (is(T
== shared))
890 // shared const <- shared
891 // shared const <- shared const
892 // shared const <- immutable
893 enum isModConvertible
= is(S
== shared) ||
is(S
== immutable);
898 // const <- immutable
899 enum isModConvertible
= !is(S
== shared);
904 static if (is(T
== shared))
906 // shared <- shared mutable
907 enum isModConvertible
= is(S
== shared) && !is(S
== const);
911 // (mutable) <- (mutable)
912 enum isModConvertible
= is(Unqual
!S
== S
);
915 static assert(isModConvertible
, "Bad modifier conversion: "~S
.stringof
~" to "~T
.stringof
);
917 auto result
= ()@trusted{ return cast(T
) value
; }();
918 if (!result
&& value
)
920 throw new ConvException("Cannot convert object of static type "
921 ~S
.classinfo
.name
~" and dynamic type "~value
.classinfo
.name
922 ~" to type "~T
.classinfo
.name
);
930 import std
.exception
;
932 alias Identity(T
) = T
;
933 alias toConst(T
) = const T
;
934 alias toShared(T
) = shared T
;
935 alias toSharedConst(T
) = shared const T
;
936 alias toImmutable(T
) = immutable T
;
937 template AddModifier(int n
)
940 static if (n
== 0) alias AddModifier
= Identity
;
941 else static if (n
== 1) alias AddModifier
= toConst
;
942 else static if (n
== 2) alias AddModifier
= toShared
;
943 else static if (n
== 3) alias AddModifier
= toSharedConst
;
944 else static if (n
== 4) alias AddModifier
= toImmutable
;
955 static foreach (m1
; 0 .. 5) // enumerate modifiers
956 static foreach (m2
; 0 .. 5) // ditto
958 alias srcmod
= AddModifier
!m1
;
959 alias tgtmod
= AddModifier
!m2
;
961 // Compile time convertible equals to modifier convertible.
962 static if (is(srcmod
!Object
: tgtmod
!Object
))
964 // Test runtime conversions: class to class, class to interface,
965 // interface to class, and interface to interface
967 // Check that the runtime conversion to succeed
968 srcmod
!A ac
= new srcmod
!C();
969 srcmod
!I ic
= new srcmod
!C();
970 assert(to
!(tgtmod
!C
)(ac
) !is null); // A(c) to C
971 assert(to
!(tgtmod
!I
)(ac
) !is null); // A(c) to I
972 assert(to
!(tgtmod
!C
)(ic
) !is null); // I(c) to C
973 assert(to
!(tgtmod
!J
)(ic
) !is null); // I(c) to J
975 // Check that the runtime conversion fails
976 srcmod
!A ab
= new srcmod
!B();
977 srcmod
!I id
= new srcmod
!D();
978 assertThrown(to
!(tgtmod
!C
)(ab
)); // A(b) to C
979 assertThrown(to
!(tgtmod
!I
)(ab
)); // A(b) to I
980 assertThrown(to
!(tgtmod
!C
)(id
)); // I(d) to C
981 assertThrown(to
!(tgtmod
!J
)(id
)); // I(d) to J
985 // Check that the conversion is rejected statically
986 static assert(!is(typeof(to
!(tgtmod
!C
)(srcmod
!A
.init
)))); // A to C
987 static assert(!is(typeof(to
!(tgtmod
!I
)(srcmod
!A
.init
)))); // A to I
988 static assert(!is(typeof(to
!(tgtmod
!C
)(srcmod
!I
.init
)))); // I to C
989 static assert(!is(typeof(to
!(tgtmod
!J
)(srcmod
!I
.init
)))); // I to J
995 Handles type _to string conversions
997 private T
toImpl(T
, S
)(S value
)
999 !isEnumStrToStr
!(S
, T
) && !isNullToStr
!(S
, T
)) &&
1000 !isInfinite
!S
&& isExactSomeString
!T
)
1002 static if (isExactSomeString
!S
&& value
[0].sizeof
== ElementEncodingType
!T
.sizeof
)
1004 // string-to-string with incompatible qualifier conversion
1005 static if (is(ElementEncodingType
!T
== immutable))
1007 // conversion (mutable|const) -> immutable
1012 // conversion (immutable|const) -> mutable
1016 else static if (isExactSomeString
!S
)
1018 import std
.array
: appender
;
1019 // other string-to-string
1020 //Use Appender directly instead of toStr, which also uses a formatedWrite
1021 auto w
= appender
!T();
1025 else static if (isIntegral
!S
&& !is(S
== enum))
1027 // other integral-to-string conversions with default radix
1029 import core
.internal
.string
: signedToTempString
, unsignedToTempString
;
1031 alias EEType
= Unqual
!(ElementEncodingType
!T
);
1032 EEType
[long.sizeof
* 3 + 1] buf
= void;
1033 EEType
[] t
= isSigned
!S
1034 ? signedToTempString
!(10, false, EEType
)(value
, buf
)
1035 : unsignedToTempString
!(10, false, EEType
)(value
, buf
);
1038 else static if (is(S
== void[]) ||
is(S
== const(void)[]) ||
is(S
== immutable(void)[]))
1040 import core
.stdc
.string
: memcpy
;
1041 import std
.exception
: enforce
;
1042 // Converting void array to string
1043 alias Char
= Unqual
!(ElementEncodingType
!T
);
1044 auto raw
= cast(const(ubyte)[]) value
;
1045 enforce(raw
.length
% Char
.sizeof
== 0,
1046 new ConvException("Alignment mismatch in converting a "
1047 ~ S
.stringof
~ " to a "
1049 auto result
= new Char
[raw
.length
/ Char
.sizeof
];
1050 ()@trusted{ memcpy(result
.ptr
, value
.ptr
, value
.length
); }();
1051 return cast(T
) result
;
1053 else static if (isPointer
!S
&& isSomeChar
!(PointerTarget
!S
))
1055 // This is unsafe because we cannot guarantee that the pointer is null terminated.
1057 static if (is(S
: const(char)*))
1058 import core
.stdc
.string
: strlen
;
1060 size_t
strlen(S s
) nothrow
1066 return toImpl
!T(value ? value
[0 .. strlen(value
)].dup
: null);
1069 else static if (isSomeString
!T
&& is(S
== enum))
1071 static if (isSwitchable
!(OriginalType
!S
) && EnumMembers
!S
.length
<= 50)
1075 foreach (member
; NoDuplicates
!(EnumMembers
!S
))
1078 return to
!T(enumRep
!(immutable(T
), S
, member
));
1085 foreach (member
; EnumMembers
!S
)
1087 if (value
== member
)
1088 return to
!T(enumRep
!(immutable(T
), S
, member
));
1092 import std
.array
: appender
;
1093 import std
.format
.spec
: FormatSpec
;
1094 import std
.format
.write
: formatValue
;
1096 //Default case, delegate to format
1097 //Note: we don't call toStr directly, to avoid duplicate work.
1098 auto app
= appender
!T();
1099 app
.put("cast(" ~ S
.stringof
~ ")");
1101 formatValue(app
, cast(OriginalType
!S
) value
, f
);
1106 // other non-string values runs formatting
1107 return toStr
!T(value
);
1111 // https://issues.dlang.org/show_bug.cgi?id=14042
1114 immutable(char)* ptr
= "hello".ptr
;
1115 auto result
= ptr
.to
!(char[]);
1117 // https://issues.dlang.org/show_bug.cgi?id=8384
1120 void test1(T
)(T lp
, string
cmp)
1122 static foreach (e
; AliasSeq
!(char, wchar, dchar))
1124 test2
!(e
[])(lp
, cmp);
1125 test2
!(const(e
)[])(lp
, cmp);
1126 test2
!(immutable(e
)[])(lp
, cmp);
1130 void test2(D
, S
)(S lp
, string
cmp)
1132 assert(to
!string(to
!D(lp
)) == cmp);
1135 static foreach (e
; AliasSeq
!("Hello, world!", "Hello, world!"w
, "Hello, world!"d
))
1137 test1(e
, "Hello, world!");
1138 test1(e
.ptr
, "Hello, world!");
1140 static foreach (e
; AliasSeq
!("", ""w
, ""d
))
1148 To string conversion for non copy-able structs
1150 private T
toImpl(T
, S
)(ref S value
)
1152 !isEnumStrToStr
!(S
, T
) && !isNullToStr
!(S
, T
)) &&
1153 !isInfinite
!S
&& isExactSomeString
!T
&& !isCopyable
!S
&& !isStaticArray
!S
)
1155 import std
.array
: appender
;
1156 import std
.format
.spec
: FormatSpec
;
1157 import std
.format
.write
: formatValue
;
1159 auto w
= appender
!T();
1160 FormatSpec
!(ElementEncodingType
!T
) f
;
1161 formatValue(w
, value
, f
);
1165 // https://issues.dlang.org/show_bug.cgi?id=16108
1173 string
toString() { return text(val
, ":", flag
); }
1175 @disable this(this);
1179 assert(to
!string(a
) == "0:false");
1186 @disable this(this);
1190 assert(to
!string(b
) == "B(0, false)");
1193 // https://issues.dlang.org/show_bug.cgi?id=20070
1196 void writeThem(T
)(ref inout(T
) them
)
1198 assert(them
.to
!string
== "[1, 2, 3, 4]");
1201 const(uint)[4] vals
= [ 1, 2, 3, 4 ];
1206 Check whether type `T` can be used in a switch statement.
1207 This is useful for compile-time generation of switch case statements.
1209 private template isSwitchable(E
)
1211 enum bool isSwitchable
= is(typeof({
1212 switch (E
.init
) { default: }
1219 static assert(isSwitchable
!int);
1220 static assert(!isSwitchable
!double);
1221 static assert(!isSwitchable
!real);
1224 //Static representation of the index I of the enum S,
1225 //In representation T.
1226 //T must be an immutable string (avoids un-necessary initializations).
1227 private template enumRep(T
, S
, S value
)
1228 if (is (T
== immutable) && isExactSomeString
!T
&& is(S
== enum))
1230 static T enumRep
= toStr
!T(value
);
1235 import std
.exception
;
1238 // string to string conversion
1239 alias Chars
= AliasSeq
!(char, wchar, dchar);
1240 foreach (LhsC
; Chars
)
1242 alias LhStrings
= AliasSeq
!(LhsC
[], const(LhsC
)[], immutable(LhsC
)[]);
1243 foreach (Lhs
; LhStrings
)
1245 foreach (RhsC
; Chars
)
1247 alias RhStrings
= AliasSeq
!(RhsC
[], const(RhsC
)[], immutable(RhsC
)[]);
1248 foreach (Rhs
; RhStrings
)
1250 Lhs s1
= to
!Lhs("wyda");
1251 Rhs s2
= to
!Rhs(s1
);
1252 //writeln(Lhs.stringof, " -> ", Rhs.stringof);
1253 assert(s1
== to
!Lhs(s2
));
1263 T
[] s1
= to
!(T
[])("Hello, world!");
1264 auto s2
= to
!(U
[])(s1
);
1265 assert(s1
== to
!(T
[])(s2
));
1266 auto s3
= to
!(const(U
)[])(s1
);
1267 assert(s1
== to
!(T
[])(s3
));
1268 auto s4
= to
!(immutable(U
)[])(s1
);
1269 assert(s1
== to
!(T
[])(s4
));
1279 // Conversion representing bool value with string
1281 assert(to
!string(b
) == "false");
1283 assert(to
!string(b
) == "true");
1288 // Conversion representing character value with string
1290 AliasSeq
!( char, const( char), immutable( char),
1291 wchar, const(wchar), immutable(wchar),
1292 dchar, const(dchar), immutable(dchar));
1293 foreach (Char1
; AllChars
)
1295 foreach (Char2
; AllChars
)
1298 assert(to
!(Char2
[])(c
)[0] == c
);
1301 assert(to
!(Char1
[])(x
) == "4");
1310 assert(s2
== "foo");
1313 @safe pure nothrow unittest
1315 import std
.exception
;
1316 // Conversion representing integer values with string
1318 static foreach (Int
; AliasSeq
!(ubyte, ushort, uint, ulong))
1320 assert(to
!string(Int(0)) == "0");
1321 assert(to
!string(Int(9)) == "9");
1322 assert(to
!string(Int(123)) == "123");
1325 static foreach (Int
; AliasSeq
!(byte, short, int, long))
1327 assert(to
!string(Int(0)) == "0");
1328 assert(to
!string(Int(9)) == "9");
1329 assert(to
!string(Int(123)) == "123");
1330 assert(to
!string(Int(-0)) == "0");
1331 assert(to
!string(Int(-9)) == "-9");
1332 assert(to
!string(Int(-123)) == "-123");
1333 assert(to
!string(const(Int
)(6)) == "6");
1336 assert(wtext(int.max
) == "2147483647"w
);
1337 assert(wtext(int.min
) == "-2147483648"w
);
1338 assert(to
!string(0L) == "0");
1342 assert(to
!string(1uL << 62) == "4611686018427387904");
1343 assert(to
!string(0x100000000) == "4294967296");
1344 assert(to
!string(-138L) == "-138");
1348 @safe unittest // sprintf issue
1350 double[2] a
= [ 1.5, 2.5 ];
1351 assert(to
!string(a
) == "[1.5, 2.5]");
1356 // Conversion representing class object with string
1359 override string
toString() @safe const { return "an A"; }
1362 assert(to
!string(a
) == "null");
1364 assert(to
!string(a
) == "an A");
1366 // https://issues.dlang.org/show_bug.cgi?id=7660
1367 class C
{ override string
toString() @safe const { return "C"; } }
1368 struct S
{ C c
; alias c
this; }
1370 assert(to
!string(s
) == "C");
1375 // Conversion representing struct object with string
1378 string
toString() { return "wyda"; }
1380 assert(to
!string(S1()) == "wyda");
1388 assert(to
!string(s2
) == "S2(42, 43.5)");
1390 // Test for https://issues.dlang.org/show_bug.cgi?id=8080
1395 string
toString() { return "<S>"; }
1398 assert(to
!string(s8080
) == "<S>");
1403 // Conversion representing enum value with string
1404 enum EB
: bool { a
= true }
1405 enum EU
: uint { a
= 0, b
= 1, c
= 2 } // base type is unsigned
1406 // base type is signed (https://issues.dlang.org/show_bug.cgi?id=7909)
1407 enum EI
: int { a
= -1, b
= 0, c
= 1 }
1408 enum EF
: real { a
= 1.414, b
= 1.732, c
= 2.236 }
1409 enum EC
: char { a
= 'x', b
= 'y' }
1410 enum ES
: string
{ a
= "aaa", b
= "bbb" }
1412 static foreach (E
; AliasSeq
!(EB
, EU
, EI
, EF
, EC
, ES
))
1414 assert(to
! string(E
.a
) == "a"c
);
1415 assert(to
!wstring(E
.a
) == "a"w
);
1416 assert(to
!dstring(E
.a
) == "a"d
);
1419 // Test an value not corresponding to an enum member.
1420 auto o
= cast(EU
) 5;
1421 assert(to
! string(o
) == "cast(EU)5"c
);
1422 assert(to
!wstring(o
) == "cast(EU)5"w
);
1423 assert(to
!dstring(o
) == "cast(EU)5"d
);
1431 doo
= foo
, // check duplicate switch statements
1435 //Test regression 12494
1436 assert(to
!string(E
.foo
) == "foo");
1437 assert(to
!string(E
.doo
) == "foo");
1438 assert(to
!string(E
.bar
) == "bar");
1440 static foreach (S
; AliasSeq
!(string
, wstring
, dstring
, const(char[]), const(wchar[]), const(dchar[])))
1442 auto s1
= to
!S(E
.foo
);
1443 auto s2
= to
!S(E
.foo
);
1445 // ensure we don't allocate when it's unnecessary
1449 static foreach (S
; AliasSeq
!(char[], wchar[], dchar[]))
1451 auto s1
= to
!S(E
.foo
);
1452 auto s2
= to
!S(E
.foo
);
1454 // ensure each mutable array is unique
1460 @trusted pure private T
toImpl(T
, S
)(S value
, uint radix
, LetterCase letterCase
= LetterCase
.upper
)
1462 isExactSomeString
!T
)
1465 assert(radix
>= 2 && radix
<= 36, "radix must be in range [2,36]");
1469 alias EEType
= Unqual
!(ElementEncodingType
!T
);
1471 T
toStringRadixConvert(size_t bufLen
)(uint runtimeRadix
= 0)
1473 Unsigned
!(Unqual
!S
) div = void, mValue
= unsigned(value
);
1475 size_t index
= bufLen
;
1476 EEType
[bufLen
] buffer
= void;
1477 char baseChar
= letterCase
== LetterCase
.lower ?
'a' : 'A';
1482 div = cast(S
)(mValue
/ runtimeRadix
);
1483 mod
= cast(ubyte)(mValue
% runtimeRadix
);
1484 mod
+= mod
< 10 ?
'0' : baseChar
- 10;
1485 buffer
[--index
] = cast(char) mod
;
1489 return cast(T
) buffer
[index
.. $].dup
;
1492 import std
.array
: array
;
1496 // The (value+0) is so integral promotions happen to the type
1497 return toChars
!(10, EEType
)(value
+ 0).array
;
1499 // The unsigned(unsigned(value)+0) is so unsigned integral promotions happen to the type
1500 if (letterCase
== letterCase
.upper
)
1501 return toChars
!(16, EEType
, LetterCase
.upper
)(unsigned(unsigned(value
) + 0)).array
;
1503 return toChars
!(16, EEType
, LetterCase
.lower
)(unsigned(unsigned(value
) + 0)).array
;
1505 return toChars
!(2, EEType
)(unsigned(unsigned(value
) + 0)).array
;
1507 return toChars
!(8, EEType
)(unsigned(unsigned(value
) + 0)).array
;
1510 return toStringRadixConvert
!(S
.sizeof
* 6)(radix
);
1514 @safe pure nothrow unittest
1516 static foreach (Int
; AliasSeq
!(uint, ulong))
1518 assert(to
!string(Int(16), 16) == "10");
1519 assert(to
!string(Int(15), 2u) == "1111");
1520 assert(to
!string(Int(1), 2u) == "1");
1521 assert(to
!string(Int(0x1234AF), 16u) == "1234AF");
1522 assert(to
!string(Int(0x1234BCD), 16u, LetterCase
.upper
) == "1234BCD");
1523 assert(to
!string(Int(0x1234AF), 16u, LetterCase
.lower
) == "1234af");
1526 static foreach (Int
; AliasSeq
!(int, long))
1528 assert(to
!string(Int(-10), 10u) == "-10");
1531 assert(to
!string(byte(-10), 16) == "F6");
1532 assert(to
!string(long.min
) == "-9223372036854775808");
1533 assert(to
!string(long.max
) == "9223372036854775807");
1537 Narrowing numeric-numeric conversions throw when the value does not
1538 fit in the narrower type.
1540 private T
toImpl(T
, S
)(S value
)
1542 (isNumeric
!S || isSomeChar
!S || isBoolean
!S
) &&
1543 (isNumeric
!T || isSomeChar
!T || isBoolean
!T
) && !is(T
== enum))
1545 static if (isFloatingPoint
!S
&& isIntegral
!T
)
1547 import std
.math
.traits
: isNaN
;
1548 if (value
.isNaN
) throw new ConvException("Input was NaN");
1551 enum sSmallest
= mostNegative
!S
;
1552 enum tSmallest
= mostNegative
!T
;
1553 static if (sSmallest
< 0)
1555 // possible underflow converting from a signed
1556 static if (tSmallest
== 0)
1558 immutable good
= value
>= 0;
1562 static assert(tSmallest
< 0,
1563 "minimum value of T must be smaller than 0");
1564 immutable good
= value
>= tSmallest
;
1567 throw new ConvOverflowException("Conversion negative overflow");
1569 static if (S
.max
> T
.max
)
1571 // possible overflow
1573 throw new ConvOverflowException("Conversion positive overflow");
1575 return (ref value
)@trusted{ return cast(T
) value
; }(value
);
1580 import std
.exception
;
1583 assert(to
!char(a
) == ' ');
1585 assert(collectException(to
!char(a
)));
1588 char to0
= to
!char(from0
);
1591 char to1
= to
!char(from1
);
1594 char to2
= to
!char(from2
);
1597 wchar to3
= to
!wchar(from3
);
1600 dchar to4
= to
!dchar(from4
);
1605 import std
.exception
;
1607 // Narrowing conversions from enum -> integral should be allowed, but they
1608 // should throw at runtime if the enum value doesn't fit in the target
1610 enum E1
: ulong { A
= 1, B
= 1UL << 48, C
= 0 }
1611 assert(to
!int(E1
.A
) == 1);
1612 assert(to
!bool(E1
.A
) == true);
1613 assertThrown
!ConvOverflowException(to
!int(E1
.B
)); // E1.B overflows int
1614 assertThrown
!ConvOverflowException(to
!bool(E1
.B
)); // E1.B overflows bool
1615 assert(to
!bool(E1
.C
) == false);
1617 enum E2
: long { A
= -1L << 48, B
= -1 << 31, C
= 1 << 31 }
1618 assertThrown
!ConvOverflowException(to
!int(E2
.A
)); // E2.A overflows int
1619 assertThrown
!ConvOverflowException(to
!uint(E2
.B
)); // E2.B overflows uint
1620 assert(to
!int(E2
.B
) == -1 << 31); // but does not overflow int
1621 assert(to
!int(E2
.C
) == 1 << 31); // E2.C does not overflow int
1623 enum E3
: int { A
= -1, B
= 1, C
= 255, D
= 0 }
1624 assertThrown
!ConvOverflowException(to
!ubyte(E3
.A
));
1625 assertThrown
!ConvOverflowException(to
!bool(E3
.A
));
1626 assert(to
!byte(E3
.A
) == -1);
1627 assert(to
!byte(E3
.B
) == 1);
1628 assert(to
!ubyte(E3
.C
) == 255);
1629 assert(to
!bool(E3
.B
) == true);
1630 assertThrown
!ConvOverflowException(to
!byte(E3
.C
));
1631 assertThrown
!ConvOverflowException(to
!bool(E3
.C
));
1632 assert(to
!bool(E3
.D
) == false);
1638 import std
.exception
;
1639 import std
.math
.traits
: isNaN
;
1641 double d
= double.nan
;
1642 float f
= to
!float(d
);
1644 assert(to
!double(f
).isNaN
);
1645 assertThrown
!ConvException(to
!int(d
));
1646 assertThrown
!ConvException(to
!int(f
));
1647 auto ex
= collectException(d
.to
!int);
1648 assert(ex
.msg
== "Input was NaN");
1652 Array-to-array conversion (except when target is a string type)
1653 converts each element in turn by using `to`.
1655 private T
toImpl(T
, S
)(scope S value
)
1657 !isSomeString
!S
&& isDynamicArray
!S
&&
1658 !isExactSomeString
!T
&& isArray
!T
)
1660 alias E
= typeof(T
.init
[0]);
1662 static if (isStaticArray
!T
)
1664 import std
.exception
: enforce
;
1665 auto res
= to
!(E
[])(value
);
1666 enforce
!ConvException(T
.length
== res
.length
,
1667 convFormat("Length mismatch when converting to static array: %s vs %s", T
.length
, res
.length
));
1668 return res
[0 .. T
.length
];
1672 import std
.array
: appender
;
1673 auto w
= appender
!(E
[])();
1674 w
.reserve(value
.length
);
1675 foreach (ref e
; value
)
1685 import std
.exception
;
1687 // array to array conversions
1688 uint[] a
= [ 1u, 2, 3 ];
1689 auto b
= to
!(float[])(a
);
1690 assert(b
== [ 1.0f, 2, 3 ]);
1692 immutable(int)[3] d
= [ 1, 2, 3 ];
1693 b
= to
!(float[])(d
);
1694 assert(b
== [ 1.0f, 2, 3 ]);
1696 uint[][] e
= [ a
, a
];
1697 auto f
= to
!(float[][])(e
);
1698 assert(f
[0] == b
&& f
[1] == b
);
1700 // Test for https://issues.dlang.org/show_bug.cgi?id=8264
1706 Wrap
[] warr
= to
!(Wrap
[])(["foo", "bar"]); // should work
1708 // https://issues.dlang.org/show_bug.cgi?id=12633
1709 import std
.conv
: to
;
1710 const s2
= ["10", "20"];
1712 immutable int[2] a3
= s2
.to
!(int[2]);
1713 assert(a3
== [10, 20]);
1715 // verify length mismatches are caught
1716 immutable s4
= [1, 2, 3, 4];
1719 auto ex
= collectException(s4
[0 .. i
].to
!(int[2]));
1720 assert(ex
&& ex
.msg
== "Length mismatch when converting to static array: 2 vs " ~ [cast(char)(i
+ '0')],
1721 ex ? ex
.msg
: "Exception was not thrown!");
1727 auto b
= [ 1.0f, 2, 3 ];
1729 auto c
= to
!(string
[])(b
);
1730 assert(c
[0] == "1" && c
[1] == "2" && c
[2] == "3");
1734 Associative array to associative array conversion converts each key
1735 and each value in turn.
1737 private T
toImpl(T
, S
)(S value
)
1738 if (!is(S
: T
) && isAssociativeArray
!S
&&
1739 isAssociativeArray
!T
&& !is(T
== enum))
1741 /* This code is potentially unsafe.
1743 alias K2
= KeyType
!T
;
1744 alias V2
= ValueType
!T
;
1746 // While we are "building" the AA, we need to unqualify its values, and only re-qualify at the end
1747 Unqual
!V2
[K2
] result
;
1749 foreach (k1
, v1
; value
)
1751 // Cast values temporarily to Unqual!V2 to store them to result variable
1752 result
[to
!K2(k1
)] = to
!(Unqual
!V2
)(v1
);
1754 // Cast back to original type
1755 return () @trusted { return cast(T
) result
; }();
1760 // hash to hash conversions
1764 auto b
= to
!(double[dstring
])(a
);
1765 assert(b
["0"d
] == 1 && b
["1"d
] == 2);
1768 // https://issues.dlang.org/show_bug.cgi?id=8705, from doc
1771 import std
.exception
;
1772 int[string
][double[int[]]] a
;
1773 auto b
= to
!(short[wstring
][string
[double[]]])(a
);
1774 a
= [null:["hello":int.max
]];
1775 assertThrown
!ConvOverflowException(to
!(short[wstring
][string
[double[]]])(a
));
1777 @system unittest // Extra cases for AA with qualifiers conversion
1779 int[][int[]] a
;// = [[], []];
1780 auto b
= to
!(immutable(short[])[immutable short[]])(a
);
1782 double[dstring
][int[long[]]] c
;
1783 auto d
= to
!(immutable(short[immutable wstring
])[immutable string
[double[]]])(c
);
1788 import std
.algorithm
.comparison
: equal
;
1789 import std
.array
: byPair
;
1792 assert(a
.to
!(int[int]) == a
);
1793 assert(a
.to
!(const(int)[int]).byPair
.equal(a
.byPair
));
1798 static void testIntegralToFloating(Integral
, Floating
)()
1801 auto b
= to
!Floating(a
);
1803 assert(a
== to
!Integral(b
));
1805 static void testFloatingToIntegral(Floating
, Integral
)()
1807 import std
.math
.traits
: floatTraits
, RealFormat
;
1809 bool convFails(Source
, Target
, E
)(Source src
)
1812 cast(void) to
!Target(src
);
1818 // convert some value
1820 auto b
= to
!Integral(a
);
1821 assert(is(typeof(b
) == Integral
) && b
== 42);
1822 // convert some negative value (if applicable)
1824 static if (Integral
.min
< 0)
1827 assert(is(typeof(b
) == Integral
) && b
== -42);
1831 // no go for unsigned types
1832 assert(convFails
!(Floating
, Integral
, ConvOverflowException
)(a
));
1834 // convert to the smallest integral value
1835 a
= 0.0 + Integral
.min
;
1836 static if (Integral
.min
< 0)
1838 a
= -a
; // -Integral.min not representable as an Integral
1839 assert(convFails
!(Floating
, Integral
, ConvOverflowException
)(a
)
1840 || Floating
.sizeof
<= Integral
.sizeof
1841 || floatTraits
!Floating
.realFormat
== RealFormat
.ieeeExtended53
);
1843 a
= 0.0 + Integral
.min
;
1844 assert(to
!Integral(a
) == Integral
.min
);
1845 --a
; // no more representable as an Integral
1846 assert(convFails
!(Floating
, Integral
, ConvOverflowException
)(a
)
1847 || Floating
.sizeof
<= Integral
.sizeof
1848 || floatTraits
!Floating
.realFormat
== RealFormat
.ieeeExtended53
);
1849 a
= 0.0 + Integral
.max
;
1850 assert(to
!Integral(a
) == Integral
.max
1851 || Floating
.sizeof
<= Integral
.sizeof
1852 || floatTraits
!Floating
.realFormat
== RealFormat
.ieeeExtended53
);
1853 ++a
; // no more representable as an Integral
1854 assert(convFails
!(Floating
, Integral
, ConvOverflowException
)(a
)
1855 || Floating
.sizeof
<= Integral
.sizeof
1856 || floatTraits
!Floating
.realFormat
== RealFormat
.ieeeExtended53
);
1857 // convert a value with a fractional part
1859 assert(to
!Integral(a
) == 3);
1861 assert(to
!Integral(a
) == 3);
1862 static if (Integral
.min
< 0)
1865 assert(to
!Integral(a
) == -3);
1867 assert(to
!Integral(a
) == -3);
1871 alias AllInts
= AliasSeq
!(byte, ubyte, short, ushort, int, uint, long, ulong);
1872 alias AllFloats
= AliasSeq
!(float, double, real);
1873 alias AllNumerics
= AliasSeq
!(AllInts
, AllFloats
);
1874 // test with same type
1876 foreach (T
; AllNumerics
)
1880 assert(is(typeof(a
) == typeof(b
)) && a
== b
);
1883 // test that floating-point numbers convert properly to largest ints
1884 // see http://oregonstate.edu/~peterseb/mth351/docs/351s2001_fp80x87.html
1885 // look for "largest fp integer with a predecessor"
1888 int a
= 16_777_215; // 2^24 - 1
1889 assert(to
!int(to
!float(a
)) == a
);
1890 assert(to
!int(to
!float(-a
)) == -a
);
1892 long b
= 9_007_199_254_740_991; // 2^53 - 1
1893 assert(to
!long(to
!double(b
)) == b
);
1894 assert(to
!long(to
!double(-b
)) == -b
);
1896 static if (real.mant_dig
>= 64)
1898 ulong c
= 18_446_744_073_709_551_615UL; // 2^64 - 1
1899 assert(to
!ulong(to
!real(c
)) == c
);
1902 // test conversions floating => integral
1904 foreach (Integral
; AllInts
)
1906 foreach (Floating
; AllFloats
)
1908 testFloatingToIntegral
!(Floating
, Integral
)();
1912 // test conversion integral => floating
1914 foreach (Integral
; AllInts
)
1916 foreach (Floating
; AllFloats
)
1918 testIntegralToFloating
!(Integral
, Floating
)();
1924 foreach (T
; AllNumerics
)
1926 // from type immutable(char)[2]
1927 auto a
= to
!T("42");
1930 char[] s1
= "42".dup
;
1933 // from type char[2]
1938 // from type immutable(wchar)[2]
1947 alias AllInts
= AliasSeq
!(byte, ubyte, short, ushort, int, uint, long, ulong);
1948 alias AllFloats
= AliasSeq
!(float, double, real);
1949 alias AllNumerics
= AliasSeq
!(AllInts
, AllFloats
);
1950 // test conversions to string
1952 foreach (T
; AllNumerics
)
1955 string s
= to
!string(a
);
1956 assert(s
== "42", s
);
1957 wstring ws
= to
!wstring(a
);
1958 assert(ws
== "42"w
, to
!string(ws
));
1959 dstring
ds = to
!dstring(a
);
1960 assert(ds == "42"d
, to
!string(ds));
1965 assert(to
!string(b
) == "[42, 33]");
1968 // test array to string conversion
1969 foreach (T
; AllNumerics
)
1971 auto a
= [to
!T(1), 2, 3];
1972 assert(to
!string(a
) == "[1, 2, 3]");
1974 // test enum to int conversion
1975 enum Testing
{ Test1
, Test2
}
1977 auto a
= to
!string(t
);
1978 assert(a
== "Test1");
1983 String, or string-like input range, to non-string conversion runs parsing.
1985 $(LI When the source is a wide string, it is first converted to a narrow
1986 string and then parsed.)
1987 $(LI When the source is a narrow string, normal text parsing occurs.))
1989 private T
toImpl(T
, S
)(S value
)
1990 if (isInputRange
!S
&& isSomeChar
!(ElementEncodingType
!S
) &&
1991 !isExactSomeString
!T
&& is(typeof(parse
!T(value
))) &&
1992 // https://issues.dlang.org/show_bug.cgi?id=20539
1993 !(is(T
== enum) && is(typeof(value
== OriginalType
!T
.init
)) && !isSomeString
!(OriginalType
!T
)))
1999 throw convError
!(S
, T
)(value
);
2002 return parse
!T(value
);
2006 private T
toImpl(T
, S
)(S value
, uint radix
)
2007 if (isSomeFiniteCharInputRange
!S
&&
2008 isIntegral
!T
&& is(typeof(parse
!T(value
, radix
))))
2014 throw convError
!(S
, T
)(value
);
2017 return parse
!T(value
, radix
);
2022 // https://issues.dlang.org/show_bug.cgi?id=6668
2023 // ensure no collaterals thrown
2024 try { to
!uint("-1"); }
2025 catch (ConvException e
) { assert(e
.next
is null); }
2030 static foreach (Str
; AliasSeq
!(string
, wstring
, dstring
))
2033 assert(to
!int(a
) == 123);
2034 assert(to
!double(a
) == 123);
2037 // https://issues.dlang.org/show_bug.cgi?id=6255
2038 auto n
= to
!int("FF", 16);
2042 // https://issues.dlang.org/show_bug.cgi?id=15800
2045 import std
.utf
: byCodeUnit
, byChar
, byWchar
, byDchar
;
2047 assert(to
!int(byCodeUnit("10")) == 10);
2048 assert(to
!int(byCodeUnit("10"), 10) == 10);
2049 assert(to
!int(byCodeUnit("10"w
)) == 10);
2050 assert(to
!int(byCodeUnit("10"w
), 10) == 10);
2052 assert(to
!int(byChar("10")) == 10);
2053 assert(to
!int(byChar("10"), 10) == 10);
2054 assert(to
!int(byWchar("10")) == 10);
2055 assert(to
!int(byWchar("10"), 10) == 10);
2056 assert(to
!int(byDchar("10")) == 10);
2057 assert(to
!int(byDchar("10"), 10) == 10);
2061 String, or string-like input range, to char type not directly
2062 supported by parse parses the first dchar of the source.
2064 Returns: the first code point of the input range, converted
2067 Throws: ConvException if the input range contains more than
2068 a single code point, or if the code point does not
2069 fit into a code unit of type T.
2071 private T
toImpl(T
, S
)(S value
)
2072 if (isSomeChar
!T
&& !is(typeof(parse
!T(value
))) &&
2073 is(typeof(parse
!dchar(value
))))
2075 import std
.utf
: encode
;
2077 immutable dchar codepoint
= parse
!dchar(value
);
2079 throw new ConvException(convFormat("Cannot convert \"%s\" to %s because it " ~
2080 "contains more than a single code point.",
2081 value
, T
.stringof
));
2082 T
[dchar.sizeof
/ T
.sizeof
] decodedCodepoint
;
2083 if (encode(decodedCodepoint
, codepoint
) != 1)
2084 throw new ConvException(convFormat("First code point '%s' of \"%s\" does not fit into a " ~
2085 "single %s code unit", codepoint
, value
, T
.stringof
));
2086 return decodedCodepoint
[0];
2091 import std
.exception
: assertThrown
;
2093 assert(toImpl
!wchar("a") == 'a');
2095 assert(toImpl
!char("a"d
) == 'a');
2096 assert(toImpl
!char("a"w
) == 'a');
2097 assert(toImpl
!wchar("a"d
) == 'a');
2099 assertThrown
!ConvException(toImpl
!wchar("ab"));
2100 assertThrown
!ConvException(toImpl
!char("😃"d
));
2104 Convert a value that is implicitly convertible to the enum base type
2105 into an Enum value. If the value does not match any enum member values
2106 a ConvException is thrown.
2107 Enums with floating-point or string base types are not supported.
2109 private T
toImpl(T
, S
)(S value
)
2110 if (is(T
== enum) && !is(S
== enum)
2111 && is(typeof(value
== OriginalType
!T
.init
))
2112 && !isFloatingPoint
!(OriginalType
!T
) && !isSomeString
!(OriginalType
!T
))
2114 foreach (Member
; EnumMembers
!T
)
2116 if (Member
== value
)
2119 throw new ConvException(convFormat("Value (%s) does not match any member value of enum '%s'", value
, T
.stringof
));
2124 import std
.exception
;
2125 enum En8143
: int { A
= 10, B
= 20, C
= 30, D
= 20 }
2126 enum En8143
[][] m3
= to
!(En8143
[][])([[10, 30], [30, 10]]);
2127 static assert(m3
== [[En8143
.A
, En8143
.C
], [En8143
.C
, En8143
.A
]]);
2129 En8143 en1
= to
!En8143(10);
2130 assert(en1
== En8143
.A
);
2131 assertThrown
!ConvException(to
!En8143(5)); // matches none
2132 En8143
[][] m1
= to
!(En8143
[][])([[10, 30], [30, 10]]);
2133 assert(m1
== [[En8143
.A
, En8143
.C
], [En8143
.C
, En8143
.A
]]);
2136 // https://issues.dlang.org/show_bug.cgi?id=20539
2139 import std
.exception
: assertNotThrown
;
2141 // To test that the bug is fixed it is required that the struct is static,
2142 // otherwise, the frame pointer makes the test pass even if the bug is not
2158 assertNotThrown("xxx".to
!ColorA
);
2160 // This is a guard for the future.
2175 assertNotThrown("xxx".to
!ColorB
);
2178 /***************************************************************
2179 Rounded conversion from floating point to integral.
2181 Rounded conversions do not work with non-integral target types.
2184 template roundTo(Target
)
2186 Target
roundTo(Source
)(Source value
)
2188 import core
.math
: abs
= fabs;
2189 import std
.math
.exponential
: log2
;
2190 import std
.math
.rounding
: trunc
;
2192 static assert(isFloatingPoint
!Source
);
2193 static assert(isIntegral
!Target
);
2195 // If value >= 2 ^^ (real.mant_dig - 1), the number is an integer
2196 // and adding 0.5 won't work, but we allready know, that we do
2197 // not have to round anything.
2198 if (log2(abs(value
)) >= real.mant_dig
- 1)
2199 return to
!Target(value
);
2201 return to
!Target(trunc(value
+ (value
< 0 ?
-0.5L : 0.5L)));
2208 assert(roundTo
!int(3.14) == 3);
2209 assert(roundTo
!int(3.49) == 3);
2210 assert(roundTo
!int(3.5) == 4);
2211 assert(roundTo
!int(3.999) == 4);
2212 assert(roundTo
!int(-3.14) == -3);
2213 assert(roundTo
!int(-3.49) == -3);
2214 assert(roundTo
!int(-3.5) == -4);
2215 assert(roundTo
!int(-3.999) == -4);
2216 assert(roundTo
!(const int)(to
!(const double)(-3.999)) == -4);
2221 import std
.exception
;
2223 static foreach (Int
; AliasSeq
!(byte, ubyte, short, ushort, int, uint))
2225 assert(roundTo
!Int(Int
.min
- 0.4L) == Int
.min
);
2226 assert(roundTo
!Int(Int
.max
+ 0.4L) == Int
.max
);
2227 assertThrown
!ConvOverflowException(roundTo
!Int(Int
.min
- 0.5L));
2228 assertThrown
!ConvOverflowException(roundTo
!Int(Int
.max
+ 0.5L));
2234 import std
.exception
;
2235 assertThrown
!ConvException(roundTo
!int(float.init
));
2236 auto ex
= collectException(roundTo
!int(float.init
));
2237 assert(ex
.msg
== "Input was NaN");
2240 // https://issues.dlang.org/show_bug.cgi?id=5232
2243 static if (real.mant_dig
>= 64)
2244 ulong maxOdd
= ulong.max
;
2246 ulong maxOdd
= (1UL << real.mant_dig
) - 1;
2249 assert(roundTo
!ulong(r1
) == maxOdd
);
2251 real r2
= maxOdd
- 1;
2252 assert(roundTo
!ulong(r2
) == maxOdd
- 1);
2254 real r3
= maxOdd
/ 2;
2255 assert(roundTo
!ulong(r3
) == maxOdd
/ 2);
2257 real r4
= maxOdd
/ 2 + 1;
2258 assert(roundTo
!ulong(r4
) == maxOdd
/ 2 + 1);
2260 // this is only an issue on computers where real == double
2261 long l
= -((1L << double.mant_dig
) - 1);
2263 assert(roundTo
!long(r5
) == l
);
2268 The `parse` family of functions works quite like the $(LREF to)
2269 family, except that:
2271 $(LI It only works with character ranges as input.)
2272 $(LI It takes the input by reference. This means that rvalues (such
2273 as string literals) are not accepted: use `to` instead.)
2274 $(LI It advances the input to the position following the conversion.)
2275 $(LI It does not throw if it could not convert the entire input.))
2278 This overload parses a `bool` from a character input range.
2281 Target = the boolean type to convert to
2282 source = the lvalue of an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
2283 doCount = the flag for deciding to report the number of consumed characters
2287 $(LI A `bool` if `doCount` is set to `No.doCount`)
2288 $(LI A `tuple` containing a `bool` and a `size_t` if `doCount` is set to `Yes.doCount`))
2291 A $(LREF ConvException) if the range does not represent a `bool`.
2294 All character input range conversions using $(LREF to) are forwarded
2295 to `parse` and do not require lvalues.
2297 auto parse(Target
, Source
, Flag
!"doCount" doCount
= No
.doCount
)(ref Source source
)
2298 if (is(immutable Target
== immutable bool) &&
2299 isInputRange
!Source
&&
2300 isSomeChar
!(ElementType
!Source
))
2302 import std
.ascii
: toLower
;
2304 static if (isNarrowString
!Source
)
2306 import std
.string
: representation
;
2307 auto s
= source
.representation
;
2316 auto c1
= toLower(s
.front
);
2317 bool result
= c1
== 't';
2318 if (result || c1
== 'f')
2321 foreach (c
; result ?
"rue" : "alse")
2323 if (s
.empty ||
toLower(s
.front
) != c
)
2328 static if (isNarrowString
!Source
)
2329 source
= cast(Source
) s
;
2334 return tuple
!("data", "count")(result
, 4);
2335 return tuple
!("data", "count")(result
, 5);
2344 throw parseError("bool should be case-insensitive 'true' or 'false'");
2350 import std
.typecons
: Flag
, Yes
, No
;
2352 bool b
= parse
!bool(s
);
2355 bool b2
= parse
!(bool, string
, No
.doCount
)(s2
);
2358 auto b3
= parse
!(bool, string
, Yes
.doCount
)(s3
);
2359 assert(b3
.data
&& b3
.count
== 4);
2361 auto b4
= parse
!(bool, string
, Yes
.doCount
)(s4
);
2362 assert(!b4
.data
&& b4
.count
== 5);
2367 import std
.algorithm
.comparison
: equal
;
2368 import std
.exception
;
2372 @property auto front() { return _s
.front
; }
2373 @property bool empty() { return _s
.empty
; }
2374 void popFront() { _s
.popFront(); }
2377 auto s
= InputString("trueFALSETrueFalsetRUEfALSE");
2378 assert(parse
!bool(s
) == true);
2379 assert(s
.equal("FALSETrueFalsetRUEfALSE"));
2380 assert(parse
!bool(s
) == false);
2381 assert(s
.equal("TrueFalsetRUEfALSE"));
2382 assert(parse
!bool(s
) == true);
2383 assert(s
.equal("FalsetRUEfALSE"));
2384 assert(parse
!bool(s
) == false);
2385 assert(s
.equal("tRUEfALSE"));
2386 assert(parse
!bool(s
) == true);
2387 assert(s
.equal("fALSE"));
2388 assert(parse
!bool(s
) == false);
2391 foreach (ss
; ["tfalse", "ftrue", "t", "f", "tru", "fals", ""])
2393 s
= InputString(ss
);
2394 assertThrown
!ConvException(parse
!bool(s
));
2399 Parses an integer from a character $(REF_ALTTEXT input range, isInputRange, std,range,primitives).
2402 Target = the integral type to convert to
2403 s = the lvalue of an input range
2404 doCount = the flag for deciding to report the number of consumed characters
2408 $(LI A number of type `Target` if `doCount` is set to `No.doCount`)
2409 $(LI A `tuple` containing a number of type `Target` and a `size_t` if `doCount` is set to `Yes.doCount`))
2412 A $(LREF ConvException) If an overflow occurred during conversion or
2413 if no character of the input was meaningfully converted.
2415 auto parse(Target
, Source
, Flag
!"doCount" doCount
= No
.doCount
)(ref scope Source s
)
2416 if (isIntegral
!Target
&& !is(Target
== enum) &&
2417 isSomeChar
!(ElementType
!Source
))
2419 static if (Target
.sizeof
< int.sizeof
)
2421 // smaller types are handled like integers
2422 auto v
= .parse
!(Select
!(Target
.min
< 0, int, uint), Source
, Yes
.doCount
)(s
);
2423 auto result
= (() @trusted => cast (Target
) v
.data
)();
2424 if (result
== v
.data
)
2428 return tuple
!("data", "count")(result
, v
.count
);
2435 throw new ConvOverflowException("Overflow in integral conversion");
2439 // int or larger types
2441 static if (Target
.min
< 0)
2444 enum bool sign
= false;
2446 enum char maxLastDigit
= Target
.min
< 0 ?
7 : 5;
2449 static if (isNarrowString
!Source
)
2451 import std
.string
: representation
;
2452 auto source
= s
.representation
;
2466 static if (Target
.min
< 0)
2491 Target v
= cast(Target
) c
;
2496 while (!source
.empty
)
2498 c
= cast(typeof(c
)) (source
.front
- '0');
2503 if (v
>= 0 && (v
< Target
.max
/10 ||
2504 (v
== Target
.max
/10 && c
<= maxLastDigit
+ sign
)))
2506 // Note: `v` can become negative here in case of parsing
2507 // the most negative value:
2508 v
= cast(Target
) (v
* 10 + c
);
2513 throw new ConvOverflowException("Overflow in integral conversion");
2519 static if (isNarrowString
!Source
)
2520 s
= s
[$-source
.length
..$];
2524 return tuple
!("data", "count")(v
, count
);
2532 static if (isNarrowString
!Source
)
2533 throw convError
!(Source
, Target
)(cast(Source
) source
);
2535 throw convError
!(Source
, Target
)(source
);
2542 import std
.typecons
: Flag
, Yes
, No
;
2544 auto a
= parse
!int(s
);
2548 auto a1
= parse
!(int, string
, Yes
.doCount
)(s1
);
2549 assert(a1
.data
== 123 && a1
.count
== 3);
2551 // parse only accepts lvalues
2552 static assert(!__traits(compiles
, parse
!int("123")));
2558 import std
.string
: tr
;
2559 import std
.typecons
: Flag
, Yes
, No
;
2560 string
test = "123 \t 76.14";
2561 auto a
= parse
!uint(test);
2563 assert(test == " \t 76.14"); // parse bumps string
2564 test = tr(test, " \t\n\r", "", "d"); // skip ws
2565 assert(test == "76.14");
2566 auto b
= parse
!double(test);
2570 string test2
= "123 \t 76.14";
2571 auto a2
= parse
!(uint, string
, Yes
.doCount
)(test2
);
2572 assert(a2
.data
== 123 && a2
.count
== 3);
2573 assert(test2
== " \t 76.14");// parse bumps string
2574 test2
= tr(test2
, " \t\n\r", "", "d"); // skip ws
2575 assert(test2
== "76.14");
2576 auto b2
= parse
!(double, string
, Yes
.doCount
)(test2
);
2577 assert(b2
.data
== 76.14 && b2
.count
== 5);
2578 assert(test2
== "");
2584 static foreach (Int
; AliasSeq
!(byte, ubyte, short, ushort, int, uint, long, ulong))
2587 assert(to
!Int("0") == 0);
2589 static if (isSigned
!Int
)
2591 assert(to
!Int("+0") == 0);
2592 assert(to
!Int("-0") == 0);
2596 static if (Int
.sizeof
>= byte.sizeof
)
2598 assert(to
!Int("6") == 6);
2599 assert(to
!Int("23") == 23);
2600 assert(to
!Int("68") == 68);
2601 assert(to
!Int("127") == 0x7F);
2603 static if (isUnsigned
!Int
)
2605 assert(to
!Int("255") == 0xFF);
2607 static if (isSigned
!Int
)
2609 assert(to
!Int("+6") == 6);
2610 assert(to
!Int("+23") == 23);
2611 assert(to
!Int("+68") == 68);
2612 assert(to
!Int("+127") == 0x7F);
2614 assert(to
!Int("-6") == -6);
2615 assert(to
!Int("-23") == -23);
2616 assert(to
!Int("-68") == -68);
2617 assert(to
!Int("-128") == -128);
2621 static if (Int
.sizeof
>= short.sizeof
)
2623 assert(to
!Int("468") == 468);
2624 assert(to
!Int("32767") == 0x7FFF);
2626 static if (isUnsigned
!Int
)
2628 assert(to
!Int("65535") == 0xFFFF);
2630 static if (isSigned
!Int
)
2632 assert(to
!Int("+468") == 468);
2633 assert(to
!Int("+32767") == 0x7FFF);
2635 assert(to
!Int("-468") == -468);
2636 assert(to
!Int("-32768") == -32768);
2640 static if (Int
.sizeof
>= int.sizeof
)
2642 assert(to
!Int("2147483647") == 0x7FFFFFFF);
2644 static if (isUnsigned
!Int
)
2646 assert(to
!Int("4294967295") == 0xFFFFFFFF);
2649 static if (isSigned
!Int
)
2651 assert(to
!Int("+2147483647") == 0x7FFFFFFF);
2653 assert(to
!Int("-2147483648") == -2147483648);
2657 static if (Int
.sizeof
>= long.sizeof
)
2659 assert(to
!Int("9223372036854775807") == 0x7FFFFFFFFFFFFFFF);
2661 static if (isUnsigned
!Int
)
2663 assert(to
!Int("18446744073709551615") == 0xFFFFFFFFFFFFFFFF);
2666 static if (isSigned
!Int
)
2668 assert(to
!Int("+9223372036854775807") == 0x7FFFFFFFFFFFFFFF);
2670 assert(to
!Int("-9223372036854775808") == 0x8000000000000000);
2678 import std
.exception
;
2680 immutable string
[] errors
=
2699 immutable string
[] unsignedErrors
=
2705 // parsing error check
2706 static foreach (Int
; AliasSeq
!(byte, ubyte, short, ushort, int, uint, long, ulong))
2708 foreach (j
, s
; errors
)
2709 assertThrown
!ConvException(to
!Int(s
));
2711 // parse!SomeUnsigned cannot parse head sign.
2712 static if (isUnsigned
!Int
)
2714 foreach (j
, s
; unsignedErrors
)
2715 assertThrown
!ConvException(to
!Int(s
));
2719 immutable string
[] positiveOverflowErrors
=
2721 "128", // > byte.max
2722 "256", // > ubyte.max
2723 "32768", // > short.max
2724 "65536", // > ushort.max
2725 "2147483648", // > int.max
2726 "4294967296", // > uint.max
2727 "9223372036854775808", // > long.max
2728 "18446744073709551616", // > ulong.max
2730 // positive overflow check
2731 static foreach (i
, Int
; AliasSeq
!(byte, ubyte, short, ushort, int, uint, long, ulong))
2733 foreach (j
, s
; positiveOverflowErrors
[i
..$])
2734 assertThrown
!ConvOverflowException(to
!Int(s
));
2737 immutable string
[] negativeOverflowErrors
=
2739 "-129", // < byte.min
2740 "-32769", // < short.min
2741 "-2147483649", // < int.min
2742 "-9223372036854775809", // < long.min
2744 // negative overflow check
2745 static foreach (i
, Int
; AliasSeq
!(byte, short, int, long))
2747 foreach (j
, s
; negativeOverflowErrors
[i
..$])
2748 assertThrown
!ConvOverflowException(to
!Int(s
));
2754 void checkErrMsg(string input
, dchar charInMsg
, dchar charNotInMsg
)
2758 int x
= input
.to
!int();
2759 assert(false, "Invalid conversion did not throw");
2761 catch (ConvException e
)
2763 // Ensure error message contains failing character, not the character
2765 import std
.algorithm
.searching
: canFind
;
2766 assert( e
.msg
.canFind(charInMsg
) &&
2767 !e
.msg
.canFind(charNotInMsg
));
2771 assert(false, "Did not throw ConvException");
2774 checkErrMsg("@$", '@', '$');
2775 checkErrMsg("@$123", '@', '$');
2776 checkErrMsg("1@$23", '@', '$');
2777 checkErrMsg("1@$", '@', '$');
2778 checkErrMsg("1@$2", '@', '$');
2779 checkErrMsg("12@$", '@', '$');
2784 import std
.exception
;
2785 assertCTFEable
!({ string s
= "1234abc"; assert(parse
! int(s
) == 1234 && s
== "abc"); });
2786 assertCTFEable
!({ string s
= "-1234abc"; assert(parse
! int(s
) == -1234 && s
== "abc"); });
2787 assertCTFEable
!({ string s
= "1234abc"; assert(parse
!uint(s
) == 1234 && s
== "abc"); });
2789 assertCTFEable
!({ string s
= "1234abc"; assert(parse
!( int, string
, Yes
.doCount
)(s
) ==
2790 tuple( 1234, 4) && s
== "abc"); });
2791 assertCTFEable
!({ string s
= "-1234abc"; assert(parse
!( int, string
, Yes
.doCount
)(s
) ==
2792 tuple(-1234, 5) && s
== "abc"); });
2793 assertCTFEable
!({ string s
= "1234abc"; assert(parse
!(uint, string
, Yes
.doCount
)(s
) ==
2794 tuple( 1234 ,4) && s
== "abc"); });
2797 // https://issues.dlang.org/show_bug.cgi?id=13931
2800 import std
.exception
;
2802 assertThrown
!ConvOverflowException("-21474836480".to
!int());
2803 assertThrown
!ConvOverflowException("-92233720368547758080".to
!long());
2806 // https://issues.dlang.org/show_bug.cgi?id=14396
2809 struct StrInputRange
2811 this (string s
) { str = s
; }
2812 char front() const @property { return str[front_index
]; }
2813 char popFront() { return str[front_index
++]; }
2814 bool empty() const @property { return str.length
<= front_index
; }
2816 size_t front_index
= 0;
2818 auto input
= StrInputRange("777");
2819 assert(parse
!int(input
) == 777);
2821 auto input2
= StrInputRange("777");
2822 assert(parse
!(int, StrInputRange
, Yes
.doCount
)(input2
) == tuple(777, 3));
2825 // https://issues.dlang.org/show_bug.cgi?id=9621
2828 string s1
= "[ \"\\141\", \"\\0\", \"\\41\", \"\\418\" ]";
2829 assert(parse
!(string
[])(s1
) == ["a", "\0", "!", "!8"]);
2831 s1
= "[ \"\\141\", \"\\0\", \"\\41\", \"\\418\" ]";
2832 auto len
= s1
.length
;
2833 assert(parse
!(string
[], string
, Yes
.doCount
)(s1
) == tuple(["a", "\0", "!", "!8"], len
));
2837 auto parse(Target
, Source
, Flag
!"doCount" doCount
= No
.doCount
)(ref Source source
, uint radix
)
2838 if (isIntegral
!Target
&& !is(Target
== enum) &&
2839 isSomeChar
!(ElementType
!Source
))
2842 assert(radix
>= 2 && radix
<= 36, "radix must be in range [2,36]");
2846 import core
.checkedint
: mulu
, addu
;
2847 import std
.exception
: enforce
;
2851 return parse
!(Target
, Source
, doCount
)(source
);
2854 enforce
!ConvException(!source
.empty
, "s must not be empty in integral parse");
2856 immutable uint beyond
= (radix
< 10 ?
'0' : 'a'-10) + radix
;
2859 static if (isNarrowString
!Source
)
2861 import std
.string
: representation
;
2862 scope s
= source
.representation
;
2885 c |
= 0x20;//poorman's tolower
2886 if (c
< 'a' || c
>= beyond
)
2892 bool overflow
= false;
2893 auto nextv
= v
.mulu(radix
, overflow
).addu(c
- '0', overflow
);
2894 enforce
!ConvOverflowException(!overflow
&& nextv
<= Target
.max
, "Overflow in integral conversion");
2895 v
= cast(Target
) nextv
;
2903 static if (isNarrowString
!Source
)
2904 throw convError
!(Source
, Target
)(cast(Source
) source
);
2906 throw convError
!(Source
, Target
)(source
);
2909 static if (isNarrowString
!Source
)
2910 source
= source
[$ - s
.length
.. $];
2914 return tuple
!("data", "count")(v
, count
);
2924 string s
; // parse doesn't accept rvalues
2925 foreach (i
; 2 .. 37)
2927 assert(parse
!int(s
= "0", i
) == 0);
2928 assert(parse
!int(s
= "1", i
) == 1);
2929 assert(parse
!byte(s
= "10", i
) == i
);
2930 assert(parse
!(int, string
, Yes
.doCount
)(s
= "0", i
) == tuple(0, 1));
2931 assert(parse
!(int, string
, Yes
.doCount
)(s
= "1", i
) == tuple(1, 1));
2932 assert(parse
!(byte, string
, Yes
.doCount
)(s
= "10", i
) == tuple(i
, 2));
2935 assert(parse
!int(s
= "0011001101101", 2) == 0b0011001101101);
2936 assert(parse
!int(s
= "765", 8) == octal
!765);
2937 assert(parse
!int(s
= "000135", 8) == octal
!"135");
2938 assert(parse
!int(s
= "fCDe", 16) == 0xfcde);
2940 // https://issues.dlang.org/show_bug.cgi?id=6609
2941 assert(parse
!int(s
= "-42", 10) == -42);
2943 assert(parse
!ubyte(s
= "ff", 16) == 0xFF);
2946 // https://issues.dlang.org/show_bug.cgi?id=7302
2949 import std
.range
: cycle
;
2950 auto r
= cycle("2A!");
2951 auto u
= parse
!uint(r
, 16);
2953 assert(r
.front
== '!');
2955 auto r2
= cycle("2A!");
2956 auto u2
= parse
!(uint, typeof(r2
), Yes
.doCount
)(r2
, 16);
2957 assert(u2
.data
== 42 && u2
.count
== 2);
2958 assert(r2
.front
== '!');
2961 // https://issues.dlang.org/show_bug.cgi?id=13163
2964 import std
.exception
;
2965 foreach (s
; ["fff", "123"])
2966 assertThrown
!ConvOverflowException(s
.parse
!ubyte(16));
2969 // https://issues.dlang.org/show_bug.cgi?id=17282
2972 auto str = "0=\x00\x02\x55\x40&\xff\xf0\n\x00\x04\x55\x40\xff\xf0~4+10\n";
2973 assert(parse
!uint(str) == 0);
2975 str = "0=\x00\x02\x55\x40&\xff\xf0\n\x00\x04\x55\x40\xff\xf0~4+10\n";
2976 assert(parse
!(uint, string
, Yes
.doCount
)(str) == tuple(0, 1));
2979 // https://issues.dlang.org/show_bug.cgi?id=18248
2982 import std
.exception
: assertThrown
;
2985 assertThrown(str.parse
!uint(16));
2986 assertThrown(str.parse
!(uint, string
, Yes
.doCount
)(16));
2990 * Parses an `enum` type from a string representing an enum member name.
2993 * Target = the `enum` type to convert to
2994 * s = the lvalue of the range to _parse
2995 * doCount = the flag for deciding to report the number of consumed characters
2999 * $(LI An `enum` of type `Target` if `doCount` is set to `No.doCount`)
3000 * $(LI A `tuple` containing an `enum` of type `Target` and a `size_t` if `doCount` is set to `Yes.doCount`))
3003 * A $(LREF ConvException) if type `Target` does not have a member
3004 * represented by `s`.
3006 auto parse(Target
, Source
, Flag
!"doCount" doCount
= No
.doCount
)(ref Source s
)
3007 if (is(Target
== enum) && isSomeString
!Source
&& !is(Source
== enum))
3009 import std
.algorithm
.searching
: startsWith
;
3010 import std
.traits
: Unqual
, EnumMembers
;
3012 Unqual
!Target result
;
3013 size_t longest_match
= 0;
3015 foreach (i
, e
; EnumMembers
!Target
)
3017 auto ident
= __traits(allMembers
, Target
)[i
];
3018 if (longest_match
< ident
.length
&& s
.startsWith(ident
))
3021 longest_match
= ident
.length
;
3025 if (longest_match
> 0)
3027 s
= s
[longest_match
.. $];
3030 return tuple
!("data", "count")(result
, longest_match
);
3038 throw new ConvException(
3039 Target
.stringof
~ " does not have a member named '"
3040 ~ to
!string(s
) ~ "'");
3046 import std
.typecons
: Flag
, Yes
, No
, tuple
;
3047 enum EnumType
: bool { a
= true, b
= false, c
= a
}
3050 assert(parse
!EnumType(str) == EnumType
.a
);
3052 assert(parse
!(EnumType
, string
, No
.doCount
)(str2
) == EnumType
.a
);
3054 assert(parse
!(EnumType
, string
, Yes
.doCount
)(str3
) == tuple(EnumType
.a
, 1));
3060 import std
.exception
;
3062 enum EB
: bool { a
= true, b
= false, c
= a
}
3064 enum EI
{ a
= -1, b
= 0, c
= 1 }
3065 enum EF
: real { a
= 1.414, b
= 1.732, c
= 2.236 }
3066 enum EC
: char { a
= 'a', b
= 'b', c
= 'c' }
3067 enum ES
: string
{ a
= "aaa", b
= "bbb", c
= "ccc" }
3069 static foreach (E
; AliasSeq
!(EB
, EU
, EI
, EF
, EC
, ES
))
3071 assert(to
!E("a"c
) == E
.a
);
3072 assert(to
!E("b"w
) == E
.b
);
3073 assert(to
!E("c"d
) == E
.c
);
3075 assert(to
!(const E
)("a") == E
.a
);
3076 assert(to
!(immutable E
)("a") == E
.a
);
3077 assert(to
!(shared E
)("a") == E
.a
);
3079 assertThrown
!ConvException(to
!E("d"));
3083 // https://issues.dlang.org/show_bug.cgi?id=4744
3086 enum A
{ member1
, member11
, member111
}
3087 assert(to
!A("member1" ) == A
.member1
);
3088 assert(to
!A("member11" ) == A
.member11
);
3089 assert(to
!A("member111") == A
.member111
);
3090 auto s
= "member1111";
3091 assert(parse
!A(s
) == A
.member111
&& s
== "1");
3092 auto s2
= "member1111";
3093 assert(parse
!(A
, string
, No
.doCount
)(s2
) == A
.member111
&& s2
== "1");
3094 auto s3
= "member1111";
3095 assert(parse
!(A
, string
, Yes
.doCount
)(s3
) == tuple(A
.member111
, 9) && s3
== "1");
3099 * Parses a floating point number from a character range.
3102 * Target = a floating point type
3103 * source = the lvalue of the range to _parse
3104 * doCount = the flag for deciding to report the number of consumed characters
3108 * $(LI A floating point number of type `Target` if `doCount` is set to `No.doCount`)
3109 * $(LI A `tuple` containing a floating point number of·type `Target` and a `size_t`
3110 * if `doCount` is set to `Yes.doCount`))
3113 * A $(LREF ConvException) if `source` is empty, if no number could be
3114 * parsed, or if an overflow occurred.
3116 auto parse(Target
, Source
, Flag
!"doCount" doCount
= No
.doCount
)(ref Source source
)
3117 if (isFloatingPoint
!Target
&& !is(Target
== enum) &&
3118 isInputRange
!Source
&& isSomeChar
!(ElementType
!Source
) && !is(Source
== enum))
3120 import std
.ascii
: isDigit
, isAlpha
, toLower
, toUpper
, isHexDigit
;
3121 import std
.exception
: enforce
;
3123 static if (isNarrowString
!Source
)
3125 import std
.string
: representation
;
3126 scope p
= source
.representation
;
3133 void advanceSource()
3135 static if (isNarrowString
!Source
)
3136 source
= source
[$ - p
.length
.. $];
3139 static immutable real[14] negtab
=
3140 [ 1e-4096L,1e-2048L,1e-1024L,1e-512L,1e-256L,1e-128L,1e-64L,1e-32L,
3141 1e-16L,1e-8L,1e-4L,1e-2L,1e-1L,1.0L ];
3142 static immutable real[13] postab
=
3143 [ 1e+4096L,1e+2048L,1e+1024L,1e+512L,1e+256L,1e+128L,1e+64L,1e+32L,
3144 1e+16L,1e+8L,1e+4L,1e+2L,1e+1L ];
3146 ConvException
bailOut()(string msg
= null, string fn
= __FILE__
, size_t ln
= __LINE__
)
3149 msg
= "Floating point conversion error";
3150 return new ConvException(text(msg
, " for input \"", source
, "\"."), fn
, ln
);
3153 enforce(!p
.empty
, bailOut());
3164 enforce(!p
.empty
, bailOut());
3165 if (toLower(p
.front
) == 'i')
3171 enforce(!p
.empty
, bailOut());
3177 enforce(!p
.empty
&& toUpper(p
.front
) == 'N',
3178 bailOut("error converting input to floating point"));
3181 enforce(!p
.empty
&& toUpper(p
.front
) == 'F',
3182 bailOut("error converting input to floating point"));
3183 // skip past the last 'f'
3189 return tuple
!("data", "count")(sign ?
-Target
.infinity
: Target
.infinity
, count
);
3193 return sign ?
-Target
.infinity
: Target
.infinity
;
3199 bool startsWithZero
= p
.front
== '0';
3209 return tuple
!("data", "count")(cast (Target
) (sign ?
-0.0 : 0.0), count
);
3213 return sign ?
-0.0 : 0.0;
3217 isHex
= p
.front
== 'x' || p
.front
== 'X';
3224 else if (toLower(p
.front
) == 'n')
3229 enforce(!p
.empty
&& toUpper(p
.front
) == 'A',
3230 bailOut("error converting input to floating point"));
3233 enforce(!p
.empty
&& toUpper(p
.front
) == 'N',
3234 bailOut("error converting input to floating point"));
3235 // skip past the last 'n'
3241 return tuple
!("data", "count")(Target
.nan
, count
);
3245 return typeof(return).nan
;
3250 * The following algorithm consists of 2 steps:
3251 * 1) parseDigits processes the textual input into msdec and possibly
3252 * lsdec/msscale variables, followed by the exponent parser which sets
3254 * Hex: input is 0xaaaaa...p+000... where aaaa is the mantissa in hex
3255 * and 000 is the exponent in decimal format with base 2.
3256 * Decimal: input is 0.00333...p+000... where 0.0033 is the mantissa
3257 * in decimal and 000 is the exponent in decimal format with base 10.
3258 * 2) Convert msdec/lsdec and exp into native real format
3262 char dot
= 0; /* if decimal point has been seen */
3264 ulong msdec
= 0, lsdec
= 0;
3268 enum { hex
, decimal
}
3270 // sets msdec, lsdec/msscale, and sawDigits by parsing the mantissa digits
3271 void parseDigits(alias FloatFormat
)()
3273 static if (FloatFormat
== hex
)
3275 enum uint base
= 16;
3276 enum ulong msscaleMax
= 0x1000_0000_0000_0000UL; // largest power of 16 a ulong holds
3277 enum ubyte expIter
= 4; // iterate the base-2 exponent by 4 for every hex digit
3278 alias checkDigit
= isHexDigit
;
3280 * convert letter to binary representation: First clear bit
3281 * to convert lower space chars to upperspace, then -('A'-10)
3282 * converts letter A to 10, letter B to 11, ...
3284 alias convertDigit
= (int x
) => isAlpha(x
) ?
((x
& ~0x20) - ('A' - 10)) : x
- '0';
3287 else static if (FloatFormat
== decimal
)
3289 enum uint base
= 10;
3290 enum ulong msscaleMax
= 10_000_000_000_000_000_000UL; // largest power of 10 a ulong holds
3291 enum ubyte expIter
= 1; // iterate the base-10 exponent once for every decimal digit
3292 alias checkDigit
= isDigit
;
3293 alias convertDigit
= (int x
) => x
- '0';
3294 // Used to enforce that any mantissa digits are present
3295 sawDigits
= startsWithZero
;
3298 static assert(false, "Unrecognized floating-point format used.");
3303 while (checkDigit(i
))
3305 sawDigits
= true; /* must have at least 1 digit */
3307 i
= convertDigit(i
);
3309 if (msdec
< (ulong.max
- base
)/base
)
3311 // For base 16: Y = ... + y3*16^3 + y2*16^2 + y1*16^1 + y0*16^0
3312 msdec
= msdec
* base
+ i
;
3314 else if (msscale
< msscaleMax
)
3316 lsdec
= lsdec
* base
+ i
;
3338 if (i
== '.' && !dot
)
3348 // Have we seen any mantissa digits so far?
3349 enforce(sawDigits
, bailOut("no digits seen"));
3350 static if (FloatFormat
== hex
)
3351 enforce(!p
.empty
&& (p
.front
== 'p' || p
.front
== 'P'),
3352 bailOut("Floating point parsing: exponent is required"));
3358 parseDigits
!decimal
;
3360 if (isHex ||
(!p
.empty
&& (p
.front
== 'e' || p
.front
== 'E')))
3367 enforce(!p
.empty
, new ConvException("Unexpected end of input"));
3378 while (!p
.empty
&& isDigit(p
.front
))
3380 if (e
< 0x7FFFFFFF / 10 - 10) // prevent integer overflow
3382 e
= e
* 10 + p
.front
- '0';
3388 exp
+= (sexp
) ?
-e
: e
;
3389 enforce(sawDigits
, new ConvException("No digits seen."));
3393 if (msscale
!= 1) /* if stuff was accumulated in lsdec */
3394 ldval
= ldval
* msscale
+ lsdec
;
3397 import core
.math
: ldexp
;
3399 // Exponent is power of 2, not power of 10
3400 ldval
= ldexp(ldval
,exp
);
3422 enforce(ldval
!= 0, new ConvException("Range error"));
3430 Target result
= cast(Target
) (sign ?
-ldval
: ldval
);
3432 // if overflow occurred
3433 import std
.math
.traits
: isFinite
;
3434 enforce(isFinite(result
), new ConvException("Range error"));
3439 return tuple
!("data", "count")(result
, count
);
3451 import std
.math
.operations
: isClose
;
3452 import std
.math
.traits
: isNaN
, isInfinity
;
3453 import std
.typecons
: Flag
, Yes
, No
;
3454 auto str = "123.456";
3455 assert(parse
!double(str).isClose(123.456));
3456 auto str2
= "123.456";
3457 assert(parse
!(double, string
, No
.doCount
)(str2
).isClose(123.456));
3458 auto str3
= "123.456";
3459 auto r
= parse
!(double, string
, Yes
.doCount
)(str3
);
3460 assert(r
.data
.isClose(123.456));
3461 assert(r
.count
== 7);
3462 auto str4
= "-123.456";
3463 r
= parse
!(double, string
, Yes
.doCount
)(str4
);
3464 assert(r
.data
.isClose(-123.456));
3465 assert(r
.count
== 8);
3466 auto str5
= "+123.456";
3467 r
= parse
!(double, string
, Yes
.doCount
)(str5
);
3468 assert(r
.data
.isClose(123.456));
3469 assert(r
.count
== 8);
3471 r
= parse
!(double, string
, Yes
.doCount
)(str6
);
3472 assert(isInfinity(r
.data
) && r
.count
== 3 && str6
== "0");
3474 auto r2
= parse
!(float, string
, Yes
.doCount
)(str7
);
3475 assert(r2
.data
.isClose(0.0) && r2
.count
== 2);
3477 auto r3
= parse
!(real, string
, Yes
.doCount
)(str8
);
3478 assert(isNaN(r3
.data
) && r3
.count
== 3);
3483 import std
.exception
;
3484 import std
.math
.traits
: isNaN
, isInfinity
;
3485 import std
.math
.algebraic
: fabs;
3487 // Compare reals with given precision
3488 bool feq(in real rx
, in real ry
, in real precision
= 0.000001L)
3494 return cast(bool) isNaN(ry
);
3499 return cast(bool)(fabs(rx
- ry
) <= precision
);
3502 // Make given typed literal
3508 static foreach (Float
; AliasSeq
!(float, double, real))
3510 assert(to
!Float("123") == Literal
!Float(123));
3511 assert(to
!Float("+123") == Literal
!Float(+123));
3512 assert(to
!Float("-123") == Literal
!Float(-123));
3513 assert(to
!Float("123e2") == Literal
!Float(123e2
));
3514 assert(to
!Float("123e+2") == Literal
!Float(123e+2));
3515 assert(to
!Float("123e-2") == Literal
!Float(123e-2L));
3516 assert(to
!Float("123.") == Literal
!Float(123.0));
3517 assert(to
!Float(".375") == Literal
!Float(.375));
3519 assert(to
!Float("1.23375E+2") == Literal
!Float(1.23375E+2));
3521 assert(to
!Float("0") is 0.0);
3522 assert(to
!Float("-0") is -0.0);
3524 assert(isNaN(to
!Float("nan")));
3526 assertThrown
!ConvException(to
!Float("\x00"));
3530 float f
= to
!float("1.17549e-38");
3531 assert(feq(cast(real) f
, cast(real) 1.17549e-38));
3532 assert(feq(cast(real) f
, cast(real) float.min_normal
));
3533 f
= to
!float("3.40282e+38");
3534 assert(to
!string(f
) == to
!string(3.40282e+38));
3537 double d
= to
!double("2.22508e-308");
3538 assert(feq(cast(real) d
, cast(real) 2.22508e-308));
3539 assert(feq(cast(real) d
, cast(real) double.min_normal
));
3540 d
= to
!double("1.79769e+308");
3541 assert(to
!string(d
) == to
!string(1.79769e+308));
3542 assert(to
!string(d
) == to
!string(double.max
));
3544 auto z
= real.max
/ 2L;
3545 static assert(is(typeof(z
) == real));
3547 assert(!isInfinity(z
));
3548 string a
= to
!string(z
);
3549 real b
= to
!real(a
);
3550 string c
= to
!string(b
);
3552 assert(c
== a
, "\n" ~ c
~ "\n" ~ a
);
3554 assert(to
!string(to
!real(to
!string(real.max
/ 2L))) == to
!string(real.max
/ 2L));
3557 real r
= to
!real(to
!string(real.min_normal
));
3561 // to!string returns 3.3621e-4932L. It is less than real.min_normal and it is subnormal value
3563 // long double rd = 3.3621e-4932L;
3564 // printf("%Le\n", rd);
3565 // has unexpected result: 1.681050e-4932
3567 // Bug report: http://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=50937
3571 assert(to
!string(r
) == to
!string(real.min_normal
));
3573 r
= to
!real(to
!string(real.max
));
3574 assert(to
!string(r
) == to
!string(real.max
));
3576 real pi
= 3.1415926535897932384626433832795028841971693993751L;
3577 string fullPrecision
= "3.1415926535897932384626433832795028841971693993751";
3578 assert(feq(parse
!real(fullPrecision
), pi
, 2*real.epsilon
));
3579 string fullPrecision2
= "3.1415926535897932384626433832795028841971693993751";
3580 assert(feq(parse
!(real, string
, No
.doCount
)(fullPrecision2
), pi
, 2*real.epsilon
));
3581 string fullPrecision3
= "3.1415926535897932384626433832795028841971693993751";
3582 auto len
= fullPrecision3
.length
;
3583 auto res
= parse
!(real, string
, Yes
.doCount
)(fullPrecision3
);
3584 assert(feq(res
.data
, pi
, 2*real.epsilon
));
3585 assert(res
.count
== len
);
3587 real x
= 0x1.FAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAAFAAFAFAFAFAFAFAFAP
-252L;
3588 string full
= "0x1.FAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAAFAAFAFAFAFAFAFAFAP-252";
3589 assert(parse
!real(full
) == x
);
3590 string full2
= "0x1.FAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAAFAAFAFAFAFAFAFAFAP-252";
3591 assert(parse
!(real, string
, No
.doCount
)(full2
) == x
);
3592 string full3
= "0x1.FAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAAFAAFAFAFAFAFAFAFAP-252";
3593 auto len2
= full3
.length
;
3594 assert(parse
!(real, string
, Yes
.doCount
)(full3
) == tuple(x
, len2
));
3597 // Tests for the double implementation
3600 // @system because strtod is not @safe.
3601 import std
.math
.traits
: floatTraits
, RealFormat
;
3603 static if (floatTraits
!real.realFormat
== RealFormat
.ieeeDouble
)
3605 import core
.stdc
.stdlib
, std
.exception
, std
.math
;
3607 //Should be parsed exactly: 53 bit mantissa
3608 string s
= "0x1A_BCDE_F012_3456p10";
3609 auto x
= parse
!real(s
);
3610 assert(x
== 0x1A_BCDE_F
012_3456p
10L);
3612 assert(((*cast(ulong*)&x
) & 0x000F_FFFF_FFFF_FFFF
) == 0xA_BCDE_F
012_3456);
3613 assert(strtod("0x1ABCDEF0123456p10", null) == x
);
3615 s
= "0x1A_BCDE_F012_3456p10";
3616 auto len
= s
.length
;
3617 assert(parse
!(real, string
, Yes
.doCount
)(s
) == tuple(x
, len
));
3619 //Should be parsed exactly: 10 bit mantissa
3622 assert(x
== 0x03FFp
10);
3624 assert(((*cast(ulong*)&x
) & 0x000F_FFFF_FFFF_FFFF
) == 0x000F_F
800_0000_0000);
3625 assert(strtod("0x3FFp10", null) == x
);
3627 //60 bit mantissa, round up
3628 s
= "0xFFF_FFFF_FFFF_FFFFp10";
3630 assert(isClose(x
, 0xFFF_FFFF_FFFF_FFFFp
10));
3632 assert(((*cast(ulong*)&x
) & 0x000F_FFFF_FFFF_FFFF
) == 0x0000_0000_0000_0000);
3633 assert(strtod("0xFFFFFFFFFFFFFFFp10", null) == x
);
3635 //60 bit mantissa, round down
3636 s
= "0xFFF_FFFF_FFFF_FF90p10";
3638 assert(isClose(x
, 0xFFF_FFFF_FFFF_FF
90p
10));
3640 assert(((*cast(ulong*)&x
) & 0x000F_FFFF_FFFF_FFFF
) == 0x000F_FFFF_FFFF_FFFF
);
3641 assert(strtod("0xFFFFFFFFFFFFF90p10", null) == x
);
3643 //61 bit mantissa, round up 2
3644 s
= "0x1F0F_FFFF_FFFF_FFFFp10";
3646 assert(isClose(x
, 0x1F0F_FFFF_FFFF_FFFFp
10));
3648 assert(((*cast(ulong*)&x
) & 0x000F_FFFF_FFFF_FFFF
) == 0x000F_1000_0000_0000);
3649 assert(strtod("0x1F0FFFFFFFFFFFFFp10", null) == x
);
3651 //61 bit mantissa, round down 2
3652 s
= "0x1F0F_FFFF_FFFF_FF10p10";
3654 assert(isClose(x
, 0x1F0F_FFFF_FFFF_FF
10p
10));
3656 assert(((*cast(ulong*)&x
) & 0x000F_FFFF_FFFF_FFFF
) == 0x000F_0FFF_FFFF_FFFF
);
3657 assert(strtod("0x1F0FFFFFFFFFFF10p10", null) == x
);
3660 s
= "0x1F_FFFF_FFFF_FFFFp900";
3662 assert(strtod("0x1FFFFFFFFFFFFFp900", null) == x
);
3664 //exponent too big -> converror
3666 assertThrown
!ConvException(x
= parse
!real(s
));
3667 assert(strtod("0x1FFFFFFFFFFFFFp1024", null) == real.infinity
);
3669 //-exponent too big -> 0
3670 s
= "0x1FFFFFFFFFFFFFp-2000";
3673 assert(strtod("0x1FFFFFFFFFFFFFp-2000", null) == x
);
3675 s
= "0x1FFFFFFFFFFFFFp-2000";
3677 assert(parse
!(real, string
, Yes
.doCount
)(s
) == tuple(x
, len
));
3683 import core
.stdc
.errno
;
3684 import core
.stdc
.stdlib
;
3685 import std
.math
.traits
: floatTraits
, RealFormat
;
3687 errno
= 0; // In case it was set by another unittest in a different module.
3690 static if (floatTraits
!real.realFormat
== RealFormat
.ieeeQuadruple
)
3694 else static if (floatTraits
!real.realFormat
== RealFormat
.ieeeExtended ||
3695 floatTraits
!real.realFormat
== RealFormat
.ieeeExtended53
)
3699 else static if (floatTraits
!real.realFormat
== RealFormat
.ieeeDouble
)
3704 static assert(false, "Not implemented");
3713 static if (floatTraits
!real.realFormat
== RealFormat
.ieeeQuadruple
)
3714 enum s
= "0x1.FFFFFFFFFFFFFFFFFFFFFFFFFFFFp-16382";
3715 else static if (floatTraits
!real.realFormat
== RealFormat
.ieeeExtended
)
3716 enum s
= "0x1.FFFFFFFFFFFFFFFEp-16382";
3717 else static if (floatTraits
!real.realFormat
== RealFormat
.ieeeExtended53
)
3718 enum s
= "0x1.FFFFFFFFFFFFFFFEp-16382";
3719 else static if (floatTraits
!real.realFormat
== RealFormat
.ieeeDouble
)
3720 enum s
= "0x1.FFFFFFFFFFFFFFFEp-1000";
3722 static assert(false, "Floating point format for real not supported");
3725 ld
= parse
!real(s2
);
3727 x
= *cast(longdouble
*)&ld
;
3729 static if (floatTraits
!real.realFormat
== RealFormat
.ieeeExtended
)
3731 version (CRuntime_Microsoft
)
3732 ld1
= 0x1.FFFFFFFFFFFFFFFEp
-16382L; // strtold currently mapped to strtod
3734 ld1
= strtold(s
.ptr
, null);
3736 else static if (floatTraits
!real.realFormat
== RealFormat
.ieeeExtended53
)
3737 ld1
= 0x1.FFFFFFFFFFFFFFFEp
-16382L; // strtold rounds to 53 bits.
3739 ld1
= strtold(s
.ptr
, null);
3741 x1
= *cast(longdouble
*)&ld1
;
3742 assert(x1
== x
&& ld1
== ld
);
3747 ld
= parse
!real(s2
);
3749 x
= *cast(longdouble
*)&ld
;
3750 ld1
= strtold("1.0e5", null);
3751 x1
= *cast(longdouble
*)&ld1
;
3756 import std
.exception
;
3758 // https://issues.dlang.org/show_bug.cgi?id=4959
3761 auto x
= parse
!double(s
);
3767 auto x
= parse
!(double, string
, Yes
.doCount
)(s
);
3769 assert(x
== tuple(0.0, 1));
3772 // https://issues.dlang.org/show_bug.cgi?id=3369
3773 assert(to
!float("inf") == float.infinity
);
3774 assert(to
!float("-inf") == -float.infinity
);
3776 // https://issues.dlang.org/show_bug.cgi?id=6160
3777 assert(6_5.536e3L
== to
!real("6_5.536e3")); // 2^16
3778 assert(0x1000_000_000_p
10 == to
!real("0x1000_000_000_p10")); // 7.03687e+13
3780 // https://issues.dlang.org/show_bug.cgi?id=6258
3781 assertThrown
!ConvException(to
!real("-"));
3782 assertThrown
!ConvException(to
!real("in"));
3784 // https://issues.dlang.org/show_bug.cgi?id=7055
3785 assertThrown
!ConvException(to
!float("INF2"));
3787 //extra stress testing
3788 auto ssOK
= ["1.", "1.1.1", "1.e5", "2e1e", "2a", "2e1_1", "3.4_",
3789 "inf", "-inf", "infa", "-infa", "inf2e2", "-inf2e2",
3790 "nan", "-NAN", "+NaN", "-nAna", "NAn2e2", "-naN2e2"];
3791 auto ssKO
= ["", " ", "2e", "2e+", "2e-", "2ee", "2e++1", "2e--1", "2e_1",
3792 "+inf", "-in", "I", "+N", "-NaD", "0x3.F"];
3796 assertThrown
!ConvException(parse
!double(s
));
3799 @safe unittest // https://issues.dlang.org/show_bug.cgi?id=22637
3801 import std
.exception
: assertThrown
, assertNotThrown
;
3802 auto src
= "9991232549867999698999493543521458974414359998784641646846435132132543645435456345634541999999999999999"
3803 ~ "9999999943321231321311999231345312413646846354354354399999934153465464654646464654134135354199999999996515734999"
3804 ~ "9999999320135273486741354354731567431324134999999999999999999999999999999999999999999999135411.9";
3805 assertThrown
!ConvException(parse
!double(src
));
3806 static if (real.max_10_exp
> 310) assertNotThrown
!ConvException(parse
!real(src
));
3810 Parses one character from a character range.
3813 Target = the type to convert to
3814 s = the lvalue of an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
3815 doCount = the flag for deciding to report the number of consumed characters
3819 $(LI A character of type `Target` if `doCount` is set to `No.doCount`)
3820 $(LI A `tuple` containing a character of type `Target` and a `size_t` if `doCount` is set to `Yes.doCount`))
3823 A $(LREF ConvException) if the range is empty.
3825 auto parse(Target
, Source
, Flag
!"doCount" doCount
= No
.doCount
)(ref Source s
)
3826 if (staticIndexOf
!(immutable Target
, immutable dchar, immutable ElementEncodingType
!Source
) >= 0 &&
3827 isSomeString
!Source
&& !is(Source
== enum))
3830 throw convError
!(Source
, Target
)(s
);
3831 static if (is(immutable Target
== immutable dchar))
3833 Target result
= s
.front
;
3837 return tuple
!("data", "count")(result
, 1);
3847 // Special case: okay so parse a Char off a Char[]
3848 Target result
= s
[0];
3852 return tuple
!("data", "count")(result
, 1);
3863 static foreach (Str
; AliasSeq
!(string
, wstring
, dstring
))
3865 static foreach (Char
; AliasSeq
!(char, wchar, dchar))
3867 static if (is(immutable Char
== immutable dchar) ||
3868 Char
.sizeof
== ElementEncodingType
!Str
.sizeof
)
3871 assert(parse
!Char(s
) == 'a');
3873 assert(parse
!(Char
, typeof(s
), No
.doCount
)(s
) == 'a');
3875 assert(parse
!(Char
, typeof(s
), Yes
.doCount
)(s
) == tuple('a', 1) && s
== "");
3882 auto parse(Target
, Source
, Flag
!"doCount" doCount
= No
.doCount
)(ref Source s
)
3883 if (isSomeChar
!Target
&& Target
.sizeof
>= ElementType
!Source
.sizeof
&& !is(Target
== enum) &&
3884 !isSomeString
!Source
&& isInputRange
!Source
&& isSomeChar
!(ElementType
!Source
))
3887 throw convError
!(Source
, Target
)(s
);
3888 Target result
= s
.front
;
3892 return tuple
!("data", "count")(result
, 1);
3903 import std
.typecons
: Flag
, Yes
, No
;
3904 auto s
= "Hello, World!";
3905 char first
= parse
!char(s
);
3906 assert(first
== 'H');
3907 assert(s
== "ello, World!");
3908 char second
= parse
!(char, string
, No
.doCount
)(s
);
3909 assert(second
== 'e');
3910 assert(s
== "llo, World!");
3911 auto third
= parse
!(char, string
, Yes
.doCount
)(s
);
3912 assert(third
.data
== 'l' && third
.count
== 1);
3913 assert(s
== "lo, World!");
3918 Tests for to!bool and parse!bool
3922 import std
.exception
;
3924 assert(to
!bool("TruE") == true);
3925 assert(to
!bool("faLse"d
) == false);
3926 assertThrown
!ConvException(to
!bool("maybe"));
3928 auto t
= "TrueType";
3929 assert(parse
!bool(t
) == true);
3930 assert(t
== "Type");
3932 auto f
= "False killer whale"d
;
3933 assert(parse
!bool(f
) == false);
3934 assert(f
== " killer whale"d
);
3936 f
= "False killer whale"d
;
3937 assert(parse
!(bool, dstring
, Yes
.doCount
)(f
) == tuple(false, 5));
3938 assert(f
== " killer whale"d
);
3941 assertThrown
!ConvException(parse
!bool(m
));
3942 assertThrown
!ConvException(parse
!(bool, string
, Yes
.doCount
)(m
));
3943 assert(m
== "maybe"); // m shouldn't change on failure
3946 auto b
= parse
!(const(bool))(s
);
3951 Parses `typeof(null)` from a character range if the range
3952 spells `"null"`. This function is case insensitive.
3955 Target = the type to convert to
3956 s = the lvalue of an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
3957 doCount = the flag for deciding to report the number of consumed characters
3961 $(LI `null` if `doCount` is set to `No.doCount`)
3962 $(LI A `tuple` containing `null` and a `size_t` if `doCount` is set to `Yes.doCount`))
3965 A $(LREF ConvException) if the range doesn't represent `null`.
3967 auto parse(Target
, Source
, Flag
!"doCount" doCount
= No
.doCount
)(ref Source s
)
3968 if (is(immutable Target
== immutable typeof(null)) &&
3969 isInputRange
!Source
&&
3970 isSomeChar
!(ElementType
!Source
))
3972 import std
.ascii
: toLower
;
3975 if (s
.empty ||
toLower(s
.front
) != c
)
3976 throw parseError("null should be case-insensitive 'null'");
3981 return tuple
!("data", "count")(null, 4);
3992 import std
.exception
: assertThrown
;
3993 import std
.typecons
: Flag
, Yes
, No
;
3995 alias NullType
= typeof(null);
3997 assert(parse
!NullType(s1
) is null);
4001 assert(parse
!NullType(s2
) is null);
4004 auto s3
= "nuLlNULl";
4005 assert(parse
!(NullType
, string
, No
.doCount
)(s3
) is null);
4006 auto r
= parse
!(NullType
, string
, Yes
.doCount
)(s3
);
4007 assert(r
.data
is null && r
.count
== 4);
4010 assertThrown
!ConvException(parse
!NullType(m
));
4011 assertThrown
!ConvException(parse
!(NullType
, string
, Yes
.doCount
)(m
));
4012 assert(m
== "maybe"); // m shouldn't change on failure
4015 assert(parse
!(const NullType
)(s
) is null);
4018 //Used internally by parse Array/AA, to remove ascii whites
4019 package auto skipWS(R
, Flag
!"doCount" doCount
= No
.doCount
)(ref R r
)
4021 import std
.ascii
: isWhite
;
4022 static if (isSomeString
!R
)
4024 //Implementation inspired from stripLeft.
4040 auto len
= r
.length
;
4041 r
= r
[0 .. 0]; //Empty string with correct type.
4054 for (; !r
.empty
&& isWhite(r
.front
); r
.popFront(), ++i
)
4064 * Parses an array from a string given the left bracket (default $(D
4065 * '[')), right bracket (default `']'`), and element separator (by
4066 * default `','`). A trailing separator is allowed.
4069 * s = The string to parse
4070 * lbracket = the character that starts the array
4071 * rbracket = the character that ends the array
4072 * comma = the character that separates the elements of the array
4073 * doCount = the flag for deciding to report the number of consumed characters
4077 * $(LI An array of type `Target` if `doCount` is set to `No.doCount`)
4078 * $(LI A `tuple` containing an array of type `Target` and a `size_t` if `doCount` is set to `Yes.doCount`))
4080 auto parse(Target
, Source
, Flag
!"doCount" doCount
= No
.doCount
)(ref Source s
, dchar lbracket
= '[',
4081 dchar rbracket
= ']', dchar comma
= ',')
4082 if (isDynamicArray
!Target
&& !is(Target
== enum) &&
4083 isSomeString
!Source
&& !is(Source
== enum))
4085 import std
.array
: appender
;
4087 auto result
= appender
!Target();
4089 parseCheck
!s(lbracket
);
4090 size_t count
= 1 + skipWS
!(Source
, Yes
.doCount
)(s
);
4092 throw convError
!(Source
, Target
)(s
);
4093 if (s
.front
== rbracket
)
4098 return tuple
!("data", "count")(result
.data
, ++count
);
4105 for (;; s
.popFront(), count
+= 1 + skipWS
!(Source
, Yes
.doCount
)(s
))
4107 if (!s
.empty
&& s
.front
== rbracket
)
4109 auto r
= parseElement
!(WideElementType
!Target
, Source
, Yes
.doCount
)(s
);
4111 count
+= r
.count
+ skipWS
!(Source
, Yes
.doCount
)(s
);
4113 throw convError
!(Source
, Target
)(s
);
4114 if (s
.front
!= comma
)
4117 parseCheck
!s(rbracket
);
4120 return tuple
!("data", "count")(result
.data
, ++count
);
4131 import std
.typecons
: Flag
, Yes
, No
;
4132 auto s1
= `[['h', 'e', 'l', 'l', 'o'], "world"]`;
4133 auto a1
= parse
!(string
[])(s1
);
4134 assert(a1
== ["hello", "world"]);
4136 auto s2
= `["aaa", "bbb", "ccc"]`;
4137 auto a2
= parse
!(string
[])(s2
);
4138 assert(a2
== ["aaa", "bbb", "ccc"]);
4140 auto s3
= `[['h', 'e', 'l', 'l', 'o'], "world"]`;
4141 auto len3
= s3
.length
;
4142 auto a3
= parse
!(string
[], string
, Yes
.doCount
)(s3
);
4143 assert(a3
.data
== ["hello", "world"]);
4144 assert(a3
.count
== len3
);
4147 // https://issues.dlang.org/show_bug.cgi?id=9615
4150 import std
.typecons
: Flag
, Yes
, No
, tuple
;
4151 string s0
= "[1,2, ]";
4152 string s1
= "[1,2, \t\v\r\n]";
4153 string s2
= "[1,2]";
4154 assert(s0
.parse
!(int[]) == [1,2]);
4155 assert(s1
.parse
!(int[]) == [1,2]);
4156 assert(s2
.parse
!(int[]) == [1,2]);
4159 auto len0
= s0
.length
;
4160 s1
= "[1,2, \t\v\r\n]";
4161 auto len1
= s1
.length
;
4163 auto len2
= s2
.length
;
4164 assert(s0
.parse
!(int[], string
, Yes
.doCount
) == tuple([1,2], len0
));
4165 assert(s1
.parse
!(int[], string
, Yes
.doCount
) == tuple([1,2], len1
));
4166 assert(s2
.parse
!(int[], string
, Yes
.doCount
) == tuple([1,2], len2
));
4168 string s3
= `["a","b",]`;
4169 string s4
= `["a","b"]`;
4170 assert(s3
.parse
!(string
[]) == ["a","b"]);
4171 assert(s4
.parse
!(string
[]) == ["a","b"]);
4174 auto len3
= s3
.length
;
4175 assert(s3
.parse
!(string
[], string
, Yes
.doCount
) == tuple(["a","b"], len3
));
4178 assert(tuple([], s3
.length
) == s3
.parse
!(string
[], string
, Yes
.doCount
));
4180 import std
.exception
: assertThrown
;
4182 string s6
= "[, \t,]";
4183 assertThrown
!ConvException(parse
!(string
[])(s5
));
4184 assertThrown
!ConvException(parse
!(int[])(s6
));
4188 assertThrown
!ConvException(parse
!(string
[], string
, Yes
.doCount
)(s5
));
4189 assertThrown
!ConvException(parse
!(string
[], string
, Yes
.doCount
)(s6
));
4194 int[] a
= [1, 2, 3, 4, 5];
4195 auto s
= to
!string(a
);
4196 assert(to
!(int[])(s
) == a
);
4201 int[][] a
= [ [1, 2] , [3], [4, 5] ];
4202 auto s
= to
!string(a
);
4203 assert(to
!(int[][])(s
) == a
);
4208 int[][][] ia
= [ [[1,2],[3,4],[5]] , [[6],[],[7,8,9]] , [[]] ];
4210 char[] s
= to
!(char[])(ia
);
4213 ia2
= to
!(typeof(ia2
))(s
);
4219 import std
.exception
;
4220 import std
.typecons
: Flag
, Yes
, No
;
4222 //Check proper failure
4223 auto s
= "[ 1 , 2 , 3 ]";
4225 foreach (i
; 0 .. s
.length
-1)
4227 auto ss
= s
[0 .. i
];
4228 assertThrown
!ConvException(parse
!(int[])(ss
));
4229 assertThrown
!ConvException(parse
!(int[], string
, Yes
.doCount
)(ss
));
4231 int[] arr
= parse
!(int[])(s
);
4232 auto arr2
= parse
!(int[], string
, Yes
.doCount
)(s2
);
4238 //Checks parsing of strings with escaped characters
4240 "Contains a\0null!",
4243 "backslash \\ slash / question \?",
4245 "unicode \u65E5 sun",
4246 "very long \U000065E5 sun"
4249 //Note: escaped characters purposefully replaced and isolated to guarantee
4250 //there are no typos in the escape syntax
4252 "Contains a" ~ '\0' ~ "null!",
4253 "tab" ~ '\t' ~ "here",
4254 "line" ~ '\n' ~ "break",
4255 "backslash " ~ '\\' ~ " slash / question ?",
4260 string s3
= s1
.save
;
4261 assert(s2
== parse
!(string
[])(s1
));
4263 assert(tuple(s2
, s3
.length
) == parse
!(string
[], string
, Yes
.doCount
)(s3
));
4267 auto parse(Target
, Source
, Flag
!"doCount" doCount
= No
.doCount
)(ref Source s
, dchar lbracket
= '[',
4268 dchar rbracket
= ']', dchar comma
= ',')
4269 if (isStaticArray
!Target
&& !is(Target
== enum) &&
4270 isExactSomeString
!Source
)
4272 static if (hasIndirections
!Target
)
4273 Target result
= Target
.init
[0].init
;
4275 Target result
= void;
4277 parseCheck
!s(lbracket
);
4278 size_t count
= 1 + skipWS
!(Source
, Yes
.doCount
)(s
);
4280 throw convError
!(Source
, Target
)(s
);
4281 if (s
.front
== rbracket
)
4283 static if (result
.length
!= 0)
4290 return tuple
!("data", "count")(result
, ++count
);
4298 for (size_t i
= 0; ; s
.popFront(), count
+= 1 + skipWS
!(Source
, Yes
.doCount
)(s
))
4300 if (i
== result
.length
)
4302 auto r
= parseElement
!(ElementType
!Target
, Source
, Yes
.doCount
)(s
);
4303 result
[i
++] = r
.data
;
4304 count
+= r
.count
+ skipWS
!(Source
, Yes
.doCount
)(s
);
4306 throw convError
!(Source
, Target
)(s
);
4307 if (s
.front
!= comma
)
4309 if (i
!= result
.length
)
4314 parseCheck
!s(rbracket
);
4317 return tuple
!("data", "count")(result
, ++count
);
4326 throw parseError(text("Too many elements in input, ", result
.length
, " elements expected."));
4329 throw parseError(text("Too few elements in input, ", result
.length
, " elements expected."));
4334 import std
.exception
;
4336 auto s1
= "[1,2,3,4]";
4337 auto sa1
= parse
!(int[4])(s1
);
4338 assert(sa1
== [1,2,3,4]);
4340 assert(tuple([1,2,3,4], s1
.length
) == parse
!(int[4], string
, Yes
.doCount
)(s1
));
4342 auto s2
= "[[1],[2,3],[4]]";
4343 auto sa2
= parse
!(int[][3])(s2
);
4344 assert(sa2
== [[1],[2,3],[4]]);
4345 s2
= "[[1],[2,3],[4]]";
4346 assert(tuple([[1],[2,3],[4]], s2
.length
) == parse
!(int[][3], string
, Yes
.doCount
)(s2
));
4348 auto s3
= "[1,2,3]";
4349 assertThrown
!ConvException(parse
!(int[4])(s3
));
4350 assertThrown
!ConvException(parse
!(int[4], string
, Yes
.doCount
)(s3
));
4352 auto s4
= "[1,2,3,4,5]";
4353 assertThrown
!ConvException(parse
!(int[4])(s4
));
4354 assertThrown
!ConvException(parse
!(int[4], string
, Yes
.doCount
)(s4
));
4358 * Parses an associative array from a string given the left bracket (default $(D
4359 * '[')), right bracket (default `']'`), key-value separator (default $(D
4360 * ':')), and element seprator (by default `','`).
4363 * s = the string to parse
4364 * lbracket = the character that starts the associative array
4365 * rbracket = the character that ends the associative array
4366 * keyval = the character that associates the key with the value
4367 * comma = the character that separates the elements of the associative array
4368 * doCount = the flag for deciding to report the number of consumed characters
4372 * $(LI An associative array of type `Target` if `doCount` is set to `No.doCount`)
4373 * $(LI A `tuple` containing an associative array of type `Target` and a `size_t`
4374 * if `doCount` is set to `Yes.doCount`))
4376 auto parse(Target
, Source
, Flag
!"doCount" doCount
= No
.doCount
)(ref Source s
, dchar lbracket
= '[',
4377 dchar rbracket
= ']', dchar keyval
= ':', dchar comma
= ',')
4378 if (isAssociativeArray
!Target
&& !is(Target
== enum) &&
4379 isSomeString
!Source
&& !is(Source
== enum))
4381 alias KeyType
= typeof(Target
.init
.keys
[0]);
4382 alias ValType
= typeof(Target
.init
.values
[0]);
4386 parseCheck
!s(lbracket
);
4387 size_t count
= 1 + skipWS
!(Source
, Yes
.doCount
)(s
);
4389 throw convError
!(Source
, Target
)(s
);
4390 if (s
.front
== rbracket
)
4395 return tuple
!("data", "count")(result
, ++count
);
4402 for (;; s
.popFront(), count
+= 1 + skipWS
!(Source
, Yes
.doCount
)(s
))
4404 auto key
= parseElement
!(KeyType
, Source
, Yes
.doCount
)(s
);
4405 count
+= key
.count
+ skipWS
!(Source
, Yes
.doCount
)(s
);
4406 parseCheck
!s(keyval
);
4407 count
+= 1 + skipWS
!(Source
, Yes
.doCount
)(s
);
4408 auto val
= parseElement
!(ValType
, Source
, Yes
.doCount
)(s
);
4409 count
+= val
.count
+ skipWS
!(Source
, Yes
.doCount
)(s
);
4410 result
[key
.data
] = val
.data
;
4412 throw convError
!(Source
, Target
)(s
);
4413 if (s
.front
!= comma
)
4416 parseCheck
!s(rbracket
);
4419 return tuple
!("data", "count")(result
, ++count
);
4430 import std
.typecons
: Flag
, Yes
, No
, tuple
;
4431 import std
.range
.primitives
: save
;
4432 import std
.array
: assocArray
;
4433 auto s1
= "[1:10, 2:20, 3:30]";
4434 auto copyS1
= s1
.save
;
4435 auto aa1
= parse
!(int[int])(s1
);
4436 assert(aa1
== [1:10, 2:20, 3:30]);
4437 assert(tuple([1:10, 2:20, 3:30], copyS1
.length
) == parse
!(int[int], string
, Yes
.doCount
)(copyS1
));
4439 auto s2
= `["aaa":10, "bbb":20, "ccc":30]`;
4440 auto copyS2
= s2
.save
;
4441 auto aa2
= parse
!(int[string
])(s2
);
4442 assert(aa2
== ["aaa":10, "bbb":20, "ccc":30]);
4443 assert(tuple(["aaa":10, "bbb":20, "ccc":30], copyS2
.length
) ==
4444 parse
!(int[string
], string
, Yes
.doCount
)(copyS2
));
4446 auto s3
= `["aaa":[1], "bbb":[2,3], "ccc":[4,5,6]]`;
4447 auto copyS3
= s3
.save
;
4448 auto aa3
= parse
!(int[][string
])(s3
);
4449 assert(aa3
== ["aaa":[1], "bbb":[2,3], "ccc":[4,5,6]]);
4450 assert(tuple(["aaa":[1], "bbb":[2,3], "ccc":[4,5,6]], copyS3
.length
) ==
4451 parse
!(int[][string
], string
, Yes
.doCount
)(copyS3
));
4455 assert(tuple(emptyAA
, s4
.length
) == parse
!(int[int], string
, Yes
.doCount
)(s4
));
4460 import std
.exception
;
4462 //Check proper failure
4463 auto s
= "[1:10, 2:20, 3:30]";
4465 foreach (i
; 0 .. s
.length
-1)
4467 auto ss
= s
[0 .. i
];
4468 assertThrown
!ConvException(parse
!(int[int])(ss
));
4469 assertThrown
!ConvException(parse
!(int[int], string
, Yes
.doCount
)(ss
));
4471 int[int] aa
= parse
!(int[int])(s
);
4472 auto aa2
= parse
!(int[int], string
, Yes
.doCount
)(s2
);
4477 private auto parseEscape(Source
, Flag
!"doCount" doCount
= No
.doCount
)(ref Source s
)
4478 if (isInputRange
!Source
&& isSomeChar
!(ElementType
!Source
))
4483 throw parseError("Unterminated escape sequence");
4485 // consumes 1 element from Source
4486 dchar getHexDigit()(ref Source s_
= s
) // workaround
4488 import std
.ascii
: isAlpha
, isHexDigit
;
4490 throw parseError("Unterminated escape sequence");
4493 throw parseError("Unterminated escape sequence");
4496 throw parseError("Hex digit is missing");
4497 return isAlpha(c
) ?
((c
& ~0x20) - ('A' - 10)) : c
- '0';
4500 // We need to do octals separate, because they need a lookahead to find out,
4501 // where the escape sequence ends.
4502 auto first
= s
.front
;
4503 if (first
>= '0' && first
<= '7')
4512 return tuple
!("data", "count")(cast (dchar) (c1
- '0'), count
);
4516 return cast (dchar) (c1
- '0');
4520 if (c2
< '0' || c2
> '7')
4524 return tuple
!("data", "count")(cast (dchar)(c1
- '0'), count
);
4528 return cast (dchar)(c1
- '0');
4534 if (c3
< '0' || c3
> '7')
4538 return tuple
!("data", "count")(cast (dchar) (8 * (c1
- '0') + (c2
- '0')), count
);
4542 return cast (dchar) (8 * (c1
- '0') + (c2
- '0'));
4548 throw parseError("Octal sequence is larger than \\377");
4551 return tuple
!("data", "count")(cast (dchar) (64 * (c1
- '0') + 8 * (c2
- '0') + (c3
- '0')), count
);
4555 return cast (dchar) (64 * (c1
- '0') + 8 * (c2
- '0') + (c3
- '0'));
4563 case '"': result
= '\"'; break;
4564 case '\'': result
= '\''; break;
4565 case '?': result
= '\?'; break;
4566 case '\\': result
= '\\'; break;
4567 case 'a': result
= '\a'; break;
4568 case 'b': result
= '\b'; break;
4569 case 'f': result
= '\f'; break;
4570 case 'n': result
= '\n'; break;
4571 case 'r': result
= '\r'; break;
4572 case 't': result
= '\t'; break;
4573 case 'v': result
= '\v'; break;
4575 result
= getHexDigit() << 4;
4576 result |
= getHexDigit();
4580 result
= getHexDigit() << 12;
4581 result |
= getHexDigit() << 8;
4582 result |
= getHexDigit() << 4;
4583 result |
= getHexDigit();
4587 result
= getHexDigit() << 28;
4588 result |
= getHexDigit() << 24;
4589 result |
= getHexDigit() << 20;
4590 result |
= getHexDigit() << 16;
4591 result |
= getHexDigit() << 12;
4592 result |
= getHexDigit() << 8;
4593 result |
= getHexDigit() << 4;
4594 result |
= getHexDigit();
4598 throw parseError("Unknown escape character " ~ to
!string(s
.front
));
4601 throw parseError("Unterminated escape sequence");
4607 return tuple
!("data", "count")(cast (dchar) result
, ++count
);
4611 return cast (dchar) result
;
4618 `\"`, `\'`, `\?`, `\\`, `\a`, `\b`, `\f`, `\n`, `\r`, `\t`, `\v`, //Normal escapes
4621 `\u65E5`, `\U00012456`,
4622 // https://issues.dlang.org/show_bug.cgi?id=9621 (Named Character Entities)
4623 //`\&`, `\"`,
4625 string
[] copyS1
= s1
~ s1
[0 .. 0];
4627 const(dchar)[] s2
= [
4628 '\"', '\'', '\?', '\\', '\a', '\b', '\f', '\n', '\r', '\t', '\v', //Normal escapes
4631 '\u65E5', '\U00012456',
4632 // https://issues.dlang.org/show_bug.cgi?id=9621 (Named Character Entities)
4633 //'\&', '\"',
4636 foreach (i
; 0 .. s1
.length
)
4638 assert(s2
[i
] == parseEscape(s1
[i
]));
4639 assert(s1
[i
].empty
);
4641 assert(tuple(s2
[i
], copyS1
[i
].length
) == parseEscape
!(string
, Yes
.doCount
)(copyS1
[i
]));
4642 assert(copyS1
[i
].empty
);
4648 import std
.exception
;
4651 `hello!`, //Not an escape
4652 `\`, //Premature termination
4653 `\/`, //Not an escape
4654 `\gggg`, //Not an escape
4655 `\xzz`, //Not an hex
4656 `\x0`, //Premature hex end
4657 `\XB9`, //Not legal hex syntax
4658 `\u!!`, //Not a unicode hex
4659 `\777`, //Octal is larger than a byte
4660 `\80`, //Wrong digit at beginning of octal
4661 `\u123`, //Premature hex end
4662 `\U123123` //Premature hex end
4666 assertThrown
!ConvException(parseEscape(s
));
4667 assertThrown
!ConvException(parseEscape
!(string
, Yes
.doCount
)(s
));
4672 auto parseElement(Target
, Source
, Flag
!"doCount" doCount
= No
.doCount
)(ref Source s
)
4673 if (isInputRange
!Source
&& isSomeChar
!(ElementType
!Source
) && !is(Source
== enum) &&
4674 isExactSomeString
!Target
)
4676 import std
.array
: appender
;
4677 auto result
= appender
!Target();
4679 // parse array of chars
4681 throw convError
!(Source
, Target
)(s
);
4684 return parse
!(Target
, Source
, doCount
)(s
);
4690 throw convError
!(Source
, Target
)(s
);
4691 if (s
.front
== '\"')
4696 return tuple
!("data", "count")(result
.data
, ++count
);
4707 throw parseError("Unterminated quoted string");
4714 return tuple
!("data", "count")(result
.data
, ++count
);
4721 auto r
= parseEscape
!(typeof(s
), Yes
.doCount
)(s
);
4726 result
.put(s
.front
);
4732 assert(false, "Unexpected fallthrough");
4736 auto parseElement(Target
, Source
, Flag
!"doCount" doCount
= No
.doCount
)(ref Source s
)
4737 if (isInputRange
!Source
&& isSomeChar
!(ElementType
!Source
) && !is(Source
== enum) &&
4738 is(CharTypeOf
!Target
== dchar) && !is(Target
== enum))
4745 throw convError
!(Source
, Target
)(s
);
4746 ++count
; // for the following if-else sequence
4747 if (s
.front
!= '\\')
4757 return tuple
!("data", "count")(c
, ++count
);
4766 auto parseElement(Target
, Source
, Flag
!"doCount" doCount
= No
.doCount
)(ref Source s
)
4767 if (isInputRange
!Source
&& isSomeChar
!(ElementType
!Source
) &&
4768 !isSomeString
!Target
&& !isSomeChar
!Target
)
4770 return parse
!(Target
, Source
, doCount
)(s
);
4773 // Use this when parsing a type that will ultimately be appended to a
4775 package template WideElementType(T
)
4777 alias E
= ElementType
!T
;
4778 static if (isSomeChar
!E
)
4779 alias WideElementType
= dchar;
4781 alias WideElementType
= E
;
4785 /***************************************************************
4786 * Convenience functions for converting one or more arguments
4787 * of any type into _text (the three character widths).
4789 string
text(T
...)(T args
)
4790 if (T
.length
> 0) { return textImpl
!string(args
); }
4793 wstring
wtext(T
...)(T args
)
4794 if (T
.length
> 0) { return textImpl
!wstring(args
); }
4797 dstring
dtext(T
...)(T args
)
4798 if (T
.length
> 0) { return textImpl
!dstring(args
); }
4803 assert( text(42, ' ', 1.5, ": xyz") == "42 1.5: xyz"c
);
4804 assert(wtext(42, ' ', 1.5, ": xyz") == "42 1.5: xyz"w
);
4805 assert(dtext(42, ' ', 1.5, ": xyz") == "42 1.5: xyz"d
);
4814 assert( text(c
, "ello", ' ', w
, "好 ", d
, "ው ሰላም ነው") == "hello 你好 እው ሰላም ነው"c
);
4815 assert(wtext(c
, "ello", ' ', w
, "好 ", d
, "ው ሰላም ነው") == "hello 你好 እው ሰላም ነው"w
);
4816 assert(dtext(c
, "ello", ' ', w
, "好 ", d
, "ው ሰላም ነው") == "hello 你好 እው ሰላም ነው"d
);
4819 wstring ws
= "여보세요";
4820 dstring
ds = "Здравствуйте";
4822 assert( text(cs
, ' ', ws
, " ", ds) == "今日は 여보세요 Здравствуйте"c
);
4823 assert(wtext(cs
, ' ', ws
, " ", ds) == "今日は 여보세요 Здравствуйте"w
);
4824 assert(dtext(cs
, ' ', ws
, " ", ds) == "今日は 여보세요 Здравствуйте"d
);
4827 private S
textImpl(S
, U
...)(U args
)
4829 static if (U
.length
== 0)
4833 else static if (U
.length
== 1)
4835 return to
!S(args
[0]);
4839 import std
.array
: appender
;
4840 import std
.traits
: isSomeChar
, isSomeString
;
4842 auto app
= appender
!S();
4844 // assume that on average, parameters will have less
4846 app
.reserve(U
.length
* 20);
4847 // Must be static foreach because of https://issues.dlang.org/show_bug.cgi?id=21209
4848 static foreach (arg
; args
)
4851 isSomeChar
!(typeof(arg
))
4852 || isSomeString
!(typeof(arg
))
4853 ||
( isInputRange
!(typeof(arg
)) && isSomeChar
!(ElementType
!(typeof(arg
))) )
4858 is(immutable typeof(arg
) == immutable uint) ||
is(immutable typeof(arg
) == immutable ulong) ||
4859 is(immutable typeof(arg
) == immutable int) ||
is(immutable typeof(arg
) == immutable long)
4861 // https://issues.dlang.org/show_bug.cgi?id=17712#c15
4862 app
.put(textImpl
!(S
)(arg
));
4872 /***************************************************************
4873 The `octal` facility provides a means to declare a number in base 8.
4874 Using `octal!177` or `octal!"177"` for 127 represented in octal
4875 (same as 0177 in C).
4877 The rules for strings are the usual for literals: If it can fit in an
4878 `int`, it is an `int`. Otherwise, it is a `long`. But, if the
4879 user specifically asks for a `long` with the `L` suffix, always
4880 give the `long`. Give an unsigned iff it is asked for with the $(D
4881 U) or `u` suffix. _Octals created from integers preserve the type
4882 of the passed-in integral.
4885 $(LREF parse) for parsing octal strings at runtime.
4887 template octal(string num
)
4888 if (isOctalLiteral(num
))
4890 static if ((octalFitsInInt
!num
&& !literalIsLong
!num
) && !literalIsUnsigned
!num
)
4891 enum octal
= octal
!int(num
);
4892 else static if ((!octalFitsInInt
!num || literalIsLong
!num
) && !literalIsUnsigned
!num
)
4893 enum octal
= octal
!long(num
);
4894 else static if ((octalFitsInInt
!num
&& !literalIsLong
!num
) && literalIsUnsigned
!num
)
4895 enum octal
= octal
!uint(num
);
4896 else static if ((!octalFitsInInt
!(num
) || literalIsLong
!(num
)) && literalIsUnsigned
!(num
))
4897 enum octal
= octal
!ulong(num
);
4899 static assert(false, "Unusable input " ~ num
);
4903 template octal(alias decimalInteger
)
4904 if (is(typeof(decimalInteger
)) && isIntegral
!(typeof(decimalInteger
)))
4906 enum octal
= convertToOctal(decimalInteger
);
4914 // octal is a compile-time device
4916 // Create an unsigned octal
4917 auto c
= octal
!"1_000_000u";
4918 // Leading zeros are allowed when converting from a string
4919 auto d
= octal
!"0001_200_000";
4922 /*************************************
4923 * Convert a decimal integer to an octal integer with the same digits.
4925 * i = integer to convert
4927 * octal integer with the same type and same digits
4929 private T
convertToOctal(T
)(T i
)
4931 assert((i
% 10) < 8);
4932 return i ?
convertToOctal(i
/ 10) * 8 + i
% 10 : 0;
4936 Takes a string, num, which is an octal literal, and returns its
4937 value, in the type T specified.
4939 private T
octal(T
)(const string num
)
4941 assert(isOctalLiteral(num
), num
~ " is not an octal literal");
4945 foreach (const char s
; num
)
4947 if (s
< '0' || s
> '7') // we only care about digits; skip the rest
4948 // safe to skip - this is checked out in the assert so these
4949 // are just suffixes
4961 int a
= octal
!int("10");
4964 int b
= octal
!int("000137");
4969 Take a look at int.max and int.max+1 in octal and the logic for this
4970 function follows directly.
4972 private template octalFitsInInt(string octalNum
)
4974 // note it is important to strip the literal of all
4975 // non-numbers. kill the suffix and underscores lest they mess up
4976 // the number of digits here that we depend on.
4977 enum bool octalFitsInInt
= strippedOctalLiteral(octalNum
).length
< 11 ||
4978 strippedOctalLiteral(octalNum
).length
== 11 &&
4979 strippedOctalLiteral(octalNum
)[0] == '1';
4982 private string
strippedOctalLiteral(string original
)
4984 string stripped
= "";
4985 bool leading_zeros
= true;
4986 foreach (c
; original
)
4988 if (!('0' <= c
&& c
<= '7'))
4997 leading_zeros
= false;
5001 if (stripped
.length
== 0)
5003 assert(leading_zeros
);
5011 static assert(strippedOctalLiteral("7") == "7");
5012 static assert(strippedOctalLiteral("123") == "123");
5013 static assert(strippedOctalLiteral("00123") == "123");
5014 static assert(strippedOctalLiteral("01230") == "1230");
5015 static assert(strippedOctalLiteral("0") == "0");
5016 static assert(strippedOctalLiteral("00_000") == "0");
5017 static assert(strippedOctalLiteral("000_000_12_300") == "12300");
5020 private template literalIsLong(string num
)
5022 static if (num
.length
> 1)
5023 // can be xxL or xxLu according to spec
5024 enum literalIsLong
= (num
[$-1] == 'L' || num
[$-2] == 'L');
5026 enum literalIsLong
= false;
5029 private template literalIsUnsigned(string num
)
5031 static if (num
.length
> 1)
5032 // can be xxU or xxUL according to spec
5033 enum literalIsUnsigned
= (num
[$-1] == 'u' || num
[$-2] == 'u')
5034 // both cases are allowed too
5035 ||
(num
[$-1] == 'U' || num
[$-2] == 'U');
5037 enum literalIsUnsigned
= false;
5041 Returns if the given string is a correctly formatted octal literal.
5043 The format is specified in spec/lex.html. The leading zeros are allowed,
5046 @safe pure nothrow @nogc
5047 private bool isOctalLiteral(const string num
)
5049 if (num
.length
== 0)
5052 // Must start with a digit.
5053 if (num
[0] < '0' || num
[0] > '7')
5058 if (('0' <= c
&& c
<= '7') || c
== '_') // a legal character
5061 if (i
< num
.length
- 2)
5064 // gotta check for those suffixes
5065 if (c
!= 'U' && c
!= 'u' && c
!= 'L')
5067 if (i
!= num
.length
- 1)
5069 // if we're not the last one, the next one must
5070 // also be a suffix to be valid
5072 if (c2
!= 'U' && c2
!= 'u' && c2
!= 'L')
5073 return false; // spam at the end of the string
5075 return false; // repeats are disallowed
5084 // ensure that you get the right types, even with embedded underscores
5085 auto w
= octal
!"100_000_000_000";
5086 static assert(!is(typeof(w
) == int));
5087 auto w2
= octal
!"1_000_000_000";
5088 static assert(is(typeof(w2
) == int));
5090 static assert(octal
!"45" == 37);
5091 static assert(octal
!"0" == 0);
5092 static assert(octal
!"7" == 7);
5093 static assert(octal
!"10" == 8);
5094 static assert(octal
!"666" == 438);
5095 static assert(octal
!"0004001" == 2049);
5096 static assert(octal
!"00" == 0);
5097 static assert(octal
!"0_0" == 0);
5099 static assert(octal
!45 == 37);
5100 static assert(octal
!0 == 0);
5101 static assert(octal
!7 == 7);
5102 static assert(octal
!10 == 8);
5103 static assert(octal
!666 == 438);
5105 static assert(octal
!"66_6" == 438);
5106 static assert(octal
!"0_0_66_6" == 438);
5108 static assert(octal
!2520046213 == 356535435);
5109 static assert(octal
!"2520046213" == 356535435);
5111 static assert(octal
!17777777777 == int.max
);
5113 static assert(!__traits(compiles
, octal
!823));
5115 static assert(!__traits(compiles
, octal
!"823"));
5117 static assert(!__traits(compiles
, octal
!"_823"));
5118 static assert(!__traits(compiles
, octal
!"spam"));
5119 static assert(!__traits(compiles
, octal
!"77%"));
5121 static assert(is(typeof(octal
!"17777777777") == int));
5122 static assert(octal
!"17777777777" == int.max
);
5124 static assert(is(typeof(octal
!"20000000000U") == ulong)); // Shouldn't this be uint?
5125 static assert(octal
!"20000000000" == uint(int.max
) + 1);
5127 static assert(is(typeof(octal
!"777777777777777777777") == long));
5128 static assert(octal
!"777777777777777777777" == long.max
);
5130 static assert(is(typeof(octal
!"1000000000000000000000U") == ulong));
5131 static assert(octal
!"1000000000000000000000" == ulong(long.max
) + 1);
5136 // biggest value that should fit in an it
5137 a
= octal
!"17777777777";
5138 assert(a
== int.max
);
5139 // should not fit in the int
5140 static assert(!__traits(compiles
, a
= octal
!"20000000000"));
5141 // ... but should fit in a long
5142 b
= octal
!"20000000000";
5143 assert(b
== 1L + int.max
);
5151 // emplace() used to be here but was moved to druntime
5152 public import core
.lifetime
: emplace
;
5154 // https://issues.dlang.org/show_bug.cgi?id=9559
5157 import std
.algorithm
.iteration
: map
;
5158 import std
.array
: array
;
5159 import std
.typecons
: Nullable
;
5160 alias I
= Nullable
!int;
5161 auto ints
= [0, 1, 2].map
!(i
=> i
& 1 ? I
.init
: I(i
))();
5162 auto asArray
= array(ints
);
5165 @system unittest //http://forum.dlang.org/post/nxbdgtdlmwscocbiypjs@forum.dlang.org
5167 import std
.array
: array
;
5168 import std
.datetime
: SysTime
, UTC
;
5169 import std
.math
.traits
: isNaN
;
5181 assert(a
.i
.isNaN(), "why is 'j' zero?? and i is not NaN?");
5183 assert(!a
.i
.isNaN());
5185 SysTime when
; // comment this line avoid the breakage
5191 assert(&b1
); // verify that default eyes invariants are ok;
5193 auto b2
= B(SysTime(0, UTC()), 1, A(1));
5195 auto b3
= B(SysTime(0, UTC()), 1, A(1));
5198 auto arr
= [b2
, b3
];
5200 assert(arr
[0].j
== 1);
5201 assert(arr
[1].j
== 1);
5202 auto a2
= arr
.array(); // << bang, invariant is raised, also if b2 and b3 are good
5207 import std
.algorithm
.comparison
: equal
;
5208 import std
.algorithm
.iteration
: map
;
5209 // Check fix for https://issues.dlang.org/show_bug.cgi?id=2971
5210 assert(equal(map
!(to
!int)(["42", "34", "345"]), [42, 34, 345]));
5213 // Undocumented for the time being
5214 void toTextRange(T
, W
)(T value
, W writer
)
5215 if (isIntegral
!T
&& isOutputRange
!(W
, char))
5217 import core
.internal
.string
: SignedStringBuf
, signedToTempString
,
5218 UnsignedStringBuf
, unsignedToTempString
;
5222 SignedStringBuf buf
= void;
5223 put(writer
, signedToTempString(value
, buf
));
5227 UnsignedStringBuf buf
= void;
5228 put(writer
, unsignedToTempString(value
, buf
));
5234 import std
.array
: appender
;
5235 auto result
= appender
!(char[])();
5236 toTextRange(-1, result
);
5237 assert(result
.data
== "-1");
5242 Returns the corresponding _unsigned value for `x` (e.g. if `x` has type
5243 `int`, it returns $(D cast(uint) x)). The advantage compared to the cast
5244 is that you do not need to rewrite the cast if `x` later changes type
5245 (e.g from `int` to `long`).
5247 Note that the result is always mutable even if the original type was const
5248 or immutable. In order to retain the constness, use $(REF Unsigned, std,traits).
5250 auto unsigned(T
)(T x
)
5253 return cast(Unqual
!(Unsigned
!T
))x
;
5259 import std
.traits
: Unsigned
;
5260 immutable int s
= 42;
5261 auto u1
= unsigned(s
); //not qualified
5262 static assert(is(typeof(u1
) == uint));
5263 Unsigned
!(typeof(s
)) u2
= unsigned(s
); //same qualification
5264 static assert(is(typeof(u2
) == immutable uint));
5265 immutable u3
= unsigned(s
); //explicitly qualified
5269 auto unsigned(T
)(T x
)
5272 // All characters are unsigned
5273 static assert(T
.min
== 0, T
.stringof
~ ".min must be zero");
5274 return cast(Unqual
!T
) x
;
5279 static foreach (T
; AliasSeq
!(byte, ubyte))
5281 static assert(is(typeof(unsigned(cast(T
) 1)) == ubyte));
5282 static assert(is(typeof(unsigned(cast(const T
) 1)) == ubyte));
5283 static assert(is(typeof(unsigned(cast(immutable T
) 1)) == ubyte));
5286 static foreach (T
; AliasSeq
!(short, ushort))
5288 static assert(is(typeof(unsigned(cast(T
) 1)) == ushort));
5289 static assert(is(typeof(unsigned(cast(const T
) 1)) == ushort));
5290 static assert(is(typeof(unsigned(cast(immutable T
) 1)) == ushort));
5293 static foreach (T
; AliasSeq
!(int, uint))
5295 static assert(is(typeof(unsigned(cast(T
) 1)) == uint));
5296 static assert(is(typeof(unsigned(cast(const T
) 1)) == uint));
5297 static assert(is(typeof(unsigned(cast(immutable T
) 1)) == uint));
5300 static foreach (T
; AliasSeq
!(long, ulong))
5302 static assert(is(typeof(unsigned(cast(T
) 1)) == ulong));
5303 static assert(is(typeof(unsigned(cast(const T
) 1)) == ulong));
5304 static assert(is(typeof(unsigned(cast(immutable T
) 1)) == ulong));
5310 static foreach (T
; AliasSeq
!(char, wchar, dchar))
5312 static assert(is(typeof(unsigned(cast(T
)'A')) == T
));
5313 static assert(is(typeof(unsigned(cast(const T
)'A')) == T
));
5314 static assert(is(typeof(unsigned(cast(immutable T
)'A')) == T
));
5320 Returns the corresponding _signed value for `x` (e.g. if `x` has type
5321 `uint`, it returns $(D cast(int) x)). The advantage compared to the cast
5322 is that you do not need to rewrite the cast if `x` later changes type
5323 (e.g from `uint` to `ulong`).
5325 Note that the result is always mutable even if the original type was const
5326 or immutable. In order to retain the constness, use $(REF Signed, std,traits).
5331 return cast(Unqual
!(Signed
!T
))x
;
5337 import std
.traits
: Signed
;
5339 immutable uint u
= 42;
5340 auto s1
= signed(u
); //not qualified
5341 static assert(is(typeof(s1
) == int));
5342 Signed
!(typeof(u
)) s2
= signed(u
); //same qualification
5343 static assert(is(typeof(s2
) == immutable int));
5344 immutable s3
= signed(u
); //explicitly qualified
5349 static foreach (T
; AliasSeq
!(byte, ubyte))
5351 static assert(is(typeof(signed(cast(T
) 1)) == byte));
5352 static assert(is(typeof(signed(cast(const T
) 1)) == byte));
5353 static assert(is(typeof(signed(cast(immutable T
) 1)) == byte));
5356 static foreach (T
; AliasSeq
!(short, ushort))
5358 static assert(is(typeof(signed(cast(T
) 1)) == short));
5359 static assert(is(typeof(signed(cast(const T
) 1)) == short));
5360 static assert(is(typeof(signed(cast(immutable T
) 1)) == short));
5363 static foreach (T
; AliasSeq
!(int, uint))
5365 static assert(is(typeof(signed(cast(T
) 1)) == int));
5366 static assert(is(typeof(signed(cast(const T
) 1)) == int));
5367 static assert(is(typeof(signed(cast(immutable T
) 1)) == int));
5370 static foreach (T
; AliasSeq
!(long, ulong))
5372 static assert(is(typeof(signed(cast(T
) 1)) == long));
5373 static assert(is(typeof(signed(cast(const T
) 1)) == long));
5374 static assert(is(typeof(signed(cast(immutable T
) 1)) == long));
5378 // https://issues.dlang.org/show_bug.cgi?id=10874
5388 Returns the representation of an enumerated value, i.e. the value converted to
5389 the base type of the enumeration.
5391 OriginalType
!E
asOriginalType(E
)(E value
)
5401 static assert(is(typeof(A
.a
.asOriginalType
) == int));
5402 assert(A
.a
.asOriginalType
== 42);
5403 enum B
: double { a
= 43 }
5404 static assert(is(typeof(B
.a
.asOriginalType
) == double));
5405 assert(B
.a
.asOriginalType
== 43);
5409 A wrapper on top of the built-in cast operator that allows one to restrict
5410 casting of the original type of the value.
5412 A common issue with using a raw cast is that it may silently continue to
5413 compile even if the value's type has changed during refactoring,
5414 which breaks the initial assumption about the cast.
5417 From = The type to cast from. The programmer must ensure it is legal
5420 template castFrom(From
)
5424 To = The type _to cast _to.
5425 value = The value _to cast. It must be of type `From`,
5426 otherwise a compile-time error is emitted.
5429 the value after the cast, returned by reference if possible.
5431 auto ref to(To
, T
)(auto ref T value
) @system
5435 "the value to cast is not of specified type '" ~ From
.stringof
~
5436 "', it is of type '" ~ T
.stringof
~ "'"
5440 is(typeof(cast(To
) value
)),
5441 "can't cast from '" ~ From
.stringof
~ "' to '" ~ To
.stringof
~ "'"
5444 return cast(To
) value
;
5451 // Regular cast, which has been verified to be legal by the programmer:
5454 auto y
= cast(int) x
;
5457 // However this will still compile if 'x' is changed to be a pointer:
5460 auto y
= cast(int) x
;
5463 // castFrom provides a more reliable alternative to casting:
5466 auto y
= castFrom
!long.to
!int(x
);
5469 // Changing the type of 'x' will now issue a compiler error,
5470 // allowing bad casts to be caught before it's too late:
5474 !__traits(compiles
, castFrom
!long.to
!int(x
))
5477 // if cast is still needed, must be changed to:
5478 auto y
= castFrom
!(long*).to
!int(x
);
5482 // https://issues.dlang.org/show_bug.cgi?id=16667
5485 ubyte[] a
= ['a', 'b', 'c'];
5486 assert(castFrom
!(ubyte[]).to
!(string
)(a
) == "abc");
5490 Check the correctness of a string for `hexString`.
5491 The result is true if and only if the input string is composed of whitespace
5492 characters (\f\n\r\t\v lineSep paraSep nelSep) and
5493 an even number of hexadecimal digits (regardless of the case).
5496 private bool isHexLiteral(String
)(scope const String hexData
)
5498 import std
.ascii
: isHexDigit
;
5499 import std
.uni
: lineSep
, paraSep
, nelSep
;
5501 foreach (const dchar c
; hexData
)
5529 // test all the hex digits
5530 static assert( ("0123456789abcdefABCDEF").isHexLiteral
);
5531 // empty or white strings are not valid
5532 static assert( "\r\n\t".isHexLiteral
);
5533 // but are accepted if the count of hex digits is even
5534 static assert( "A\r\n\tB".isHexLiteral
);
5541 static assert( "".isHexLiteral
);
5542 static assert( " \r".isHexLiteral
);
5543 static assert( whitespace
.isHexLiteral
);
5544 static assert( ""w
.isHexLiteral
);
5545 static assert( " \r"w
.isHexLiteral
);
5546 static assert( ""d
.isHexLiteral
);
5547 static assert( " \r"d
.isHexLiteral
);
5548 static assert( "\u2028\u2029\u0085"d
.isHexLiteral
);
5550 static assert( !("5" ~ whitespace
).isHexLiteral
);
5551 static assert( !"123".isHexLiteral
);
5552 static assert( !"1A3".isHexLiteral
);
5553 static assert( !"1 23".isHexLiteral
);
5554 static assert( !"\r\n\tC".isHexLiteral
);
5555 static assert( !"123"w
.isHexLiteral
);
5556 static assert( !"1A3"w
.isHexLiteral
);
5557 static assert( !"1 23"w
.isHexLiteral
);
5558 static assert( !"\r\n\tC"w
.isHexLiteral
);
5559 static assert( !"123"d
.isHexLiteral
);
5560 static assert( !"1A3"d
.isHexLiteral
);
5561 static assert( !"1 23"d
.isHexLiteral
);
5562 static assert( !"\r\n\tC"d
.isHexLiteral
);
5563 // even x strings with invalid charset
5564 static assert( !"12gG".isHexLiteral
);
5565 static assert( !"2A 3q".isHexLiteral
);
5566 static assert( !"12gG"w
.isHexLiteral
);
5567 static assert( !"2A 3q"w
.isHexLiteral
);
5568 static assert( !"12gG"d
.isHexLiteral
);
5569 static assert( !"2A 3q"d
.isHexLiteral
);
5571 static assert( ("5A" ~ whitespace
).isHexLiteral
);
5572 static assert( ("5A 01A C FF de 1b").isHexLiteral
);
5573 static assert( ("0123456789abcdefABCDEF").isHexLiteral
);
5574 static assert( (" 012 34 5 6789 abcd ef\rAB\nCDEF").isHexLiteral
);
5575 static assert( ("5A 01A C FF de 1b"w
).isHexLiteral
);
5576 static assert( ("0123456789abcdefABCDEF"w
).isHexLiteral
);
5577 static assert( (" 012 34 5 6789 abcd ef\rAB\nCDEF"w
).isHexLiteral
);
5578 static assert( ("5A 01A C FF de 1b"d
).isHexLiteral
);
5579 static assert( ("0123456789abcdefABCDEF"d
).isHexLiteral
);
5580 static assert( (" 012 34 5 6789 abcd ef\rAB\nCDEF"d
).isHexLiteral
);
5581 // library version allows what's pointed by https://issues.dlang.org/show_bug.cgi?id=10454
5582 static assert( ("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").isHexLiteral
);
5586 Converts a hex literal to a string at compile time.
5588 Takes a string made of hexadecimal digits and returns
5589 the matching string by converting each pair of digits to a character.
5590 The input string can also include white characters, which can be used
5591 to keep the literal string readable in the source code.
5593 The function is intended to replace the hexadecimal literal strings
5594 starting with `'x'`, which could be removed to simplify the core language.
5597 hexData = string to be converted.
5600 a `string`, a `wstring` or a `dstring`, according to the type of hexData.
5602 template hexString(string hexData
)
5603 if (hexData
.isHexLiteral
)
5605 enum hexString
= mixin(hexToString(hexData
));
5609 template hexString(wstring hexData
)
5610 if (hexData
.isHexLiteral
)
5612 enum wstring hexString
= mixin(hexToString(hexData
));
5616 template hexString(dstring hexData
)
5617 if (hexData
.isHexLiteral
)
5619 enum dstring hexString
= mixin(hexToString(hexData
));
5625 // conversion at compile time
5626 auto string1
= hexString
!"304A314B";
5627 assert(string1
== "0J1K");
5628 auto string2
= hexString
!"304A314B"w
;
5629 assert(string2
== "0J1K"w
);
5630 auto string3
= hexString
!"304A314B"d
;
5631 assert(string3
== "0J1K"d
);
5634 @safe nothrow pure private
5636 /* These are meant to be used with CTFE.
5637 * They cause the instantiations of hexStrLiteral()
5638 * to be in Phobos, not user code.
5640 string
hexToString(string s
)
5642 return hexStrLiteral(s
);
5645 wstring
hexToString(wstring s
)
5647 return hexStrLiteral(s
);
5650 dstring
hexToString(dstring s
)
5652 return hexStrLiteral(s
);
5657 Turn a hexadecimal string into a regular string literal.
5658 I.e. "dead beef" is transformed into "\xde\xad\xbe\xef"
5659 suitable for use in a mixin.
5661 hexData is string, wstring, or dstring and validated by isHexLiteral()
5663 @trusted nothrow pure
5664 private auto hexStrLiteral(String
)(scope String hexData
)
5666 import std
.ascii
: isHexDigit
;
5667 alias C
= Unqual
!(ElementEncodingType
!String
); // char, wchar or dchar
5669 result
.length
= 1 + hexData
.length
* 2 + 1; // don't forget the " "
5670 /* Use a pointer because we know it won't overrun,
5671 * and this will reduce the size of the function substantially
5672 * by not doing the array bounds checks.
5673 * This is why this function is @trusted.
5675 auto r
= result
.ptr
;
5678 foreach (c
; hexData
)
5685 r
[1 + cnt
+ 1] = 'x';
5693 result
.length
= 1 + cnt
+ 1; // trim off any excess length
5701 assert(hexString
!"46 47 48 49 4A 4B" == "FGHIJK");
5702 assert(hexString
!"30\r\n\t\f\v31 32 33 32 31 30" == "0123210");
5703 assert(hexString
!"ab cd" == hexString
!"ABCD");
5708 * Convert integer to a range of characters.
5709 * Intended to be lightweight and fast.
5712 * radix = 2, 8, 10, 16
5713 * Char = character type for output
5714 * letterCase = lower for deadbeef, upper for DEADBEEF
5715 * value = integer to convert. Can be ubyte, ushort, uint or ulong. If radix
5716 * is 10, can also be byte, short, int or long.
5718 * Random access range with slicing and everything
5721 auto toChars(ubyte radix
= 10, Char
= char, LetterCase letterCase
= LetterCase
.lower
, T
)(T value
)
5722 pure nothrow @nogc @safe
5723 if ((radix
== 2 || radix
== 8 || radix
== 10 || radix
== 16) &&
5724 isIntegral
!T
&& (radix
== 10 || isUnsigned
!T
))
5726 alias UT
= Unqual
!T
;
5728 static if (radix
== 10)
5730 /* uint.max is 42_9496_7295
5731 * int.max is 21_4748_3647
5732 * ulong.max is 1844_6744_0737_0955_1615
5733 * long.max is 922_3372_0368_5477_5807
5735 static struct Result
5737 void initialize(UT value
)
5739 import core
.internal
.string
: signedToTempString
, unsignedToTempString
;
5741 char[] t
= value
< 0
5742 ? signedToTempString
!(10, false, char)(value
, buf
)
5743 : unsignedToTempString
!(10, false, char)(value
, buf
);
5745 lwr
= cast(uint) (buf
.length
- t
.length
);
5746 upr
= cast(uint) buf
.length
;
5749 @property size_t
length() { return upr
- lwr
; }
5751 alias opDollar
= length
;
5753 @property bool empty() { return upr
== lwr
; }
5755 @property Char
front() { return buf
[lwr
]; }
5757 void popFront() { ++lwr
; }
5759 @property Char
back() { return buf
[upr
- 1]; }
5761 void popBack() { --upr
; }
5763 @property Result
save() { return this; }
5765 Char
opIndex(size_t i
) { return buf
[lwr
+ i
]; }
5767 Result
opSlice(size_t lwr
, size_t upr
)
5769 Result result
= void;
5771 result
.lwr
= cast(uint)(this.lwr
+ lwr
);
5772 result
.upr
= cast(uint)(this.lwr
+ upr
);
5777 uint lwr
= void, upr
= void;
5778 char[(UT
.sizeof
== 4) ?
10 + isSigned
!T
: 20] buf
= void;
5782 result
.initialize(value
);
5787 static if (radix
== 2)
5789 else static if (radix
== 8)
5791 else static if (radix
== 16)
5794 static assert(false, "radix must be 2, 8, 10, or 16");
5795 static struct Result
5802 while (value
>>>= SHIFT
)
5807 @property size_t
length() { return len
; }
5809 @property bool empty() { return len
== 0; }
5811 @property Char
front() { return opIndex(0); }
5813 void popFront() { --len
; }
5815 @property Char
back() { return opIndex(len
- 1); }
5823 @property Result
save() { return this; }
5825 Char
opIndex(size_t i
)
5827 Char c
= (value
>>> ((len
- i
- 1) * SHIFT
)) & ((1 << SHIFT
) - 1);
5828 return cast(Char
)((radix
< 10 || c
< 10) ? c
+ '0'
5829 : (letterCase
== LetterCase
.upper ? c
+ 'A' - 10
5833 Result
opSlice(size_t lwr
, size_t upr
)
5835 Result result
= void;
5836 result
.value
= value
>>> ((len
- upr
) * SHIFT
);
5837 result
.len
= cast(ubyte)(upr
- lwr
);
5846 return Result(value
);
5853 import std
.algorithm
.comparison
: equal
;
5855 assert(toChars(1).equal("1"));
5856 assert(toChars(1_000_000).equal("1000000"));
5858 assert(toChars
!(2)(2U).equal("10"));
5859 assert(toChars
!(16)(255U).equal("ff"));
5860 assert(toChars
!(16, char, LetterCase
.upper
)(255U).equal("FF"));
5869 assert(toChars(123) == toChars(123));
5872 assert(toChars
!2(ubyte(0)).array
== "0");
5873 assert(toChars
!2(ushort(0)).array
== "0");
5874 assert(toChars
!2(0u).array
== "0");
5875 assert(toChars
!2(0Lu).array
== "0");
5876 assert(toChars
!2(ubyte(1)).array
== "1");
5877 assert(toChars
!2(ushort(1)).array
== "1");
5878 assert(toChars
!2(1u).array
== "1");
5879 assert(toChars
!2(1Lu).array
== "1");
5881 auto r
= toChars
!2(2u);
5882 assert(r
.length
== 2);
5883 assert(r
[0] == '1');
5884 assert(r
[1 .. 2].array
== "0");
5886 assert(r
.array
== "10");
5887 assert(s
.retro
.array
== "01");
5890 assert(toChars
!8(ubyte(0)).array
== "0");
5891 assert(toChars
!8(ushort(0)).array
== "0");
5892 assert(toChars
!8(0u).array
== "0");
5893 assert(toChars
!8(0Lu).array
== "0");
5894 assert(toChars
!8(1u).array
== "1");
5895 assert(toChars
!8(1234567Lu).array
== "4553207");
5896 assert(toChars
!8(ubyte.max
).array
== "377");
5897 assert(toChars
!8(ushort.max
).array
== "177777");
5899 auto r
= toChars
!8(8u);
5900 assert(r
.length
== 2);
5901 assert(r
[0] == '1');
5902 assert(r
[1 .. 2].array
== "0");
5904 assert(r
.array
== "10");
5905 assert(s
.retro
.array
== "01");
5908 assert(toChars
!10(ubyte(0)).array
== "0");
5909 assert(toChars
!10(ushort(0)).array
== "0");
5910 assert(toChars
!10(0u).array
== "0");
5911 assert(toChars
!10(0Lu).array
== "0");
5912 assert(toChars
!10(1u).array
== "1");
5913 assert(toChars
!10(1234567Lu).array
== "1234567");
5914 assert(toChars
!10(ubyte.max
).array
== "255");
5915 assert(toChars
!10(ushort.max
).array
== "65535");
5916 assert(toChars
!10(uint.max
).array
== "4294967295");
5917 assert(toChars
!10(ulong.max
).array
== "18446744073709551615");
5919 auto r
= toChars(10u);
5920 assert(r
.length
== 2);
5921 assert(r
[0] == '1');
5922 assert(r
[1 .. 2].array
== "0");
5924 assert(r
.array
== "10");
5925 assert(s
.retro
.array
== "01");
5928 assert(toChars
!10(0).array
== "0");
5929 assert(toChars
!10(0L).array
== "0");
5930 assert(toChars
!10(1).array
== "1");
5931 assert(toChars
!10(1234567L).array
== "1234567");
5932 assert(toChars
!10(byte.max
).array
== "127");
5933 assert(toChars
!10(short.max
).array
== "32767");
5934 assert(toChars
!10(int.max
).array
== "2147483647");
5935 assert(toChars
!10(long.max
).array
== "9223372036854775807");
5936 assert(toChars
!10(-byte.max
).array
== "-127");
5937 assert(toChars
!10(-short.max
).array
== "-32767");
5938 assert(toChars
!10(-int.max
).array
== "-2147483647");
5939 assert(toChars
!10(-long.max
).array
== "-9223372036854775807");
5940 assert(toChars
!10(byte.min
).array
== "-128");
5941 assert(toChars
!10(short.min
).array
== "-32768");
5942 assert(toChars
!10(int.min
).array
== "-2147483648");
5943 assert(toChars
!10(long.min
).array
== "-9223372036854775808");
5945 auto r
= toChars
!10(10);
5946 assert(r
.length
== 2);
5947 assert(r
[0] == '1');
5948 assert(r
[1 .. 2].array
== "0");
5950 assert(r
.array
== "10");
5951 assert(s
.retro
.array
== "01");
5954 assert(toChars
!(16)(0u).array
== "0");
5955 assert(toChars
!(16)(0Lu).array
== "0");
5956 assert(toChars
!(16)(10u).array
== "a");
5957 assert(toChars
!(16, char, LetterCase
.upper
)(0x12AF34567Lu
).array
== "12AF34567");
5958 assert(toChars
!(16)(ubyte(0)).array
== "0");
5959 assert(toChars
!(16)(ushort(0)).array
== "0");
5960 assert(toChars
!(16)(ubyte.max
).array
== "ff");
5961 assert(toChars
!(16)(ushort.max
).array
== "ffff");
5963 auto r
= toChars
!(16)(16u);
5964 assert(r
.length
== 2);
5965 assert(r
[0] == '1');
5966 assert(r
[1 .. 2].array
== "0");
5968 assert(r
.array
== "10");
5969 assert(s
.retro
.array
== "01");
5973 @safe unittest // opSlice (https://issues.dlang.org/show_bug.cgi?id=16192)
5975 import std
.meta
: AliasSeq
;
5977 static struct Test
{ ubyte radix
; uint number
; }
5979 alias tests
= AliasSeq
!(
5980 Test(2, 0b1_0110_0111u),
5981 Test(2, 0b10_1100_1110u),
5982 Test(8, octal
!123456701u),
5983 Test(8, octal
!1234567012u),
5984 Test(10, 123456789u),
5985 Test(10, 1234567890u),
5986 Test(16, 0x789ABCDu
),
5987 Test(16, 0x789ABCDEu
),
5990 foreach (test; tests
)
5992 enum ubyte radix
= test.radix
;
5993 auto original
= toChars
!radix(test.number
);
5995 // opSlice vs popFront
5996 auto r
= original
.save
;
5998 for (; !r
.empty
; r
.popFront(), ++i
)
6000 assert(original
[i
.. original
.length
].tupleof
== r
.tupleof
);
6001 // tupleof is used to work around https://issues.dlang.org/show_bug.cgi?id=16216.
6004 // opSlice vs popBack
6007 for (; !r
.empty
; r
.popBack(), ++i
)
6009 assert(original
[0 .. original
.length
- i
].tupleof
== r
.tupleof
);
6012 // opSlice vs both popFront and popBack
6015 for (; r
.length
>= 2; r
.popFront(), r
.popBack(), ++i
)
6017 assert(original
[i
.. original
.length
- i
].tupleof
== r
.tupleof
);
6022 // Converts an unsigned integer to a compile-time string constant.
6023 package enum toCtString(ulong n
) = n
.stringof
[0 .. $ - "LU".length
];
6025 // Check that .stringof does what we expect, since it's not guaranteed by the
6027 @safe /*@betterC*/ unittest
6029 assert(toCtString
!0 == "0");
6030 assert(toCtString
!123456 == "123456");