| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553 | /** * 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 { D to(D, S)(S value) { return toImpl!(D,S)(value); } D to(D, S, Def)(S value, Def def=Def.init) { 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( immutable(char)[] msg ) { super( msg ); } } private: /* * 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(const(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 ) ) enum isPOD = true; else enum isPOD = false; } template isObject(T) { static if( is( T == class ) || is( T == interface ) ) enum isObject = true; else enum isObject = false; } template isUDT(T) { enum isUDT = isPOD!(T) || isObject!(T); } template isString(T) { static if( is( typeof(T[]) : const(char[]) ) || is( typeof(T[]) : const(wchar[]) ) || is( typeof(T[]) : const(dchar[]) ) ) enum isString = true; else enum isString = false; } template isMutableString(T) { static if( is( typeof(T[]) == char[] ) || is( typeof(T[]) == wchar[] ) || is( typeof(T[]) == dchar[] ) ) enum isMutableString = true; else enum isMutableString = false; } template isImmutableString(T) { static if( is( typeof(T[]) == immutable(char)[] ) || is( typeof(T[]) == immutable(wchar)[] ) || is( typeof(T[]) == immutable(dchar)[] ) ) enum isImmutableString = true; else enum isImmutableString = false; } template isArrayType(T) { enum 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(immutable(char)[] desc="") { static assert(false, "Unsupported conversion: cannot convert to " ~ctfe_trim(D.stringof)~" from " ~(desc!="" ? desc~" " : "")~ctfe_trim(S.stringof)~"."); } template unsupported_backwards(immutable(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:const(T[])) { static if( is( T == char ) ) enum TN = "string"; else static if( is( T == wchar ) ) enum TN = "wstring"; else static if( is( T == dchar ) ) enum TN = "dstring"; else enum TN = TN!(T)~"_array"; } // ditto template TN(T:T*) { enum TN = TN!(T)~"_pointer"; } // ditto template TN(T) { static if( isAssocArrayType!(T) ) enum TN = TN!(typeof(T.keys[0]))~"_to_" ~TN!(typeof(T.values[0]))~"_map"; else enum TN = ctfe_trim(T.stringof); } // Takes care of converting between mutable and immutable strings D convertString_(D, C)(C[] ret) { static if(isImmutableString!(C)) { static if(isMutableString!(D)) return cast(D) ret.dup; else return cast(D) ret; } else { static if(isImmutableString!(D)) return cast(D) ret.idup; else return cast(D) ret; } } // Picks an appropriate toString* method from t.text.convert.Utf. T toString_(T, C)(C[] str) { static if( is( T : const(char[]) ) ) return convertString_!(T)(tango.text.convert.Utf.toString(str)); else static if( is( T : const(wchar[]) ) ) return convertString_!(T)(tango.text.convert.Utf.toString16(str)); else return convertString_!(T)(tango.text.convert.Utf.toString32(str)); } template UtfNum(T) { enum UtfNum = is(typeof(T[0]) : const(char)) ? "8" : ( is(typeof(T[0]) : const(wchar)) ? "16" : "32"); } template StringNum(T) { enum StringNum = is(typeof(T[0]) : const(char)) ? "" : ( is(typeof(T[0]) : const(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 : const(char[]) ) || is( T : const(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(immutable(char)[] fallthrough="") { D toDfromS() { static if( isString!(D) ) { static if( is( typeof(convertString_!(D)(mixin("value.toString" ~StringNum!(D)~"()"))) : D ) ) return convertString_!(D)(mixin("value.toString"~StringNum!(D)~"()")); else static if( is( typeof(value.toString()) : const(char[]) ) ) return toString_!(D)(value.toString()); else static if( is( typeof(value.toString16()) : const(wchar[]) ) ) return toString_!(D)(value.toString16); else static if( is( typeof(value.toString32()) : const(dchar[]) ) ) return toString_!(D)(value.toString32); else static if( is( typeof(value.toString()) : const(char[]) ) ) { static if( is( D : const(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!(immutable(char)[])(value)) == immutable(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!(immutable(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(); assert(0); } } else static if( isString!(S) ) { if(value.length == 5 || value.length == 4) { char[5] buf; buf[0..value.length] = value[]; switch( Ascii.toLower(buf[0..value.length]) ) { case "false": return false; case "true": return true; default: } } mixin convError; throwConvError(); assert(0); } /+ 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 == alias ) ) { mixin unsupported!("alias'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(); assert(0); } } 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..$]; size_t len; auto result = tango.text.convert.Integer.convert(s, 10, &len); if( len < s.length || len == 0 ) throwConvError(); return result; } else { size_t 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(); assert(0); } } 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; size_t 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(); assert(0); } } 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(); assert(0); } } else static if( isString!(S) ) { void fail() { mixin convError; throwConvError(); } if( value.length == 0 ) { fail(); assert(0); } else { size_t used; dchar c = firstCharOf(value, used); if( used < value.length ) { fail(); // TODO: Overflow error assert(0); } if( (cast(size_t) c) > (cast(size_t) D.max) ) { fail(); // TODO: Overflow error assert(0); } return cast(D) c; } } else static if( isPOD!(S) || isObject!(S) ) { mixin fromUDT; return toDfromS(); } else mixin unsupported; } D toStringFromString(D,S)(S value) { return toString_!(D)(value); } enum immutable(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 convertString_!(D)((&CHARS[value-0x20])[0..1]); } } auto r = new S[1]; r[0] = value; return convertString_!(D)(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 convertString_!(D)(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 convertString_!(D)(mixin("tango.text.convert.Integer.toString"~StringNum!(D)~"(value)")); } else static if( isRealType!(S) ) return convertString_!(D)(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 BaseTypeOf!(typeof(D[0])) De; De[] result; result.length = value.length; scope(failure) delete result; foreach( i,e ; value ) result[i] = to!(De)(e); /* Safe because it is newly allocated */ return cast(D)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 == 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; } immutable(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 */ { immutable(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)("-") )); } /* * Const and immutable */ { assert( to!(immutable(char)[])("Í love to æt "w.dup) == "Í love to æt "c ); assert( to!(immutable(char)[])("them smûrƒies™,"d.dup) == "them smûrƒies™,"c ); assert( to!(immutable(wchar)[])("Smûrfies™ I love"c.dup) == "Smûrfies™ I love"w ); assert( to!(immutable(wchar)[])("2 食い散らす"d.dup) == "2 食い散らす"w ); assert( to!(immutable(dchar)[])("bite đey µgly"c.dup) == "bite đey µgly"d ); assert( to!(immutable(dchar)[])("headž ㍳ff"w.dup) == "headž ㍳ff"d ); // ... nibble on they bluish feet. assert( to!(immutable(char)[])("食い散らす"c.dup) == "食い散らす"c ); assert( to!(immutable(wchar)[])("食い散らす"w.dup) == "食い散らす"w ); assert( to!(immutable(dchar)[])("食い散らす"d.dup) == "食い散らす"d ); assert( to!(immutable(char)[])(true) == "true" ); assert( to!(immutable(char)[])(false) == "false" ); assert( to!(immutable(char)[])(12345678) == "12345678" ); assert( to!(immutable(char)[])(1234.567800) == "1234.57"); assert( to!(immutable( char)[])(cast(char) 'a') == "a"c ); assert( to!(immutable(wchar)[])(cast(char) 'b') == "b"w ); assert( to!(immutable(dchar)[])(cast(char) 'c') == "c"d ); assert( to!(immutable( char)[])(cast(wchar)'d') == "d"c ); assert( to!(immutable(wchar)[])(cast(wchar)'e') == "e"w ); assert( to!(immutable(dchar)[])(cast(wchar)'f') == "f"d ); assert( to!(immutable( char)[])(cast(dchar)'g') == "g"c ); assert( to!(immutable(wchar)[])(cast(dchar)'h') == "h"w ); assert( to!(immutable(dchar)[])(cast(dchar)'i') == "i"d ); assert( to!(immutable(ubyte)[])([1,2,3]) == [cast(ubyte)1, 2, 3] ); assert( to!(const(char)[])("Í love to æt "w) == "Í love to æt "c ); Foo foo; assert( to!(immutable(char)[])(foo) == "string foo" ); assert( to!(immutable(wchar)[])(foo) == "string foo"w ); assert( to!(immutable(dchar)[])(foo) == "string foo"d ); /* assert( to!(immutable(int)[])(foo) == [1,2,3] ); */ } /* * Pass through */ { assert( to!(int)(cast(int)1) == 1 ); assert( to!(char[])("abc".dup) == "abc" ); assert( to!(immutable(char)[])("abc") == "abc" ); assert( to!(immutable(dchar)[])("abc"d) == "abc"d ); } } |