module my_metastrings; import std.traits; template Tuple(T...) { alias T Tuple; } template Map(alias Fn, T...) { static if (T.length == 0) alias Tuple!() Map; else alias Tuple!(Fn!(T[0]).val, Map!(Fn, T[1..$])) Map; } version (UnitTest) { struct ListOf(T) { } template Fn(T) { alias ListOf!(T) val; } } unittest { alias Map!(Fn, int, double, real) M; static assert(is(M[0] == ListOf!(int))); static assert(is(M[1] == ListOf!(double))); static assert(is(M[2] == ListOf!(real))); static assert(M.length == 3); } template FoldR(alias Fn, T...) { static if (T.length == 0) static assert(false, "Must have starting case for FoldR"); else static if (T.length == 1) alias T[0] FoldR; else alias FoldR!(Fn, Fn!(T[0], T[1]).val, T[2..$]) FoldR; } template ValueFoldR(alias Fn, T...) { static if (T.length == 0) static assert(false, "Must have starting case for FoldR"); else static if (T.length == 1) const ValueFoldR = T[0]; else const ValueFoldR = ValueFoldR!(Fn, Fn!(T[0], T[1]).val, T[2..$]); } version(UnitTest) { template Concat(string a, string b) { const val = a ~ ", " ~ b; } } unittest { static assert(ValueFoldR!(Concat, "int", "double", "real") == "int, double, real"); static assert(ValueFoldR!(Concat, ""[]) == ""); } template IsTypeDerived(A, B) { static if (is(A : B) || is(B == void)) const IsTypeDerived = true; else const IsTypeDerived = false; } unittest { interface Foo{} interface Bar : Foo {} static assert(IsTypeDerived!(Bar, Foo)); static assert(IsTypeDerived!(Foo, Foo)); static assert(IsTypeDerived!(Bar, void)); static assert(IsTypeDerived!(void, void)); static assert(!IsTypeDerived!(Foo, Bar)); } template Nested(T...) { alias T val; } // returns if a.val.length == b.val.length and, for every i in 0..a.val.length, is(a[i] : b[i]) template IsTupleCovariant(alias a, alias b) { static if (a.val.length != b.val.length) const IsTupleCovariant = false; else static if (a.val.length == 0) const IsTupleCovariant = true; else static if (is(a.val[0] : b.val[0])) const IsTupleCovariant = IsTupleCovariant!(Nested!(a.val[1..$]), Nested!(b.val[1..$])); else const IsTupleCovariant = false; } unittest { interface Foo {} interface Bam : Foo {} alias Nested!(Foo, int) a; alias Nested!(Bam, int) b; static assert(IsTupleCovariant!(a, a)); static assert(IsTupleCovariant!(b, a)); static assert(!IsTupleCovariant!(a, b)); } template PrettyFunctionName(string name, T) { const string names = ParameterNames!(ParameterTypeTuple!(T)); string gen() { return formatStringTango("{0} {1}({2})", cast(string)std.traits.ReturnType!(T).stringof, name, names); } } unittest { int run(int, bool) { return 0; } static assert(PrettyFunctionName!("name", typeof(run)).gen() == "int name(int, bool)"); } template ParameterNames(T...) { static if (T.length == 0) const string ParameterNames = ""; else static if (T.length == 1) const string ParameterNames = T[0].stringof; else const string ParameterNames = T[0].stringof ~ ", " ~ ParameterNames!(T[1..$]); } template GetMembers(T) { const string[] GetMembers = RemoveNonFunctions!(T, __traits(allMembers, T)).val; } template RemoveNonFunctions(T, _Names...) { const string[] Names = _Names[0]; static if (Names.length == 0) const string[] val = Names; else { static if (Names[0] == "this" || Names[0] == "_ctor" || !is(typeof(__traits(getMember, T, Names[0])) == function)) const string[] val = RemoveNonFunctions!(T, Names[1..$]).val; else const string[] val = Names[0] ~ RemoveNonFunctions!(T, Names[1..$]).val; } } /+string[] removeStupidMembers(string[] members) { for (size_t i = 0; i < members.length; i++) { if (members[i] == "this" || members[i] == "_ctor") { members = members[0..i] ~ members[i+1..$]; i--; } } return members; }+/ template GetMember(T, uint i) { const string GetMember = GetMembers!(T)[i]; } template GetCounts(BaseType, uint i = 0) { static if (i < (GetMembers!(BaseType).length - 1)) const GetCounts = ([__traits(getVirtualFunctions, BaseType, GetMember!(BaseType, i)).length]) ~ GetCounts!(BaseType, i + 1); else static if (i == (GetMembers!(BaseType).length - 1)) const GetCounts = [ __traits(getVirtualFunctions, BaseType, GetMember!(BaseType, i)).length ] ; else pragma(msg, "Crash!"); // const uint[] GetCounts = [2u, 1, 1, 1]; } unittest { interface Foo2 { int foo(int, bool); int foo(int); string bar(); int func(); } const uint[] counts = GetCounts!(Foo2); const i = counts[2]; } unittest { const x = 5; const y = 7; static assert(mixin(format("{x+1} = 6, {y} = 7")) == "6 = 6, 7 = 7"); } string format(string s) { size_t i = 0; string vars[]; while (true) { while (i+1 < s.length && s[i] != '{') ++i; if (i+1 >= s.length) break; if (s[i+1] == '{') { i += 2; continue; } auto start = i+1; while (i < s.length && s[i] != '}') ++i; if (i == s.length) assert(false, "No closing '}' to match opening '{' in string " ~ s); vars ~= s[start..i]; s = s[0..start] ~ s[i..$]; i = start+2; } string res = "formatStringTango(`" ~ s ~ "`"; foreach (v; vars) res ~= ", " ~ v; res ~= ")"; return res; } string formatStringTango(T...)(string s, T t) { // go through the string and replace {} with {index} int nextIndex = 0; size_t i = 0; while (true) { while (i+1 < s.length && s[i] != '{') ++i; if (i+1 >= s.length) break; if (s[i+1] == '{') { i += 2; continue; } if (s[i+1] == '}') s = s[0..i+1] ~ toString(nextIndex) ~ s[i+1..$]; // now parse the integer int index = cast(int)parseUlong(s[i+1..$]); size_t len = unsignedNumberLength(s[i+1..$]); assert(len > 0 && s.length >= i+1 + len && s[i+1+len] == '}', "invalid format token " ~ s[i..i+len]); nextIndex = index + 1; ++i; } // now go through and replace {index} with toString(t[index]) foreach (index, v; t) { string pattern = '{' ~ toString(index) ~ '}'; string s2 = toString(v); for (i = 0; i <= s.length - pattern.length; i++) if (s[i..i+pattern.length] == pattern) s = s[0..i] ~ s2 ~ s[i+pattern.length..$]; } // now go through and replace {{ with { for (i = 0; i+1 < s.length; i++) { if (s[i..i+2] == "{{") s = s[0..i] ~ '{' ~ s[i+2..$]; } return s; } string formatStringPhobos(T...)(string s, T t) { string val = ""; size_t i = 0; size_t start = 0; foreach (v; t) { while (i+1 < s.length && s[i..i+2] != "%s") { if (s[i..i+2] == "%%") { val ~= s[start..i+1]; start = i+2; i++; } else if (s[i] == '%') assert(false, "unrecognized format " ~ s[i..i+2]); i++; } if (i+1 >= s.length) assert(false, "Too many parameters"); val ~= s[start..i]; val ~= toString(v); start = i + 2; i++; } val ~= s[start..$]; return val; } /** * Convert argument to a string. */ string toString(char c) { return "" ~ c; } /// ditto string toString(string s) { return s; } /// ditto string toString(ulong u) { if (u == 0) return "0"; string val = ""; while (u != 0) { val = cast(char)( (u % 10) + '0' ) ~ val; u /= 10; } return val; } /// ditto string toString(long i) { if (i < 0) return "-" ~ toString( cast(ulong)( -i ) ); return toString(cast(ulong) i); } /// ditto string toString(bool b) { return (b ? "true" : "false"); } private string toString_forward(T)(T t) { static if (__traits(isUnsigned, T)) return toString(cast(ulong)t); else return toString(cast(long)t); } alias toString_forward!(int) toString; alias toString_forward!(uint) toString; alias toString_forward!(byte) toString; alias toString_forward!(ubyte) toString; alias toString_forward!(short) toString; alias toString_forward!(ushort) toString; unittest { static assert(toString(0) == "0"); static assert(toString(1) == "1"); static assert(toString(12345) == "12345"); static assert(toString(1234567890123456) == "1234567890123456"); static assert(toString(-123) == "-123"); static assert(toString(-1234567890123456) == "-1234567890123456"); static assert(toString(true) == "true"); static assert(toString(false) == "false"); static assert(toString("foo") == "foo"); static assert(toString('c') == "c"); static assert(toString(cast(byte)-5) == "-5"); static assert(toString(cast(ubyte)5) == "5"); static assert(toString(cast(short)-5) == "-5"); static assert(toString(cast(ushort)5) == "5"); static assert(toString(cast(int)-5) == "-5"); static assert(toString(cast(uint)5) == "5"); static assert(parseUlong("12345") == 12345); static assert(parseLong("-12345") == -12345); static assert(parseUlong("-12345") == 0); static assert(numberLength("12345") == 5); static assert(numberLength("-12345") == 6); static assert(unsignedNumberLength("12345") == 5); static assert(unsignedNumberLength("-12345") == 0); static assert(numberLength("12a345") == 2); static assert(numberLength("-12a345") == 3); static assert(unsignedNumberLength("12a345") == 2); static assert(unsignedNumberLength("-12a345") == 0); const string s = formatStringPhobos("hel%slo %% %s%s%s%shello", -138, 12345678901, 'a', "foo"[], true); static assert(s == "hel-138lo % 12345678901afootruehello"); const string s2 = formatStringTango("hel{}lo {2}{}{1}{}{0}hello", 0, 1, 2, 3); static assert(s2 == "hel0lo 23120hello"); static assert(formatStringTango("{0} {1}({2})", "a"[], "b"[], "c"[]) == "a b(c)"); } ulong parseUlong(string s) { ulong u = 0; size_t i = 0; while (i < s.length && '0' <= s[i] && s[i] <= '9') { u *= 10; u += s[i] - '0'; ++i; } return u; } long parseLong(string s) { if (s[0] == '-') return ( - cast(long)parseUlong(s[1..$]) ); return cast(long)parseUlong(s); } size_t unsignedNumberLength(string s) { size_t i = 0; while (i < s.length && '0' <= s[i] && s[i] <= '9') ++i; return i; } size_t numberLength(string s) { if (s.length > 0 && s[0] == '-') return 1 + unsignedNumberLength(s[1..$]); return unsignedNumberLength(s); }