www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - opDispatch shadowing toString - feature or bug?

reply Damian Ziemba <nazriel driv.pl> writes:
Greetings.

I've been playing around with opDispatch and toString methods and I found 
strange behavior.

Example:

	import std.stdio;

	struct Test
	{
		string opDispatch( string key )()
		{
			return "I am dispatching in struct!";
		}

		string toString()
		{
			return "I am Test struct!";
		}
	}

	class Test2
	{
		string opDispatch( string key )()
		{
			return "I am dispatching in class!";
		}

		string toString()
		{
			return "I am Test class!";
		}
	}

	void main()
	{
		Test test = Test();
		writeln ( test.s ); // I am dispatching in struct!
		writeln ( test.s() ); // I am dispatching in struct!
		writeln ( test ); // NOTHING :( But should return "I am 
Test struct!"

		Test2 test2 = new Test2();
		writeln ( test2.s ); // I am dispatching in class!
		writeln ( test2.s() ); // I am dispatching in class!
		writeln ( test2 ); // NOTHING :( But should return "I am 
Test class!"
	}

Is it a feature or a bug?

Best regards,
Damian Ziemba
Sep 01 2011
next sibling parent reply travert phare.normalesup.org (Christophe) writes:
Try to create the method:

const void toString(void delegate(const(char)[]) sink, string formatString)
{
  sink(toString());
}
Sep 01 2011
parent Damian Ziemba <nazriel driv.pl> writes:
On Thu, 01 Sep 2011 07:56:49 +0000, Christophe wrote:

 Try to create the method:
 
 const void toString(void delegate(const(char)[]) sink, string
 formatString) {
   sink(toString());
 }
Adding it as it is results in compiler error: ./quick.d(30): Error: function quick.Test2.toString () is not c allable using argument types () const ./quick.d(30): Error: expected 2 function arguments, not 0 ./quick.d(30): Error: cannot implicitly convert expression (this.toString()) of type void to const(char)[] Removing const from begging allows it to compile. But it doesn't change anything. Still "nothing" appears ;-)
Sep 01 2011
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 09/01/2011 09:34 AM, Damian Ziemba wrote:
 Greetings.

 I've been playing around with opDispatch and toString methods and I found
 strange behavior.

 Example:

 	import std.stdio;

 	struct Test
 	{
 		string opDispatch( string key )()
 		{
 			return "I am dispatching in struct!";
 		}

 		string toString()
 		{
 			return "I am Test struct!";
 		}
 	}

 	class Test2
 	{
 		string opDispatch( string key )()
 		{
 			return "I am dispatching in class!";
 		}

 		string toString()
 		{
 			return "I am Test class!";
 		}
 	}

 	void main()
 	{
 		Test test = Test();
 		writeln ( test.s ); // I am dispatching in struct!
 		writeln ( test.s() ); // I am dispatching in struct!
 		writeln ( test ); // NOTHING :( But should return "I am
 Test struct!"

 		Test2 test2 = new Test2();
 		writeln ( test2.s ); // I am dispatching in class!
 		writeln ( test2.s() ); // I am dispatching in class!
 		writeln ( test2 ); // NOTHING :( But should return "I am
 Test class!"
 	}

 Is it a feature or a bug?

 Best regards,
 Damian Ziemba
