digitalmars.D - generic toString() in a template class?
- %u (110/110) Jan 16 2007 I wonder how one could write generic toString() method in a template cla...
- Chris Nicholson-Sauls (24/180) Jan 16 2007 Current D, off the top of my head:
- Frits van Bommel (49/53) Jan 16 2007 [snip]
- %u (42/89) Jan 16 2007 Thank you. I'd prefer this simple solution; static check on T's type lo...
- Bill Baxter (4/13) Jan 16 2007 I'm all for the Smalltalk-ish idea of making built-in types act more
I wonder how one could write generic toString() method in a template class? Suppose, I have a template class S(T), and will pass in as type param: class A, struct B, int C: ==================================== $ cat ts.d class A{} struct B{ char[] toString() {return "B";} } int c; class S(T) { T obj; char[] toString() { return obj.toString(); } } int main(char[][] args) { S!(A) sa; S!(B*) sb; S!(int) sc; printf("%.*s", sa.toString()); printf("%.*s", sb.toString()); printf("%.*s", sc.toString()); return 0; } $ dmd.exe ts.d ts.d(14): Error: no property 'toString' for type 'int' ts.d(14): Error: function expected before (), not 1 of type int ts.d(14): Error: cannot implicitly convert expression (1()) of type int to char[] ts.d(22): template instance ts.S!(int) error instantiating ==================================== OK, no property 'toString' for type 'int'; let's use some trick to do function overloading: ==================================== $ cat ts.d import std.string; class A{} struct B{ char[] toString() {return "B";} } int c; char[] toStringFunc(Object obj) {return obj.toString();} char[] toStringFunc(int i) {return format(i);} class S(T) { T obj; char[] toString() { return toStringFunc(obj); } } int main(char[][] args) { S!(A) sa; S!(B*) sb; S!(int) sc; printf("%.*s", sa.toString()); printf("%.*s", sb.toString()); printf("%.*s", sc.toString()); return 0; } $ dmd.exe ts.d ts.d(18): function ts.toStringFunc (Object) does not match parameter types (B *) ts.d(18): Error: cannot implicitly convert expression (this.obj) of type B * to int ts.d(25): template instance ts.S!(B *) error instantiating ==================================== OK, struct is not Object, so let's just add char[] toStringFunc(void* obj) {return "null";} ==================================== $ cat ts.d import std.string; class A{} struct B{ char[] toString() {return "B";} } int c; char[] toStringFunc(Object obj) {return obj.toString();} char[] toStringFunc(int i) {return format(i);} char[] toStringFunc(void* obj) {return "null";} class S(T) { T obj; char[] toString() { return toStringFunc(obj); } } int main(char[][] args) { S!(A) sa; S!(B*) sb; S!(int) sc; printf("%.*s", sa.toString()); printf("%.*s", sb.toString()); printf("%.*s", sc.toString()); return 0; } $ dmd.exe ts.d ts.d(19): function ts.toStringFunc called with argument types: (A) matches both: ts.toStringFunc(Object) and: ts.toStringFunc(void*) ts.d(25): template instance ts.S!(A) error instantiating ==================================== Come on! class A matches both (Object) and (void*)?! How could one write a generic toString() method in a template class then? My suggestions: -- if the compiler see a basic type, int.toString(), translate into a function that does the Right Thing; just as if .toString() is also a property of basic types like int.max, int.min. -- class A, matches (Object) better than (void*), so just do the Right thing to choose overloaded-function(Object obj). comments?
Jan 16 2007
%u wrote:I wonder how one could write generic toString() method in a template class? Suppose, I have a template class S(T), and will pass in as type param: class A, struct B, int C: ==================================== $ cat ts.d class A{} struct B{ char[] toString() {return "B";} } int c; class S(T) { T obj; char[] toString() { return obj.toString(); } } int main(char[][] args) { S!(A) sa; S!(B*) sb; S!(int) sc; printf("%.*s", sa.toString()); printf("%.*s", sb.toString()); printf("%.*s", sc.toString()); return 0; } $ dmd.exe ts.d ts.d(14): Error: no property 'toString' for type 'int' ts.d(14): Error: function expected before (), not 1 of type int ts.d(14): Error: cannot implicitly convert expression (1()) of type int to char[] ts.d(22): template instance ts.S!(int) error instantiating ==================================== OK, no property 'toString' for type 'int'; let's use some trick to do function overloading: ==================================== $ cat ts.d import std.string; class A{} struct B{ char[] toString() {return "B";} } int c; char[] toStringFunc(Object obj) {return obj.toString();} char[] toStringFunc(int i) {return format(i);} class S(T) { T obj; char[] toString() { return toStringFunc(obj); } } int main(char[][] args) { S!(A) sa; S!(B*) sb; S!(int) sc; printf("%.*s", sa.toString()); printf("%.*s", sb.toString()); printf("%.*s", sc.toString()); return 0; } $ dmd.exe ts.d ts.d(18): function ts.toStringFunc (Object) does not match parameter types (B *) ts.d(18): Error: cannot implicitly convert expression (this.obj) of type B * to int ts.d(25): template instance ts.S!(B *) error instantiating ==================================== OK, struct is not Object, so let's just add char[] toStringFunc(void* obj) {return "null";} ==================================== $ cat ts.d import std.string; class A{} struct B{ char[] toString() {return "B";} } int c; char[] toStringFunc(Object obj) {return obj.toString();} char[] toStringFunc(int i) {return format(i);} char[] toStringFunc(void* obj) {return "null";} class S(T) { T obj; char[] toString() { return toStringFunc(obj); } } int main(char[][] args) { S!(A) sa; S!(B*) sb; S!(int) sc; printf("%.*s", sa.toString()); printf("%.*s", sb.toString()); printf("%.*s", sc.toString()); return 0; } $ dmd.exe ts.d ts.d(19): function ts.toStringFunc called with argument types: (A) matches both: ts.toStringFunc(Object) and: ts.toStringFunc(void*) ts.d(25): template instance ts.S!(A) error instantiating ==================================== Come on! class A matches both (Object) and (void*)?! How could one write a generic toString() method in a template class then? My suggestions: -- if the compiler see a basic type, int.toString(), translate into a function that does the Right Thing; just as if .toString() is also a property of basic types like int.max, int.min. -- class A, matches (Object) better than (void*), so just do the Right thing to choose overloaded-function(Object obj). comments?Current D, off the top of my head: char[] toString()"); -- Chris Nicholson-Sauls
Jan 16 2007
%u wrote:I wonder how one could write generic toString() method in a template class? Suppose, I have a template class S(T), and will pass in as type param: class A, struct B, int C:[snip] How about this one: ----- char[] toString() { return std.string.format("%s", obj); } ----- Works for most types you're likely to want formatted, as long as you don't mind how it formats them. It doesn't support function pointers and delegates, nor will it probably like structs without toString() defined, but other than that I think it supports everything. If you want something a bit more customizable, try something like this: (Chris beat me to posting the general idea though) ----- import std.string; // for .toString and format import std.utf; // for toUTF8 struct S(T) { T obj; char[] toString() { static if(is(typeof(obj.toString()) : char[])) // structs with toString & objects { return obj ? obj.toString() : "null-obj"; } else static if(is(typeof(obj.toUTF8()) : char[])) // char[], wchar[] & dchar[] { return obj.toUTF8(); } else static if (is(T : void*)) // pointers { return obj ? format(obj) : "null-ptr"; } else static if (is(typeof(std.string.toString(obj)) : char[])) // anything supported by std.string.toString { return std.string.toString(obj); } else { version(ReportDefaultFormatting) pragma(msg, "Default formatting for " ~ T.mangleof); return format("%s", obj); } } } -----
Jan 16 2007
== Quote from Frits van Bommel (fvbommel REMwOVExCAPSs.nl)'s articleHow about this one: ----- char[] toString() { return std.string.format("%s", obj); } ----- Works for most types you're likely to want formatted, as long as you don't mind how it formats them.Thank you. I'd prefer this simple solution; static check on T's type looks too messy to me. finally: =================================== $ cat ts.d import std.string; class A {char[] toString() {return "A";}} struct B {char[] toString() {return "B";}} class S(T) { T obj; this(T o) {obj = o;} char[] toString() {return std.string.format("S!%s", obj);} } int main(char[][] args) { A a = new A(); B b; int c = 911; S!(A) sa = new S!( A )( a); S!(S!(A)) ssa = new S!(S!(A))(sa); S!(B*) sbn = new S!(B*)(null); S!(B*) sb = new S!(B*)(&b); S!(B ) ssb = new S!(B )( b); S!(int) sc = new S!(int)(c); printf("%.*s\n", sa.toString()); printf("%.*s\n", ssa.toString()); printf("%.*s\n", sbn.toString()); printf("%.*s\n", sb.toString()); printf("%.*s\n", ssb.toString()); printf("%.*s\n", sc.toString()); return 0; } =================================== $ dmd.exe ts.d g:\project\dmd\bin\..\..\dm\bin\link.exe ts,,,user32+kernel32/noi; $ ./ts.exe S!A S!S!A S!0000 S!12FF18 S!B S!911 ===================================It doesn't support function pointers and delegates, nor will it probably like structs without toString() defined, but other than that I think it supports everything. If you want something a bit more customizable, try something like this: (Chris beat me to posting the general idea though) ----- import std.string; // for .toString and format import std.utf; // for toUTF8 struct S(T) { T obj; char[] toString() { static if(is(typeof(obj.toString()) : char[])) // structs with toString & objects { return obj ? obj.toString() : "null-obj"; } else static if(is(typeof(obj.toUTF8()) : char[])) // char[], wchar[] & dchar[] { return obj.toUTF8(); } else static if (is(T : void*)) // pointers { return obj ? format(obj) : "null-ptr"; } else static if (is(typeof(std.string.toString(obj)) : char[])) // anything supported by std.string.toString { return std.string.toString(obj); } else { version(ReportDefaultFormatting) pragma(msg, "Default formatting for " ~ T.mangleof); return format("%s", obj); } } } -----
Jan 16 2007
%u wrote:I wonder how one could write generic toString() method in a template class? [...] My suggestions: -- if the compiler see a basic type, int.toString(), translate into a function that does the Right Thing; just as if .toString() is also a property of basic types like int.max, int.min.I'm all for the Smalltalk-ish idea of making built-in types act more like full-fledged objects. --bb
Jan 16 2007