digitalmars.D - generic toString() in a template class?
- %u <no where.com> Jan 16 2007
- Chris Nicholson-Sauls <ibisbasenji gmail.com> Jan 16 2007
- Frits van Bommel <fvbommel REMwOVExCAPSs.nl> Jan 16 2007
- %u <no where.com> Jan 16 2007
- Bill Baxter <dnewsgroup billbaxter.com> Jan 16 2007
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: # class S (T) { # T obj ; # # char[] toString () { # static if (is(T == class)) { # return obj.toString; # } # else static if (is(T == struct)) { # static if (is(typeof(T.toString() == char[]))) # return obj.toString; # } # else { # static assert (false, "class S!(T): struct T must expose function char[] toString()"); # } # } # else { # std.string.toString(obj); # } # } # } -- 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:
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









Chris Nicholson-Sauls <ibisbasenji gmail.com> 