static assert(isInputRange!Test); static assert(isInputRange!Test2); toString is not shadowed, but the implementation of writeln assumes that your types are an InputRange (they provide, by the means of opDispatch, front(), empty() and popFront()) The fact that writeln([]); prints a new line instead of "[]" is a bug that has already been taken care of in a pull request afaik. This specific problem can be solved by making your types not follow the InputRange interface, by putting an appropriate constraint on your opDispatch. import std.stdio; struct Test { string opDispatch( string key )() if(key!="popFront") { return "I am dispatching in struct!"; } string toString() { return "I am Test struct!"; } } class Test2 { string opDispatch( string key )() if(key!="popFront") { return "I am dispatching in class!"; } string toString() { return "I am Test class!"; } } void main() { Test test = Test(); writeln ( test.s ); // I am dispatching in struct! writeln ( test.s() ); // I am dispatching in struct! writeln ( test ); //I am Test struct! Test2 test2 = new Test2(); writeln ( test2.s ); // I am dispatching in class! writeln ( test2.s() ); // I am dispatching in class! writeln ( test2 ); // I am Test class! }
Sep 01 2011
parent reply Damian Ziemba <nazriel driv.pl> writes:
On Thu, 01 Sep 2011 13:59:29 +0200, Timon Gehr wrote:

 static assert(isInputRange!Test);
 static assert(isInputRange!Test2);
 
 toString is not shadowed, but the implementation of writeln assumes that
 your types are an InputRange (they provide, by the means of opDispatch,
 front(), empty() and popFront())
 
 The fact that writeln([]); prints a new line instead of "[]" is a bug
 that has already been taken care of in a pull request afaik.
 
 This specific problem can be solved by making your types not follow the
 InputRange interface, by putting an appropriate constraint on your
 opDispatch.
 
 
 import std.stdio;
 
 struct Test
 {
 	string opDispatch( string key )() if(key!="popFront") {
 		return "I am dispatching in struct!";
 	}
 
 	string toString()
 	{
 		return "I am Test struct!";
 	}
 }
 
 class Test2
 {
 	string opDispatch( string key )() if(key!="popFront") {
 		return "I am dispatching in class!";
 	}
 
 	string toString()
 	{
 		return "I am Test class!";
 	}
 }
 
 void main()
 {
 	Test test = Test();
 	writeln ( test.s ); // I am dispatching in struct! writeln 
( test.s()
 	); // I am dispatching in struct! writeln ( test ); //I am Test 
struct!
 	
 	Test2 test2 = new Test2();
 	writeln ( test2.s ); // I am dispatching in class! writeln 
( test2.s()
 	); // I am dispatching in class! writeln ( test2 ); // I am Test 
class!
 }
Yes, this fix the problem. Hmm, after all its a bit loose of a keyword, becouse I can't use anymore test.popFront. For example if class acts as a storage device class Storage { string[ string ] vars; string opDispatch( string key )() if ( key != "popFront" ) { if ( key in vars ) return vars[ key ]; else return ""; } string toString() { // implement me } } auto storage = new Storage; storage.vars["test"] = "I'm a test!"; storage.vars["popFront"] = "I'm a poping around! :D"; writeln( storage.test ); // I'm a test! writeln( storage.popFront ); // error Ofcours, in opDispatch I can use empty or front instead of popFront but it is still loose of one keyword. Looks like it's a loose I have to take :-) Thank you very much for reply! Best regards, Damian Ziemba
Sep 01 2011
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 09/02/2011 12:09 AM, Damian Ziemba wrote:
 On Thu, 01 Sep 2011 13:59:29 +0200, Timon Gehr wrote:

 static assert(isInputRange!Test);
 static assert(isInputRange!Test2);

 toString is not shadowed, but the implementation of writeln assumes that
 your types are an InputRange (they provide, by the means of opDispatch,
 front(), empty() and popFront())

 The fact that writeln([]); prints a new line instead of "[]" is a bug
 that has already been taken care of in a pull request afaik.

 This specific problem can be solved by making your types not follow the
 InputRange interface, by putting an appropriate constraint on your
 opDispatch.


 import std.stdio;

 struct Test
 {
 	string opDispatch( string key )() if(key!="popFront") {
 		return "I am dispatching in struct!";
 	}

 	string toString()
 	{
 		return "I am Test struct!";
 	}
 }

 class Test2
 {
 	string opDispatch( string key )() if(key!="popFront") {
 		return "I am dispatching in class!";
 	}

 	string toString()
 	{
 		return "I am Test class!";
 	}
 }

 void main()
 {
 	Test test = Test();
 	writeln ( test.s ); // I am dispatching in struct! writeln
( test.s()
 	); // I am dispatching in struct! writeln ( test ); //I am Test
struct!
 	
 	Test2 test2 = new Test2();
 	writeln ( test2.s ); // I am dispatching in class! writeln
( test2.s()
 	); // I am dispatching in class! writeln ( test2 ); // I am Test
class!
 }
Yes, this fix the problem. Hmm, after all its a bit loose of a keyword, becouse I can't use anymore test.popFront. For example if class acts as a storage device class Storage { string[ string ] vars; string opDispatch( string key )() if ( key != "popFront" ) { if ( key in vars ) return vars[ key ]; else return ""; } string toString() { // implement me } } auto storage = new Storage; storage.vars["test"] = "I'm a test!"; storage.vars["popFront"] = "I'm a poping around! :D"; writeln( storage.test ); // I'm a test! writeln( storage.popFront ); // error Ofcours, in opDispatch I can use empty or front instead of popFront but it is still loose of one keyword. Looks like it's a loose I have to take :-) Thank you very much for reply! Best regards, Damian Ziemba
template isInputRange(R) { enum bool isInputRange = is(typeof( { R r; // can define a range object if (r.empty) {} // can test for empty r.popFront(); // can invoke popFront() auto h = r.front; // can get the front of the range }())); } With the next DMD release, you could use a struct with a disabled default constructor. Because it cannot be defined, the first line will fail, and isInputRange will be false. Basically, anything that will make R r; // can define a range object if (r.empty) {} // can test for empty r.popFront(); // can invoke popFront() auto h = r.front; // can get the front of the range fail to compile is good enough. Eg, if r.empty is not convertible to bool.
Sep 01 2011