123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455 |
|
/**
* This module provides a templated function that performs value-preserving
* conversions between arbitrary types. This function's behaviour can be
* extended for user-defined types as needed.
*
* Copyright: Copyright © 2007 Daniel Keep.
* License: BSD style: $(LICENSE)
* Authors: Daniel Keep
* Credits: Inspired in part by Andrei Alexandrescu's work on std.conv.
*/
module tango.util.Convert;
private import tango.core.Exception;
private import tango.core.Traits;
private import tango.core.Tuple : Tuple;
private import tango.math.Math;
private import tango.text.convert.Utf;
private import tango.text.convert.Float;
private import tango.text.convert.Integer;
private import Ascii = tango.text.Ascii;
version( TangoDoc )
{
/**
* Attempts to perform a value-preserving conversion of the given value
* from type S to type D. If the conversion cannot be performed in any
* context, a compile-time error will be issued describing the types
* involved. If the conversion fails at run-time because the destination
* type could not represent the value being converted, a
* ConversionException will be thrown.
*
* For example, to convert the string "123" into an equivalent integer
* value, you would use:
*
* -----
* auto v = to!(int)("123");
* -----
*
* You may also specify a default value which should be returned in the
* event that the conversion cannot take place:
*
* -----
* auto v = to!(int)("abc", 456);
* -----
*
* The function will attempt to preserve the input value as exactly as
* possible, given the limitations of the destination format. For
* instance, converting a floating-point value to an integer will cause it
* to round the value to the nearest integer value.
*
* Below is a complete list of conversions between built-in types and
* strings. Capitalised names indicate classes of types. Conversions
* between types in the same class are also possible.
*
* -----
* bool <-- Integer (0/!0), Char ('t'/'f'), String ("true"/"false")
* Integer <-- bool, Real, Char ('0'-'9'), String
* Real <-- Integer, String
* Imaginary <-- Complex
* Complex <-- Integer, Real, Imaginary
* Char <-- bool, Integer (0-9)
* String <-- bool, Integer, Real, Char
* -----
*
* Conversions between arrays and associative arrays are also supported,
* and are done element-by-element.
*
* You can add support for value conversions to your types by defining
* appropriate static and instance member functions. Given a type
* the_type, any of the following members of a type T may be used:
*
* -----
* the_type to_the_type();
* static T from_the_type(the_type);
* -----
*
* You may also use "camel case" names:
*
* -----
* the_type toTheType();
* static T fromTheType(the_type);
* -----
*
* Arrays and associative arrays can also be explicitly supported:
*
* -----
* the_type[] to_the_type_array();
* the_type[] toTheTypeArray();
*
* static T from_the_type_array(the_type[]);
* static T fromTheTypeArray(the_type[]);
*
* the_type[int] to_int_to_the_type_map();
* the_type[int] toIntToTheTypeMap();
*
* static T from_int_to_the_type_map(the_type[int]);
* static T fromIntToTheTypeMap(the_type[int]);
* -----
*
* If you have more complex requirements, you can also use the generic to
* and from templated members:
*
* -----
* the_type to(the_type)();
* static T from(the_type)(the_type);
* -----
*
* These templates will have the_type explicitly passed to them in the
* template instantiation.
*
* Finally, strings are given special support. The following members will
* be checked for:
*
* -----
* char[] toString();
* wchar[] toString16();
* dchar[] toString32();
* char[] toString();
* -----
*
* The "toString_" method corresponding to the destination string type will be
* tried first. If this method does not exist, then the function will
* look for another "toString_" method from which it will convert the result.
* Failing this, it will try "toString" and convert the result to the
* appropriate encoding.
*
* The rules for converting to a user-defined type are much the same,
* except it makes use of the "fromUtf8", "fromUtf16", "fromUtf32" and
* "fromString" static methods.
*
* Note: This module contains imports to other Tango modules that needs
* semantic analysis to be discovered. If your build tool doesn't do this
* properly, causing compile or link time problems, import the relevant
* module explicitly.
*/
D to(D,S)(S value);
D to(D,S)(S value, D default_); /// ditto
}
else
{
template to(D)
{
D to(S, Def=Missing)(S value, Def def=Def.init)
{
static if( is( Def == Missing ) )
return toImpl!(D,S)(value);
else
{
try
{
return toImpl!(D,S)(value);
}
catch( ConversionException e )
{}
return def;
}
}
}
}
/**
* This exception is thrown when the to template is unable to perform a
* conversion at run-time. This typically occurs when the source value cannot
* be represented in the destination type. This exception is also thrown when
* the conversion would cause an over- or underflow.
*/
class ConversionException : Exception
{
this( char[] msg )
{
super( msg );
}
}
private:
typedef int Missing;
/*
* So, how is this module structured?
*
* Firstly, we need a bunch of support code. The first block of this contains
* some CTFE functions for string manipulation (to cut down on the number of
* template symbols we generate.)
*
* The next contains a boat-load of templates. Most of these are trait
* templates (things like isPOD, isObject, etc.) There are also a number of
* mixins, and some switching templates (like toString_(n).)
*
* Another thing to mention is intCmp, which performs a safe comparison
* between two integers of arbitrary size and signage.
*
* Following all this are the templated to* implementations.
*
* The actual toImpl template is the second last thing in the module, with the
* module unit tests coming last.
*/
char ctfe_upper(char c)
{
if( 'a' <= c && c <= 'z' )
return cast(char)((c - 'a') + 'A');
else
return c;
}
char[] ctfe_camelCase(char[] s)
{
char[] result;
bool nextIsCapital = true;
foreach( c ; s )
{
if( nextIsCapital )
{
if( c == '_' )
result ~= c;
else
{
result ~= ctfe_upper(c);
nextIsCapital = false;
}
}
else
{
if( c == '_' )
nextIsCapital = true;
else
result ~= c;
}
}
return result;
}
bool ctfe_isSpace(T)(T c)
{
static if (T.sizeof is 1)
return (c <= 32 && (c is ' ' | c is '\t' | c is '\r'
| c is '\n' | c is '\v' | c is '\f'));
else
return (c <= 32 && (c is ' ' | c is '\t' | c is '\r'
| c is '\n' | c is '\v' | c is '\f'))
|| (c is '\u2028' | c is '\u2029');
}
T[] ctfe_triml(T)(T[] source)
{
if( source.length == 0 )
return null;
foreach( i,c ; source )
if( !ctfe_isSpace(c) )
return source[i..$];
return null;
}
T[] ctfe_trimr(T)(T[] source)
{
if( source.length == 0 )
return null;
foreach_reverse( i,c ; source )
if( !ctfe_isSpace(c) )
return source[0..i+1];
return null;
}
T[] ctfe_trim(T)(T[] source)
{
return ctfe_trimr(ctfe_triml(source));
}
template isPOD(T)
{
static if( is( T == struct ) || is( T == union ) )
const isPOD = true;
else
const isPOD = false;
}
template isObject(T)
{
static if( is( T == class ) || is( T == interface ) )
const isObject = true;
else
const isObject = false;
}
template isUDT(T)
{
const isUDT = isPOD!(T) || isObject!(T);
}
template isString(T)
{
static if( is( typeof(T[]) == char[] )
|| is( typeof(T[]) == wchar[] )
|| is( typeof(T[]) == dchar[] ) )
const isString = true;
else
const isString = false;
}
template isArrayType(T)
{
const isArrayType = isDynamicArrayType!(T) || isStaticArrayType!(T);
}
/*
* Determines which signed integer type of T and U is larger.
*/
template sintSuperType(T,U)
{
static if( is( T == long ) || is( U == long ) )
alias long sintSuperType;
else static if( is( T == int ) || is( U == int ) )
alias int sintSuperType;
else static if( is( T == short ) || is( U == short ) )
alias short sintSuperType;
else static if( is( T == byte ) || is( U == byte ) )
alias byte sintSuperType;
}
/*
* Determines which unsigned integer type of T and U is larger.
*/
template uintSuperType(T,U)
{
static if( is( T == ulong ) || is( U == ulong ) )
alias ulong uintSuperType;
else static if( is( T == uint ) || is( U == uint ) )
alias uint uintSuperType;
else static if( is( T == ushort ) || is( U == ushort ) )
alias ushort uintSuperType;
else static if( is( T == ubyte ) || is( U == ubyte ) )
alias ubyte uintSuperType;
}
template uintOfSize(uint bytes)
{
static if( bytes == 1 )
alias ubyte uintOfSize;
else static if( bytes == 2 )
alias ushort uintOfSize;
else static if( bytes == 4 )
alias uint uintOfSize;
}
/*
* Safely performs a comparison between two integer values, taking into
* account different sizes and signages.
*/
int intCmp(T,U)(T lhs, U rhs)
{
static if( isSignedIntegerType!(T) && isSignedIntegerType!(U) )
{
alias sintSuperType!(T,U) S;
auto l = cast(S) lhs;
auto r = cast(S) rhs;
if( l < r ) return -1;
else if( l > r ) return 1;
else return 0;
}
else static if( isUnsignedIntegerType!(T) && isUnsignedIntegerType!(U) )
{
alias uintSuperType!(T,U) S;
auto l = cast(S) lhs;
auto r = cast(S) rhs;
if( l < r ) return -1;
else if( l > r ) return 1;
else return 0;
}
else
{
static if( isSignedIntegerType!(T) )
{
if( lhs < 0 )
return -1;
else
{
static if( U.sizeof >= T.sizeof )
{
auto l = cast(U) lhs;
if( l < rhs ) return -1;
else if( l > rhs ) return 1;
else return 0;
}
else
{
auto l = cast(ulong) lhs;
auto r = cast(ulong) rhs;
if( l < r ) return -1;
else if( l > r ) return 1;
else return 0;
}
}
}
else static if( isSignedIntegerType!(U) )
{
if( rhs < 0 )
return 1;
else
{
static if( T.sizeof >= U.sizeof )
{
auto r = cast(T) rhs;
if( lhs < r ) return -1;
else if( lhs > r ) return 1;
else return 0;
}
else
{
auto l = cast(ulong) lhs;
auto r = cast(ulong) rhs;
if( l < r ) return -1;
else if( l > r ) return 1;
else return 0;
}
}
}
}
}
template unsupported(char[] desc="")
{
static assert(false, "Unsupported conversion: cannot convert to "
~ctfe_trim(D.stringof)~" from "
~(desc!="" ? desc~" " : "")~ctfe_trim(S.stringof)~".");
}
template unsupported_backwards(char[] desc="")
{
static assert(false, "Unsupported conversion: cannot convert to "
~(desc!="" ? desc~" " : "")~ctfe_trim(D.stringof)
~" from "~ctfe_trim(S.stringof)~".");
}
// TN works out the c_case name of the given type.
template TN(T:T[])
{
static if( is( T == char ) )
const TN = "string";
else static if( is( T == wchar ) )
const TN = "wstring";
else static if( is( T == dchar ) )
const TN = "dstring";
else
const TN = TN!(T)~"_array";
}
// ditto
template TN(T:T*)
{
const TN = TN!(T)~"_pointer";
}
// ditto
template TN(T)
{
static if( isAssocArrayType!(T) )
const TN = TN!(typeof(T.keys[0]))~"_to_"
~TN!(typeof(T.values[0]))~"_map";
else
const TN = ctfe_trim(T.stringof);
}
// Picks an appropriate toString* method from t.text.convert.Utf.
template toString_(T)
{
static if( is( T == char[] ) )
alias tango.text.convert.Utf.toString toString_;
else static if( is( T == wchar[] ) )
alias tango.text.convert.Utf.toString16 toString_;
else
alias tango.text.convert.Utf.toString32 toString_;
}
template UtfNum(T)
{
const UtfNum = is(typeof(T[0])==char) ? "8" : (
is(typeof(T[0])==wchar) ? "16" : "32");
}
template StringNum(T)
{
const StringNum = is(typeof(T[0])==char) ? "" : (
is(typeof(T[0])==wchar) ? "16" : "32");
}
// Decodes a single dchar character from a string. Yes, I know they're
// actually code points, but I can't be bothered to type that much. Although
// I suppose I just typed MORE than that by writing this comment. Meh.
dchar firstCharOf(T)(T s, out size_t used)
{
static if( is( T : char[] ) || is( T : wchar[] ) )
{
return tango.text.convert.Utf.decode(s, used);
}
else
{
used = 1;
return s[0];
}
}
// This mixin defines a general function for converting to a UDT.
template toUDT()
{
D toDfromS()
{
static if( isString!(S) )
{
static if( is( typeof(mixin("D.fromUtf"
~UtfNum!(S)~"(value)")) : D ) )
return mixin("D.fromUtf"~UtfNum!(S)~"(value)");
else static if( is( typeof(D.fromUtf8(""c)) : D ) )
return D.fromUtf8(toString_!(char[])(value));
else static if( is( typeof(D.fromUtf16(""w)) : D ) )
return D.fromUtf16(toString_!(wchar[])(value));
else static if( is( typeof(D.fromUtf32(""d)) : D ) )
return D.fromUtf32(toString_!(dchar[])(value));
else static if( is( typeof(D.fromString(""c)) : D ) )
{
static if( is( S == char[] ) )
return D.fromString(value);
else
return D.fromString(toString_!(char[])(value));
}
// Default fallbacks
else static if( is( typeof(D.from!(S)(value)) : D ) )
return D.from!(S)(value);
else
mixin unsupported!("user-defined type");
}
else
{
// TODO: Check for templates. Dunno what to do about them.
static if( is( typeof(mixin("D.from_"~TN!(S)~"()")) : D ) )
return mixin("D.from_"~TN!(S)~"()");
else static if( is( typeof(mixin("D.from"
~ctfe_camelCase(TN!(S))~"()")) : D ) )
return mixin("D.from"~ctfe_camelCase(TN!(S))~"()");
else static if( is( typeof(D.from!(S)(value)) : D ) )
return D.from!(S)(value);
else
mixin unsupported!("user-defined type");
}
}
}
// This mixin defines a general function for converting from a UDT.
template fromUDT(char[] fallthrough="")
{
D toDfromS()
{
static if( isString!(D) )
{
static if( is( typeof(mixin("value.toString"
~StringNum!(D)~"()")) == D ) )
return mixin("value.toString"~StringNum!(D)~"()");
else static if( is( typeof(value.toString()) == char[] ) )
return toString_!(D)(value.toString);
else static if( is( typeof(value.toString16()) == wchar[] ) )
return toString_!(D)(value.toString16);
else static if( is( typeof(value.toString32()) == dchar[] ) )
return toString_!(D)(value.toString32);
else static if( is( typeof(value.toString()) == char[] ) )
{
static if( is( D == char[] ) )
return value.toString;
else
{
return toString_!(D)(value.toString);
}
}
// Default fallbacks
else static if( is( typeof(value.to!(D)()) : D ) )
return value.to!(D)();
else static if( fallthrough != "" )
mixin(fallthrough);
else
mixin unsupported!("user-defined type");
}
else
{
// TODO: Check for templates. Dunno what to do about them.
static if( is( typeof(mixin("value.to_"~TN!(D)~"()")) : D ) )
return mixin("value.to_"~TN!(D)~"()");
else static if( is( typeof(mixin("value.to"
~ctfe_camelCase(TN!(D))~"()")) : D ) )
return mixin("value.to"~ctfe_camelCase(TN!(D))~"()");
else static if( is( typeof(value.to!(D)()) : D ) )
return value.to!(D)();
else static if( fallthrough != "" )
mixin(fallthrough);
else
mixin unsupported!("user-defined type");
}
}
}
template convError()
{
void throwConvError()
{
// Since we're going to use to!(T) to convert the value to a string,
// we need to make sure we don't end up in a loop...
static if( isString!(D) || !is( typeof(to!(char[])(value)) == char[] ) )
{
throw new ConversionException("Could not convert a value of type "
~S.stringof~" to type "~D.stringof~".");
}
else
{
throw new ConversionException("Could not convert `"
~to!(char[])(value)~"` of type "
~S.stringof~" to type "~D.stringof~".");
}
}
}
D toBool(D,S)(S value)
{
static assert(is(D==bool));
static if( isIntegerType!(S) /+|| isRealType!(S) || isImaginaryType!(S)
|| isComplexType!(S)+/ )
// The weird comparison is to support NaN as true
return !(value == 0);
else static if( isCharType!(S) )
{
switch( value )
{
case 'F': case 'f':
return false;
case 'T': case 't':
return true;
default:
mixin convError;
throwConvError;
}
}
else static if( isString!(S) )
{
switch( Ascii.toLower(value) )
{
case "false":
return false;
case "true":
return true;
default:
mixin convError;
throwConvError;
}
}
/+
else static if( isDynamicArrayType!(S) || isStaticArrayType!(S) )
{
mixin unsupported!("array type");
}
else static if( isAssocArrayType!(S) )
{
mixin unsupported!("associative array type");
}
else static if( isPointerType!(S) )
{
mixin unsupported!("pointer type");
}
else static if( is( S == typedef ) )
{
mixin unsupported!("typedef'ed type");
}
// +/
else static if( isPOD!(S) || isObject!(S) )
{
mixin fromUDT;
return toDfromS;
}
else
{
mixin unsupported;
}
}
D toIntegerFromInteger(D,S)(S value)
{
static if( (cast(ulong) D.max) < (cast(ulong) S.max)
|| (cast(long) D.min) > (cast(long) S.min) )
{
mixin convError; // TODO: Overflow error
if( intCmp(value,D.min)<0 || intCmp(value,D.max)>0 )
{
throwConvError;
}
}
return cast(D) value;
}
D toIntegerFromReal(D,S)(S value)
{
auto v = tango.math.Math.round(value);
if( (cast(real) D.min) <= v && v <= (cast(real) D.max) )
{
return cast(D) v;
}
else
{
mixin convError; // TODO: Overflow error
throwConvError;
}
}
D toIntegerFromString(D,S)(S value)
{
static if( is( S charT : charT[] ) )
{
mixin convError;
static if( is( D == ulong ) )
{
// Check for sign
S s = value;
if( s.length == 0 )
throwConvError;
else if( s[0] == '-' )
throwConvError;
else if( s[0] == '+' )
s = s[1..$];
uint len;
auto result = tango.text.convert.Integer.convert(s, 10, &len);
if( len < s.length || len == 0 )
throwConvError;
return result;
}
else
{
uint len;
auto result = tango.text.convert.Integer.parse(value, 10, &len);
if( len < value.length || len == 0 )
throwConvError;
return toIntegerFromInteger!(D,long)(result);
}
}
}
D toInteger(D,S)(S value)
{
static if( is( S == bool ) )
return (value ? 1 : 0);
else static if( isIntegerType!(S) )
{
return toIntegerFromInteger!(D,S)(value);
}
else static if( isCharType!(S) )
{
if( value >= '0' && value <= '9' )
{
return cast(D)(value - '0');
}
else
{
mixin convError;
throwConvError;
}
}
else static if( isRealType!(S) )
{
return toIntegerFromReal!(D,S)(value);
}
else static if( isString!(S) )
{
return toIntegerFromString!(D,S)(value);
}
else static if( isPOD!(S) || isObject!(S) )
{
mixin fromUDT;
return toDfromS;
}
else
mixin unsupported;
}
D toReal(D,S)(S value)
{
/+static if( is( S == bool ) )
return (value ? 1.0 : 0.0);
else+/ static if( isIntegerType!(S) || isRealType!(S) )
return cast(D) value;
/+else static if( isCharType!(S) )
return cast(D) to!(uint)(value);+/
else static if( isString!(S) )
{
/+
try
{
return tango.text.convert.Float.toFloat(value);
}
catch( IllegalArgumentException e )
{
mixin convError;
throwConvError;
}
+/
mixin convError;
uint len;
auto r = tango.text.convert.Float.parse(value, &len);
if( len < value.length || len == 0 )
throwConvError;
return r;
}
else static if( isPOD!(S) || isObject!(S) )
{
mixin fromUDT;
return toDfromS;
}
else
mixin unsupported;
}
D toImaginary(D,S)(S value)
{
/+static if( is( S == bool ) )
return (value ? 1.0i : 0.0i);
else+/ static if( isComplexType!(S) )
{
if( value.re == 0.0 )
return value.im * cast(D)1.0i;
else
{
mixin convError;
throwConvError;
}
}
else static if( isPOD!(S) || isObject!(S) )
{
mixin fromUDT;
return toDfromS;
}
else
mixin unsupported;
}
D toComplex(D,S)(S value)
{
static if( isIntegerType!(S) || isRealType!(S) || isImaginaryType!(S)
|| isComplexType!(S) )
return cast(D) value;
/+else static if( isCharType!(S) )
return cast(D) to!(uint)(value);+/
else static if( isPOD!(S) || isObject!(S) )
{
mixin fromUDT;
return toDfromS;
}
else
mixin unsupported;
}
D toChar(D,S)(S value)
{
static if( is( S == bool ) )
return (value ? 't' : 'f');
else static if( isIntegerType!(S) )
{
if( value >= 0 && value <= 9 )
return cast(D) value+'0';
else
{
mixin convError; // TODO: Overflow error
throwConvError;
}
}
else static if( isString!(S) )
{
void fail()
{
mixin convError;
throwConvError;
}
if( value.length == 0 )
fail();
else
{
size_t used;
dchar c = firstCharOf(value, used);
if( used < value.length )
{
fail(); // TODO: Overflow error
}
if( (cast(size_t) c) > (cast(size_t) D.max) )
{
fail(); // TODO: Overflow error
}
return cast(D) c;
}
}
else static if( isPOD!(S) || isObject!(S) )
{
mixin fromUDT;
return toDfromS;
}
else
mixin unsupported;
}
D toStringFromString(D,S)(S value)
{
static if( is( typeof(D[0]) == char ) )
return tango.text.convert.Utf.toString(value);
else static if( is( typeof(D[0]) == wchar ) )
return tango.text.convert.Utf.toString16(value);
else
{
static assert( is( typeof(D[0]) == dchar ) );
return tango.text.convert.Utf.toString32(value);
}
}
const char[] CHARS =
"\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f"
"\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f"
"\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f"
"\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f"
"\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f"
"\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e";
D toStringFromChar(D,S)(S value)
{
static if( is( D == S[] ) )
{
static if( is( S == char ) )
{
if( 0x20 <= value && value <= 0x7e )
return (&CHARS[value-0x20])[0..1];
}
auto r = new S[1];
r[0] = value;
return r;
}
else
{
S[1] temp;
temp[0] = value;
return toStringFromString!(D,S[])(temp);
}
}
D toString(D,S)(S value)
{
static if( is( S == bool ) )
return (value ? "true" : "false");
else static if( isCharType!(S) )
return toStringFromChar!(D,S)(value);
else static if( isIntegerType!(S) )
// TODO: Make sure this works with ulongs.
return mixin("tango.text.convert.Integer.toString"~StringNum!(D)~"(value)");
else static if( isRealType!(S) )
return mixin("tango.text.convert.Float.toString"~StringNum!(D)~"(value)");
else static if( isDynamicArrayType!(S) || isStaticArrayType!(S) )
mixin unsupported!("array type");
else static if( isAssocArrayType!(S) )
mixin unsupported!("associative array type");
else static if( isPOD!(S) || isObject!(S) )
{
mixin fromUDT;
return toDfromS;
}
else
mixin unsupported;
}
D fromString(D,S)(D value)
{
static if( isDynamicArrayType!(S) || isStaticArrayType!(S) )
mixin unsupported_backwards!("array type");
else static if( isAssocArrayType!(S) )
mixin unsupported_backwards!("associative array type");
else static if( isPOD!(S) || isObject!(S) )
{
mixin toUDT;
return toDfromS;
}
else
mixin unsupported_backwards;
}
D toArrayFromArray(D,S)(S value)
{
alias typeof(D[0]) De;
D result; result.length = value.length;
scope(failure) delete result;
foreach( i,e ; value )
result[i] = to!(De)(e);
return result;
}
D toMapFromMap(D,S)(S value)
{
alias typeof(D.keys[0]) Dk;
alias typeof(D.values[0]) Dv;
D result;
foreach( k,v ; value )
result[ to!(Dk)(k) ] = to!(Dv)(v);
return result;
}
D toFromUDT(D,S)(S value)
{
// Try value.to* first
static if( is( typeof(mixin("value.to_"~TN!(D)~"()")) : D ) )
return mixin("value.to_"~TN!(D)~"()");
else static if( is( typeof(mixin("value.to"
~ctfe_camelCase(TN!(D))~"()")) : D ) )
return mixin("value.to"~ctfe_camelCase(TN!(D))~"()");
else static if( is( typeof(value.to!(D)()) : D ) )
return value.to!(D)();
// Ok, try D.from* now
else static if( is( typeof(mixin("D.from_"~TN!(S)~"(value)")) : D ) )
return mixin("D.from_"~TN!(S)~"(value)");
else static if( is( typeof(mixin("D.from"
~ctfe_camelCase(TN!(S))~"(value)")) : D ) )
return mixin("D.from"~ctfe_camelCase(TN!(S))~"(value)");
else static if( is( typeof(D.from!(S)(value)) : D ) )
return D.from!(S)(value);
// Give up
else
mixin unsupported;
}
D toImpl(D,S)(S value)
{
static if( is( D == S ) )
return value;
else static if( is( S BaseType == typedef ) )
return toImpl!(D,BaseType)(value);
else static if( is( S BaseType == enum ) )
return toImpl!(D,BaseType)(value);
else static if( isArrayType!(D) && isArrayType!(S)
&& is( typeof(D[0]) == typeof(S[0]) ) )
// Special-case which catches to!(T[])!(T[n]).
return value;
else static if( is( D == bool ) )
return toBool!(D,S)(value);
else static if( isIntegerType!(D) )
return toInteger!(D,S)(value);
else static if( isRealType!(D) )
return toReal!(D,S)(value);
else static if( isImaginaryType!(D) )
return toImaginary!(D,S)(value);
else static if( isComplexType!(D) )
return toComplex!(D,S)(value);
else static if( isCharType!(D) )
return toChar!(D,S)(value);
else static if( isString!(D) && isString!(S) )
return toStringFromString!(D,S)(value);
else static if( isString!(D) )
return toString!(D,S)(value);
else static if( isString!(S) )
return fromString!(D,S)(value);
else static if( isArrayType!(D) && isArrayType!(S) )
return toArrayFromArray!(D,S)(value);
else static if( isAssocArrayType!(D) && isAssocArrayType!(S) )
return toMapFromMap!(D,S)(value);
else static if( isUDT!(D) || isUDT!(S) )
return toFromUDT!(D,S)(value);
else
mixin unsupported;
}
debug ( ConvertTest ):
void main() {}
debug( UnitTest ):
bool ex(T)(lazy T v)
{
bool result = false;
try
{
v();
}
catch( ConversionException _ )
{
result = true;
}
return result;
}
bool nx(T)(lazy T v)
{
bool result = true;
try
{
v();
}
catch( ConversionException _ )
{
result = false;
}
return result;
}
struct Foo
{
int toInt() { return 42; }
char[] toString() { return "string foo"; }
int[] toIntArray() { return [1,2,3]; }
Bar toBar()
{
Bar result; return result;
}
T to(T)()
{
static if( is( T == bool ) )
return true;
else
static assert( false );
}
}
struct Bar
{
real toReal()
{
return 3.14159;
}
ireal toIreal()
{
return 42.0i;
}
}
struct Baz
{
static Baz fromFoo(Foo foo)
{
Baz result; return result;
}
Bar toBar()
{
Bar result; return result;
}
}
unittest
{
/*
* bool
*/
static assert( !is( typeof(to!(bool)(1.0)) ) );
static assert( !is( typeof(to!(bool)(1.0i)) ) );
static assert( !is( typeof(to!(bool)(1.0+1.0i)) ) );
assert( to!(bool)(0) == false );
assert( to!(bool)(1) == true );
assert( to!(bool)(-1) == true );
assert( to!(bool)('t') == true );
assert( to!(bool)('T') == true );
assert( to!(bool)('f') == false );
assert( to!(bool)('F') == false );
assert(ex( to!(bool)('x') ));
assert( to!(bool)("true") == true );
assert( to!(bool)("false") == false );
assert( to!(bool)("TrUe") == true );
assert( to!(bool)("fAlSe") == false );
/*
* Integer
*/
assert( to!(int)(42L) == 42 );
assert( to!(byte)(42) == cast(byte)42 );
assert( to!(short)(-1701) == cast(short)-1701 );
assert( to!(long)(cast(ubyte)72) == 72L );
assert(nx( to!(byte)(127) ));
assert(ex( to!(byte)(128) ));
assert(nx( to!(byte)(-128) ));
assert(ex( to!(byte)(-129) ));
assert(nx( to!(ubyte)(255) ));
assert(ex( to!(ubyte)(256) ));
assert(nx( to!(ubyte)(0) ));
assert(ex( to!(ubyte)(-1) ));
assert(nx( to!(long)(9_223_372_036_854_775_807UL) ));
assert(ex( to!(long)(9_223_372_036_854_775_808UL) ));
assert(nx( to!(ulong)(0L) ));
assert(ex( to!(ulong)(-1L) ));
assert( to!(int)(3.14159) == 3 );
assert( to!(int)(2.71828) == 3 );
assert( to!(int)("1234") == 1234 );
assert( to!(int)(true) == 1 );
assert( to!(int)(false) == 0 );
assert( to!(int)('0') == 0 );
assert( to!(int)('9') == 9 );
/*
* Real
*/
assert( to!(real)(3) == 3.0 );
assert( to!(real)("1.125") == 1.125 );
/*
* Imaginary
*/
static assert( !is( typeof(to!(ireal)(3.0)) ) );
assert( to!(ireal)(0.0+1.0i) == 1.0i );
assert(nx( to!(ireal)(0.0+1.0i) ));
assert(ex( to!(ireal)(1.0+0.0i) ));
/*
* Complex
*/
assert( to!(creal)(1) == (1.0+0.0i) );
assert( to!(creal)(2.0) == (2.0+0.0i) );
assert( to!(creal)(3.0i) == (0.0+3.0i) );
/*
* Char
*/
assert( to!(char)(true) == 't' );
assert( to!(char)(false) == 'f' );
assert( to!(char)(0) == '0' );
assert( to!(char)(9) == '9' );
assert(ex( to!(char)(-1) ));
assert(ex( to!(char)(10) ));
assert( to!(char)("a"d) == 'a' );
assert( to!(dchar)("ε"c) == 'ε' );
assert(ex( to!(char)("ε"d) ));
/*
* String-string
*/
assert( to!(char[])("Í love to æt "w) == "Í love to æt "c );
assert( to!(char[])("them smûrƒies™,"d) == "them smûrƒies™,"c );
assert( to!(wchar[])("Smûrfies™ I love"c) == "Smûrfies™ I love"w );
assert( to!(wchar[])("2 食い散らす"d) == "2 食い散らす"w );
assert( to!(dchar[])("bite đey µgly"c) == "bite đey µgly"d );
assert( to!(dchar[])("headž ㍳ff"w) == "headž ㍳ff"d );
// ... nibble on they bluish feet.
/*
* String
*/
assert( to!(char[])(true) == "true" );
assert( to!(char[])(false) == "false" );
assert( to!(char[])(12345678) == "12345678" );
assert( to!(char[])(1234.567800) == "1234.57");
assert( to!( char[])(cast(char) 'a') == "a"c );
assert( to!(wchar[])(cast(char) 'b') == "b"w );
assert( to!(dchar[])(cast(char) 'c') == "c"d );
assert( to!( char[])(cast(wchar)'d') == "d"c );
assert( to!(wchar[])(cast(wchar)'e') == "e"w );
assert( to!(dchar[])(cast(wchar)'f') == "f"d );
assert( to!( char[])(cast(dchar)'g') == "g"c );
assert( to!(wchar[])(cast(dchar)'h') == "h"w );
assert( to!(dchar[])(cast(dchar)'i') == "i"d );
/*
* Array-array
*/
assert( to!(ubyte[])([1,2,3]) == [cast(ubyte)1, 2, 3] );
assert( to!(bool[])(["true"[], "false"]) == [true, false] );
/*
* Map-map
*/
{
char[][int] src = [1:"true"[], 2:"false"];
bool[ubyte] dst = to!(bool[ubyte])(src);
assert( dst.keys.length == 2 );
assert( dst[1] == true );
assert( dst[2] == false );
}
/*
* UDT
*/
{
Foo foo;
assert( to!(bool)(foo) == true );
assert( to!(int)(foo) == 42 );
assert( to!(char[])(foo) == "string foo" );
assert( to!(wchar[])(foo) == "string foo"w );
assert( to!(dchar[])(foo) == "string foo"d );
assert( to!(int[])(foo) == [1,2,3] );
assert( to!(ireal)(to!(Bar)(foo)) == 42.0i );
assert( to!(real)(to!(Bar)(to!(Baz)(foo))) == 3.14159 );
}
/*
* Default values
*/
{
assert( to!(int)("123", 456) == 123,
`to!(int)("123", 456) == "` ~ to!(char[])(
to!(int)("123", 456)) ~ `"` );
assert( to!(int)("abc", 456) == 456,
`to!(int)("abc", 456) == "` ~ to!(char[])(
to!(int)("abc", 456)) ~ `"` );
}
/*
* Ticket #1486
*/
{
assert(ex( to!(int)("") ));
assert(ex( to!(real)("Foo") ));
assert(ex( to!(real)("") ));
assert(ex( to!(real)("0x1.2cp+9") ));
// From d0c's patch
assert(ex( to!(int)("0x20") ));
assert(ex( to!(int)("0x") ));
assert(ex( to!(int)("-") ));
assert(ex( to!(int)("-0x") ));
assert( to!(real)("0x20") == cast(real) 0x20 );
assert(ex( to!(real)("0x") ));
assert(ex( to!(real)("-") ));
}
}
|