www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Objective-D, reflective programming, dynamic typing

reply Eljay <eljay adobe.com> writes:
/*

Although D 2.0 is a multi-paradigm programming language, it does not support
two paradigms:
+ reflective programming
+ dynamic typing

Since D 2.0 does not have support for reflective programming, nor support for
dynamic typing, I thought "What facilities does D 2.0 have which could be used
to mimic reflective programming and dynamic typing?"

(Unless I'm mistaken, and D 2.0 does support these paradigms.  Andrei and Walter
have been very busy!  In which case... oops.)

This toy program is the result.

This toy program does not try to achieve efficient performance.

Id - root class for all dynamic types.
Msg - a string:string associative array.
	TODO. Need to change to string:Id dictionary, but that's for later.
	TODO. Need to have Id wrapper for string, and other primitives.
Sel - the selector, which is the " " key bound to the selector string in the
string dictionary.

What is lacking?
- Should be able to add message responders to an object.
  What does this mean?
  Create an object.
  Insert a message responder.
  Send that message, and notice that the object now performs that message.
  Only affect that instantiated object, not the whole class.

- Should be able to add message responders to a class.
  What does this mean?
  If someone has a black box class called MyString, you should be able to inject
  a brand new message responder into that class which affects ALL instances of
  MyString.
  Upshot: you can inject new message handlers to an existing class, which is not
  a class to which you have the source.

- Should be able to override message responders to a class, and inject your own
  message responder.
  What does this mean?
  Let's say you want to do some "first grade debugging" on a class, you should
  be able to programmatically get the message responder of a class (even a class
  to which you do not have the source),
  save that message responder, inject your own diagnostic message responder into
  a class (or into a single instantiated object), output the diagnostics than
  invoke the original message responder.

- Classes themsleves should be singleton (or monostate) objects, which are
  object instantiation factories.
  Objective-C designates class message responders and object message responders
  by (note the leading '+' and '-'):
  + (void)IAmAClassMessageResponder(void)
  - (void)IAmAnObjectMessageReponder(void)

Just as a programmer can do object-oriented programming in pure C, even though
the C programming language provides no language support for object-oriented
programming, so too can you do reflective programming in D.  Even though D does
not provide any dynamic typing and reflecting programming support in the D core
language.

The only thing that is required to do reflective programming and have dynamic
typing in D is superhuman discipline, and adhering meticulously to a rigid
programming convention and never ever make a mistake.

*/

import std.stdio;
import std.conv;

alias string[string] Msg;

/*
** This is the ultimate root of all reflective objects.
** Wouldn't it be nice if this were class object?
*/
class Id
{
	void perform(in Msg msg)
	{
		switch(msg[" "])
		{
			default:
			{
				writeln("Unable to perform \"", msg[" "], "\"");
			}
			break;
		}
	}
};

/*
** This is an example of a useful object which does something interesting.
*/
class IdFoo : Id
{
	void perform(in Msg msg)
	{
		switch(msg[" "])
		{
			case "add":
			{
				performAdd(msg);
			}
			break;

			case "sub":
			{
				performSub(msg);
			}
			break;

			case "mul":
			{
				performMul(msg);
			}
			break;

			case "div":
			{
				performDiv(msg);
			}
			break;

			default:
			{
				super.perform(msg);
			}
			break;
		}
	}

	void performAdd(in Msg msg)
	{
		double a = toDouble(msg["a"]);
		double b = toDouble(msg["b"]);
		writeln(a, " + ", b, " = ", (a + b));
	}

	void performSub(in Msg msg)
	{
		double a = toDouble(msg["a"]);
		double b = toDouble(msg["b"]);
		writeln(a, " - ", b, " = ", (a - b));
	}

	void performMul(in Msg msg)
	{
		double a = toDouble(msg["a"]);
		double b = toDouble(msg["b"]);
		writeln(a, " * ", b, " = ", (a * b));
	}

	void performDiv(in Msg msg)
	{
		double a = toDouble(msg["a"]);
		double b = toDouble(msg["b"]);
		writeln(a, " / ", b, " = ", (a / b));
	}
}

/*
** This is an example of a null object.
** Imagine that the null object is a noisy debugging object, used to substitute
** in for an object which has been destroyed.
*/
class IdNull : Id
{
	void perform(in Msg msg)
	{
		switch(msg[" "])
		{
			default:
			{
				performDiscard(msg);
			}
			break;
		}
	}

	void performDiscard(in Msg msg)
	{
		writeln("Discard ", msg[" "]);
	}
}

/*
** This is an example of a proxy object, which forwards message to an object
** elsewhere.
**
** Imagine that the other object exists on another computer, and this forwarding
** object sends messages to the remote object over an IP/TCP connection.
*/
class IdForward : Id
{
	void perform(in Msg msg)
	{
		switch(msg[" "])
		{
			default:
			{
				performForward(msg);
			}
			break;
		}
	}

	void performForward(in Msg msg)
	{
		writeln("Proxy object forwarding message ", msg[" "]);
		forwardObj.perform(msg);
	}

	this(Id obj)
	{
		forwardObj = obj;
	}

	Id forwardObj;
}

void Exercise(Id obj)
{
	Msg msg;

	// [obj addWithA:5 withB:3]     -- Objective-C
	// obj(add a:5 b:3)             -- Pseudo-D ... perhaps?
	// obj(add a:"5" b:"3")            closer to what we are actually doing
	//                                 but not what's ultimately desired.
	msg[" "] = "add"; // Just using "add", but could have been "addWithA:withB:"
	msg["a"] = "5";
	msg["b"] = "3";
	obj.perform(msg);

	// [obj subWithA:5 withB:3]
	// obj(sub a:5 b:3)
	msg[" "] = "sub";
	obj.perform(msg);

	// [obj mulWithA:5 withB:3]
	// obj(mul a:5 b:3)
	msg[" "] = "mul";
	obj.perform(msg);

	// [obj divWithA:5 withB:3]
	// obj(div a:5 b:3)
	msg[" "] = "div";
	obj.perform(msg);

	// [obj fooWithA:5 withB:3]
	// obj(foo a:5 b:3)
	msg[" "] = "foo";
	obj.perform(msg);
}

void main()
{
	Id obj = new IdFoo;
	writeln(">>> Exercise IdFoo");
	Exercise(obj);

	obj = new IdNull;
	writeln("\n>>> Exercise IdNull");
	Exercise(obj);

	obj = new IdForward(new IdFoo);
	writeln("\n>>> Exercise IdForward(IdFoo)");
	Exercise(obj);

	writeln("---done---");
}
Apr 02 2009
next sibling parent reply Eljay <eljay adobe.com> writes:
Note that the toy program is just an example, and has a lot of room for
improvement.

Ultimately, I would love to have D support reflective programming and dynamic
typing in the core language.

Walter and Andrei have been very gracious, and I am amazed and pleased with how
D 2.0 is shaping up.  There is plenty on the table for D 2.0 without having the
added burden of reflective programming and dynamic typing.

So I do not expect that to happen for D 2.0.  Maybe for D 3.0.  And that is
assuming that there is sufficient value to support the paradigms (which I think
there is).

In the meantime, maybe a hand-rolled user implementation on top of D 2.0.

Food for thought.
Apr 02 2009
parent =?UTF-8?B?QWxleGFuZGVyIFDDoW5law==?= writes:
Eljay wrote:
 Ultimately, I would love to have D support reflective programming and dynamic
typing in the core language.

I agree on the reflection part, just.. the dynamic typing part sounds very very very unlikely. D has a static type system for a reason, I highly doubt this is going to change. Also, would you mind summing up what you want to do in a few lines? I don't have the time to go through text worth 4 A4 pages - and I'm sure I'm not the only one. :) Kind regards, Alex
Apr 02 2009
prev sibling next sibling parent reply "Robert Jacques" <sandford jhu.edu> writes:
On Thu, 02 Apr 2009 11:20:13 -0400, Eljay <eljay adobe.com> wrote:
 Although D 2.0 is a multi-paradigm programming language, it does not  
 support
 two paradigms:
 + dynamic typing

std.variant is a good example of a dynamic typing wrapper for value types.
 + reflective programming

By reflective programming it looks like you mean Properties or Prototype Design ala Java Script/Lua/MiniD/etc. (http://en.wikipedia.org/wiki/Prototype-based_programming_language) With D2.0's compile-time reflection you should be able to implement this very cleanly. Steve Yegge has a nice blog on the subject (http://steve-yegge.blogspot.com/2008/10/universal-design-pattern.html) which mentions several performance optimizations which are applicable to D's associative arrays.
Apr 02 2009
next sibling parent =?UTF-8?B?QWxleGFuZGVyIFDDoW5law==?= writes:
Jarrett Billingsley wrote:
 On Thu, Apr 2, 2009 at 2:14 PM, Robert Jacques <sandford jhu.edu> wrote:
 On Thu, 02 Apr 2009 11:20:13 -0400, Eljay <eljay adobe.com> wrote:
 Although D 2.0 is a multi-paradigm programming language, it does not
 support
 two paradigms:
 + dynamic typing

 + reflective programming

Design ala Java Script/Lua/MiniD/etc. (http://en.wikipedia.org/wiki/Prototype-based_programming_language) With D2.0's compile-time reflection you should be able to implement this very cleanly. Steve Yegge has a nice blog on the subject (http://steve-yegge.blogspot.com/2008/10/universal-design-pattern.html) which mentions several performance optimizations which are applicable to D's associative arrays.

Thanks for the link to that article. MiniD no longer uses a "pure" prototype-based object system, but it is still very closely related and faces the same performance issues. Most of the things he mentions I've already implemented, but freezing an object's fields is something I haven't.. frozen namespaces would also be incredibly useful for sandboxed environments. Hm.

Yes, please!
Apr 02 2009
prev sibling parent Eljay <eljay adobe.com> writes:
Hi Robert,

Thanks for the tip on std.variant, and the reference URLs.  I am a fan of Lua,
haven't heard of MiniD.

Sincerely,
--Eljay
Apr 03 2009
prev sibling next sibling parent Jarrett Billingsley <jarrett.billingsley gmail.com> writes:
On Thu, Apr 2, 2009 at 2:14 PM, Robert Jacques <sandford jhu.edu> wrote:
 On Thu, 02 Apr 2009 11:20:13 -0400, Eljay <eljay adobe.com> wrote:
 Although D 2.0 is a multi-paradigm programming language, it does not
 support
 two paradigms:
 + dynamic typing

std.variant is a good example of a dynamic typing wrapper for value types.
 + reflective programming

By reflective programming it looks like you mean Properties or Prototype Design ala Java Script/Lua/MiniD/etc. (http://en.wikipedia.org/wiki/Prototype-based_programming_language) With D2.0's compile-time reflection you should be able to implement this very cleanly. Steve Yegge has a nice blog on the subject (http://steve-yegge.blogspot.com/2008/10/universal-design-pattern.html) which mentions several performance optimizations which are applicable to D's associative arrays.

Thanks for the link to that article. MiniD no longer uses a "pure" prototype-based object system, but it is still very closely related and faces the same performance issues. Most of the things he mentions I've already implemented, but freezing an object's fields is something I haven't.. frozen namespaces would also be incredibly useful for sandboxed environments. Hm.
Apr 02 2009
prev sibling parent reply Steve Teale <steve.teale britseyeview.com> writes:
Eljay Wrote:

 /*
 
 Although D 2.0 is a multi-paradigm programming language, it does not support
 two paradigms:
 + reflective programming
 + dynamic typing
 
 Since D 2.0 does not have support for reflective programming, nor support for
 dynamic typing, I thought "What facilities does D 2.0 have which could be used
 to mimic reflective programming and dynamic typing?"
 
 (Unless I'm mistaken, and D 2.0 does support these paradigms.  Andrei and
Walter
 have been very busy!  In which case... oops.)
 
 This toy program is the result.
 
 This toy program does not try to achieve efficient performance.
 
 Id - root class for all dynamic types.
 Msg - a string:string associative array.
 	TODO. Need to change to string:Id dictionary, but that's for later.
 	TODO. Need to have Id wrapper for string, and other primitives.
 Sel - the selector, which is the " " key bound to the selector string in the
 string dictionary.
 
 What is lacking?
 - Should be able to add message responders to an object.
   What does this mean?
   Create an object.
   Insert a message responder.
   Send that message, and notice that the object now performs that message.
   Only affect that instantiated object, not the whole class.
 
 - Should be able to add message responders to a class.
   What does this mean?
   If someone has a black box class called MyString, you should be able to
inject
   a brand new message responder into that class which affects ALL instances of
   MyString.
   Upshot: you can inject new message handlers to an existing class, which is
not
   a class to which you have the source.
 
 - Should be able to override message responders to a class, and inject your own
   message responder.
   What does this mean?
   Let's say you want to do some "first grade debugging" on a class, you should
   be able to programmatically get the message responder of a class (even a
class
   to which you do not have the source),
   save that message responder, inject your own diagnostic message responder
into
   a class (or into a single instantiated object), output the diagnostics than
   invoke the original message responder.
 
 - Classes themsleves should be singleton (or monostate) objects, which are
   object instantiation factories.
   Objective-C designates class message responders and object message responders
   by (note the leading '+' and '-'):
   + (void)IAmAClassMessageResponder(void)
   - (void)IAmAnObjectMessageReponder(void)
 
 Just as a programmer can do object-oriented programming in pure C, even though
 the C programming language provides no language support for object-oriented
 programming, so too can you do reflective programming in D.  Even though D does
 not provide any dynamic typing and reflecting programming support in the D core
 language.
 
 The only thing that is required to do reflective programming and have dynamic
 typing in D is superhuman discipline, and adhering meticulously to a rigid
 programming convention and never ever make a mistake.
 
 */
 
 import std.stdio;
 import std.conv;
 
 alias string[string] Msg;
 
 /*
 ** This is the ultimate root of all reflective objects.
 ** Wouldn't it be nice if this were class object?
 */
 class Id
 {
 	void perform(in Msg msg)
 	{
 		switch(msg[" "])
 		{
 			default:
 			{
 				writeln("Unable to perform \"", msg[" "], "\"");
 			}
 			break;
 		}
 	}
 };
 
 /*
 ** This is an example of a useful object which does something interesting.
 */
 class IdFoo : Id
 {
 	void perform(in Msg msg)
 	{
 		switch(msg[" "])
 		{
 			case "add":
 			{
 				performAdd(msg);
 			}
 			break;
 
 			case "sub":
 			{
 				performSub(msg);
 			}
 			break;
 
 			case "mul":
 			{
 				performMul(msg);
 			}
 			break;
 
 			case "div":
 			{
 				performDiv(msg);
 			}
 			break;
 
 			default:
 			{
 				super.perform(msg);
 			}
 			break;
 		}
 	}
 
 	void performAdd(in Msg msg)
 	{
 		double a = toDouble(msg["a"]);
 		double b = toDouble(msg["b"]);
 		writeln(a, " + ", b, " = ", (a + b));
 	}
 
 	void performSub(in Msg msg)
 	{
 		double a = toDouble(msg["a"]);
 		double b = toDouble(msg["b"]);
 		writeln(a, " - ", b, " = ", (a - b));
 	}
 
 	void performMul(in Msg msg)
 	{
 		double a = toDouble(msg["a"]);
 		double b = toDouble(msg["b"]);
 		writeln(a, " * ", b, " = ", (a * b));
 	}
 
 	void performDiv(in Msg msg)
 	{
 		double a = toDouble(msg["a"]);
 		double b = toDouble(msg["b"]);
 		writeln(a, " / ", b, " = ", (a / b));
 	}
 }
 
 /*
 ** This is an example of a null object.
 ** Imagine that the null object is a noisy debugging object, used to substitute
 ** in for an object which has been destroyed.
 */
 class IdNull : Id
 {
 	void perform(in Msg msg)
 	{
 		switch(msg[" "])
 		{
 			default:
 			{
 				performDiscard(msg);
 			}
 			break;
 		}
 	}
 
 	void performDiscard(in Msg msg)
 	{
 		writeln("Discard ", msg[" "]);
 	}
 }
 
 /*
 ** This is an example of a proxy object, which forwards message to an object
 ** elsewhere.
 **
 ** Imagine that the other object exists on another computer, and this
forwarding
 ** object sends messages to the remote object over an IP/TCP connection.
 */
 class IdForward : Id
 {
 	void perform(in Msg msg)
 	{
 		switch(msg[" "])
 		{
 			default:
 			{
 				performForward(msg);
 			}
 			break;
 		}
 	}
 
 	void performForward(in Msg msg)
 	{
 		writeln("Proxy object forwarding message ", msg[" "]);
 		forwardObj.perform(msg);
 	}
 
 	this(Id obj)
 	{
 		forwardObj = obj;
 	}
 
 	Id forwardObj;
 }
 
 void Exercise(Id obj)
 {
 	Msg msg;
 
 	// [obj addWithA:5 withB:3]     -- Objective-C
 	// obj(add a:5 b:3)             -- Pseudo-D ... perhaps?
 	// obj(add a:"5" b:"3")            closer to what we are actually doing
 	//                                 but not what's ultimately desired.
 	msg[" "] = "add"; // Just using "add", but could have been "addWithA:withB:"
 	msg["a"] = "5";
 	msg["b"] = "3";
 	obj.perform(msg);
 
 	// [obj subWithA:5 withB:3]
 	// obj(sub a:5 b:3)
 	msg[" "] = "sub";
 	obj.perform(msg);
 
 	// [obj mulWithA:5 withB:3]
 	// obj(mul a:5 b:3)
 	msg[" "] = "mul";
 	obj.perform(msg);
 
 	// [obj divWithA:5 withB:3]
 	// obj(div a:5 b:3)
 	msg[" "] = "div";
 	obj.perform(msg);
 
 	// [obj fooWithA:5 withB:3]
 	// obj(foo a:5 b:3)
 	msg[" "] = "foo";
 	obj.perform(msg);
 }
 
 void main()
 {
 	Id obj = new IdFoo;
 	writeln(">>> Exercise IdFoo");
 	Exercise(obj);
 
 	obj = new IdNull;
 	writeln("\n>>> Exercise IdNull");
 	Exercise(obj);
 
 	obj = new IdForward(new IdFoo);
 	writeln("\n>>> Exercise IdForward(IdFoo)");
 	Exercise(obj);
 
 	writeln("---done---");
 }
 
 

Apr 02 2009
next sibling parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2009-04-02 14:32:48 -0400, Steve Teale <steve.teale britseyeview.com> said:

 Could you please give a three or four line example of what it is you 
 want to achieve.

What Eljay wants to be able to do is this: 1. dynamically add message responders to dynamic objects 2. dynamically add message responders to dynamic classes, which would applies 3. capability to add override existing message responders 4. he want classes to be represented by singleton object factories used to instanciate instances Basically, he want to reimplement Objective-C in D, or so it looks like (although Objective-C doesn't have 1), unless I'm missreading his intent. In his example he creates a base class called "Id", which is the root for dynamic objects and has a "perform" method which is called whenever you want to pass a message. At that point he uses a switch statement in the derived class to decide how to handle the message. Since he said earlier that the idea was to use a hash table (to make things extendable at runtime), I guess message handling is the point of his example. So it must be message calling. Passing a message that looks like this in Objective-C: [obj addWithA:5 withB:3]; would work something like this with his proposed system: msg[" "] = "add"; // Just using "add", but could have been "addWithA:withB:" msg["a"] = "5"; msg["b"] = "3"; obj.perform(msg); - - - And now, if I can put up my own suggestion, I'd suggest he tries with this: obj.perform("add", 5, 3); which could be implemented with a D-style variadic function, see: <http://www.digitalmars.com/d/2.0/function.html#variadic> -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Apr 03 2009
next sibling parent reply Eljay <eljay adobe.com> writes:
Hi Michel,

Thanks for the tip on D-style variadic.  I'll give those a spin.

And you are spot on with what I want to be able to do.  Objective-C, with C
replaced by D.  (Hmmm, I think #1 slipped in from my Borland CScript days.)

Sincerely,
--Eljay
Apr 03 2009
parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2009-04-03 08:43:48 -0400, Eljay <eljay adobe.com> said:

 Hi Michel,
 
 Thanks for the tip on D-style variadic.  I'll give those a spin.
 
 And you are spot on with what I want to be able to do.  Objective-C, 
 with C replaced by D.  (Hmmm, I think #1 slipped in from my Borland 
 CScript days.)

I'm not exactly sure of what you're trying to do, but perhaps, if you're on a Mac, you'd be interested in trying the D/Objective-C bridge I've made. By binding D member functions to Objective-C methods, it becomes possible to call the function by knowing its Objective-C name and argument types using objc.msg.send. Type safety is minimal at the moment, but that could be changed by using Objective-C's reflection's mecanism. <http://michelf.com/projects/d-objc-bridge/> . . . And if compatibility with existing Objective-C code is not desired, it should be possible to reuse some parts of the bridge binding system, replicate the Objective-C runtime method dispatch and get a pretty decent speed out of it (2x the time of a standard virtual call would be a realistic goal according to my Objective-C benchmarks). -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Apr 04 2009
parent Eljay <eljay adobe.com> writes:
Hi Michel,

 I'm not exactly sure of what you're trying to do, but perhaps, if 
 you're on a Mac, you'd be interested in trying the D/Objective-C bridge 
 I've made.

I am on a Mac, and I am very interested in your D/Objective-C bridge. Very cool! Ultimately, what I would like to do, is what I vaguely envision as "Objective-D" syntax as part of D proper. Without the preprocessor feel of the Objective portion of Objective-C. I do not expect this feature to be in D 2.0. It's a big feature, and D 2.0 has several big features already in the queue. Maybe in some future D 3.0. For sake of discussion, I'm going to call "D 3.0 (maybe) that supported Objective-C-like capabilities" Obj-D. Here's the tricky part... If Obj-D were link-and-interop compatible with Objective-C, that would be an impressive feat, and I would leap for joy. But that's setting the bar high, and unnecessarily constraints Obj-D, since Obj-D would use the exact same facilities that Objective-C uses. Seems that your D/Objective-C bridge does that, without having Objective-syntax as part of D (which I consider very important to support the idiom, rather than calling selectors directly -- syntax matters). I consider this to be the "D is C API & C ABI friendly" equivalent for Objective-C. If Obj-D were able to easily "toll free bridge" to-and-fro with Objective-C -- basically enabling reasonably easy Cocoa / OS X programming -- I'd be more than satisfied. But that still means a lot of work for someone to add the toll free bridging objects. Someone would do so, doubtlessly. Perhaps me. But it will be a lot of work. If Obj-D were to have learned from Objective-C (such as it has learned from C++ and Java), and then invent a purely Obj-D better solution that was not "toll free bridge" or link-and-interop compatible with Objective-C ... I would be satisfied. And this is my expectation, assuming Walter and Andrei see the value to support the paradigm in the core language. Rather than "toll free bridge", we'd have a "mapping bridge", and that would also be a lot of work to interop with Cocoa / OS X. Much as a programmer can write object-oriented programs in C, a D 2.0(alpha) programmer can write dynamic typing and reflective programming in D 2.0(alpha) right now. But the core language does not support the paradigm, and some sort of dynamic typing / reflective programming / message architecture framework would need to be built upon the facilities that D 2.0(alpha) provides. I've run into a few of the "under construction" or "not yet implemented" or "there be dragons here" parts of D 2.0(alpha). That's to be expected, so I've explored other aspects of D 2.0(alpha) that appear to be more solid. Another way to achieve the same thing is to use a scripting language (e.g., JavaScript, Lua, Python, whatever) in conjunction with D, rather than try to invent an "Objective-C-like" framework within D. I'm just tinkering right now, and learning D 2.0, so I'm finding the exercise enjoyable.
 <http://michelf.com/projects/d-objc-bridge/>

Excellent, I'll look into it! (Woot! Much appreciated!)
 And if compatibility with existing Objective-C code is not desired, it 
 should be possible to reuse some parts of the bridge binding system, 
 replicate the Objective-C runtime method dispatch and get a pretty 
 decent speed out of it (2x the time of a standard virtual call would be 
 a realistic goal according to my Objective-C benchmarks).

Compatibility with Objective-C is desired. Moreso, interoperability with Cocoa on OS X. Eventually, interoperability with Cocoa-64 and Objective-C 2.0. Decent speed of the messaging architecture, to me, is not important yet at this stage. One or two orders of magnitude slower (x10 to x100) than Objective-C's messaging facilities would be acceptable -- especially considering how SmallTalk-like runtime message late-binding is used, and the heavy reliance on the delegate pattern. I presume that if the idiom is made into a key feature of D 3.0, the performance will be brought up to par with Objective-C. Or better. ;-) Sincerely, --Eljay
Apr 05 2009
prev sibling parent reply Eljay <eljay adobe.com> writes:
Hi Michel,

I tried the experiment...
obj.perform("add", 5, 3);

Alas, I'm not sure how to pass the variadic arguments through to another
variadic function, with this
signature:
void perform(...)

I also tried using Variant array -- wherein I expected automagic Variant
packaging (assuming I
understood the documentation correctly) -- but that did not work (could be
PEBKAC):
void perform(Variant[] ar ...)

Hmmmm.

Sincerely,
--Eljay
Apr 03 2009
next sibling parent grauzone <none example.net> writes:
Jarrett Billingsley wrote:
 On Fri, Apr 3, 2009 at 10:38 AM, Eljay <eljay adobe.com> wrote:
 
 Alas, I'm not sure how to pass the variadic arguments through to another
variadic function, with this
 signature:
 void perform(...)

You can't. D's varargs suck.
 I also tried using Variant array -- wherein I expected automagic Variant
packaging (assuming I
 understood the documentation correctly) -- but that did not work (could be
PEBKAC):
 void perform(Variant[] ar ...)

This is *exactly* what I'd like to replace D's varargs. I've suggested it several times.

The Gods are saying that varargs suck too much for fixing them, and that you should use compile time varargs (with tuples) instead.
Apr 03 2009
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Jarrett Billingsley wrote:
 On Fri, Apr 3, 2009 at 10:38 AM, Eljay <eljay adobe.com> wrote:
 
 Alas, I'm not sure how to pass the variadic arguments through to another
variadic function, with this
 signature:
 void perform(...)

You can't. D's varargs suck.

Of course you can. Where did that come from? void foo(T...)(T args) { bar(args); } void bar(T...)(T args) { foreach (a; args) writeln(a); } void main() { foo(3, 4.5); } prints: 3 4.5 Andrei
Apr 03 2009
next sibling parent reply grauzone <none example.net> writes:
How do I make this work with, say, interface functions?

But I already know your reply.
Apr 03 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
grauzone wrote:
 How do I make this work with, say, interface functions?
 
 But I already know your reply.

I don't know it. What is it? Andrei
Apr 03 2009
parent grauzone <none example.net> writes:
Andrei Alexandrescu wrote:
 grauzone wrote:
 How do I make this work with, say, interface functions?

 But I already know your reply.

I don't know it. What is it?

See your reply to Jarett. Granted, being able to have final methods in interfaces would improve the situation. But in general, there's no reason to abandon varargs, if only they weren't implemented in such a sucky way. Anyway, I'll add my own proposal for improving varargs, although it's a waste of time: 1.) For _argptr, replace the void* by a void*[], that is indexed by the argument number. Currently, _argptr is a raw pointer to the stack, and getting a pointer to an individual argument on all platforms/compilers is non-trivial. 2.) Allow a way to chain function calls with varargs. Currently, if you have _argptr and _arguments, you can't pass them to another variadic function. Basically, you would have to allow passing _argptr and _arguments instead of real arguments to a function with ... in its signature. When you combine both feature requests, it would be trivial to build vararg calls at runtime. You'd simple stuff the TypeInfo and the pointer to your data into _argptr and _arguments, and call a variadic function with it. But if you don't intend to change anything about varargs, please let Walter kill this abortion. Fighting with the trivial, unnecessary restrictions of the current implementation is really annoying.
 Andrei

Apr 03 2009
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Jarrett Billingsley wrote:
 On Fri, Apr 3, 2009 at 11:20 AM, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 
 You can't.  D's varargs suck.

void foo(T...)(T args) { bar(args); } void bar(T...)(T args) { foreach (a; args) writeln(a); }

*NOT* the same thing. They are templated, which means a new instantiation for each different combination of argument types, and cannot be virtual. Please keep that in mind.

But my point was that variadic templates can be ideal wrappers for the non-templated variadics, which can be virtual. It's the best of both worlds, you get no bloating and comfortable calling syntax too. I think it's silly to ask "I want to do... xyz... but without templates because templates aren't virtual/cause bloating/bring up painful memories". Templates are an excellent starting point in many designs because they preserve full typing of the parameters. Then, of course you can route them to dynamic functions no problem. This brings me to another point - I need to file an enhancement request. Interfaces must support non-virtual functions, e.g. final functions and templates. Andrei
Apr 03 2009
next sibling parent reply grauzone <none example.net> writes:
 But my point was that variadic templates can be ideal wrappers for the 
 non-templated variadics, which can be virtual. It's the best of both 
 worlds, you get no bloating and comfortable calling syntax too.

The problem is: you can't go back. Here I use runtime as synonym for passing pointers/TypeInfo and compiletime for nesting templates: compiletime -> runtime: simple compiletime -> compiletime: very simple runtime -> runtime: simple runtime -> compiletime: oops It also generates bloat by requiring a compiletime -> runtime part. Why do this conversion, if you want to be in runtime anway? If you want to do "very dynamic" stuff (whatever that is), applying some trivial fixes to variadic functions seems to be the better way to go.
Apr 03 2009
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
grauzone wrote:
 But my point was that variadic templates can be ideal wrappers for the 
 non-templated variadics, which can be virtual. It's the best of both 
 worlds, you get no bloating and comfortable calling syntax too.

The problem is: you can't go back.

Of course you can't go back. How could you possibly?...
 Here I use runtime as synonym for 
 passing pointers/TypeInfo and compiletime for nesting templates:
 
 compiletime -> runtime: simple
 compiletime -> compiletime: very simple
 runtime -> runtime: simple
 runtime -> compiletime: oops
 
 It also generates bloat by requiring a compiletime -> runtime part. Why 
 do this conversion, if you want to be in runtime anway?

You see, you really don't know what you want. (I don't mean this pejoratively.) Previously you wanted convenient call syntax. That means you don't build stuff at runtime, you just write it and want it to work. Now you say you essentially don't care for that. Then build an array of Variant and be done with it.
 If you want to do "very dynamic" stuff (whatever that is), applying some 
 trivial fixes to variadic functions seems to be the better way to go.

I disagree. If you have a point, it is not carried on very strongly. Andrei
Apr 03 2009
prev sibling parent reply Georg Wrede <georg.wrede iki.fi> writes:
grauzone wrote:
 But my point was that variadic templates can be ideal wrappers for the 
 non-templated variadics, which can be virtual. It's the best of both 
 worlds, you get no bloating and comfortable calling syntax too.

The problem is: you can't go back. Here I use runtime as synonym for passing pointers/TypeInfo and compiletime for nesting templates: compiletime -> runtime: simple compiletime -> compiletime: very simple runtime -> runtime: simple runtime -> compiletime: oops It also generates bloat by requiring a compiletime -> runtime part. Why do this conversion, if you want to be in runtime anway? If you want to do "very dynamic" stuff (whatever that is), applying some trivial fixes to variadic functions seems to be the better way to go.

The canonical way of doing "very dynamic stuff" would be to use JavaScript within the D application.[1] So, I guess at this point some use cases or other motivators could be good for the actual point you're driving? ---- [1] For other readers: yes, yes, and the other languages too (Lua, Python, etc...)
Apr 04 2009
parent grauzone <none example.net> writes:
Very funny.
Apr 04 2009
prev sibling parent reply Christopher Wright <dhasenan gmail.com> writes:
Andrei Alexandrescu wrote:
 Jarrett Billingsley wrote:
 On Fri, Apr 3, 2009 at 11:20 AM, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:

 You can't.  D's varargs suck.

void foo(T...)(T args) { bar(args); } void bar(T...)(T args) { foreach (a; args) writeln(a); }

*NOT* the same thing. They are templated, which means a new instantiation for each different combination of argument types, and cannot be virtual. Please keep that in mind.

But my point was that variadic templates can be ideal wrappers for the non-templated variadics, which can be virtual. It's the best of both worlds, you get no bloating and comfortable calling syntax too. I think it's silly to ask "I want to do... xyz... but without templates because templates aren't virtual/cause bloating/bring up painful memories". Templates are an excellent starting point in many designs because they preserve full typing of the parameters. Then, of course you can route them to dynamic functions no problem.

Okay, so before I had to write: void foo(...) { foo(_argptr, _arguments); } void foo(void* argptr, TypeInfo[] argtypes) {} You want me to instead write: // needs a different name due to overload issues void foo_templated(T...)(T args) { foo(args); } void foo(...) { foo(_argptr, _arguments); } void foo(void* argptr, TypeInfo[] argtypes) {} How the hell does that help?
Apr 03 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Christopher Wright wrote:
 Andrei Alexandrescu wrote:
 Jarrett Billingsley wrote:
 On Fri, Apr 3, 2009 at 11:20 AM, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:

 You can't.  D's varargs suck.

void foo(T...)(T args) { bar(args); } void bar(T...)(T args) { foreach (a; args) writeln(a); }

*NOT* the same thing. They are templated, which means a new instantiation for each different combination of argument types, and cannot be virtual. Please keep that in mind.

But my point was that variadic templates can be ideal wrappers for the non-templated variadics, which can be virtual. It's the best of both worlds, you get no bloating and comfortable calling syntax too. I think it's silly to ask "I want to do... xyz... but without templates because templates aren't virtual/cause bloating/bring up painful memories". Templates are an excellent starting point in many designs because they preserve full typing of the parameters. Then, of course you can route them to dynamic functions no problem.

Okay, so before I had to write: void foo(...) { foo(_argptr, _arguments); } void foo(void* argptr, TypeInfo[] argtypes) {} You want me to instead write: // needs a different name due to overload issues void foo_templated(T...)(T args) { foo(args); } void foo(...) { foo(_argptr, _arguments); } void foo(void* argptr, TypeInfo[] argtypes) {} How the hell does that help?

void foo_templated(T...)(T args) { Variant[args.length] dynatyped; foreach (i, arg; args) dynatyped[i] = arg; return foo(dynatyped); } void foo(Variant[] dynatyped...) { ... } Andrei
Apr 03 2009
next sibling parent reply Georg Wrede <georg.wrede iki.fi> writes:
Andrei Alexandrescu wrote:
 
 void foo_templated(T...)(T args)
 {
    Variant[args.length] dynatyped;
    foreach (i, arg; args) dynatyped[i] = arg;
    return foo(dynatyped);
 }
 void foo(Variant[] dynatyped...)
 {
    ...
 }

Hmm. An idiom. My sweet tooth just got thirsty for syntactic sugar!
Apr 04 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Georg Wrede wrote:
 Andrei Alexandrescu wrote:
 void foo_templated(T...)(T args)
 {
    Variant[args.length] dynatyped;
    foreach (i, arg; args) dynatyped[i] = arg;
    return foo(dynatyped);
 }
 void foo(Variant[] dynatyped...)
 {
    ...
 }

Hmm. An idiom. My sweet tooth just got thirsty for syntactic sugar!

When opImplicitCastFrom will be implemented, foo's definition will be enough. You'll write foo(1, 2.3, "a") and the compiler will rewrite it to foo(Variant(1), Variant(2.3), Variant("a")). Andrei
Apr 04 2009
parent reply grauzone <none example.net> writes:
Andrei Alexandrescu wrote:
 Georg Wrede wrote:
 Andrei Alexandrescu wrote:
 void foo_templated(T...)(T args)
 {
    Variant[args.length] dynatyped;
    foreach (i, arg; args) dynatyped[i] = arg;
    return foo(dynatyped);
 }
 void foo(Variant[] dynatyped...)
 {
    ...
 }

Hmm. An idiom. My sweet tooth just got thirsty for syntactic sugar!

When opImplicitCastFrom will be implemented, foo's definition will be enough. You'll write foo(1, 2.3, "a") and the compiler will rewrite it to foo(Variant(1), Variant(2.3), Variant("a")).

This looks very good. How exactly will it work?
 Andrei

Apr 04 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
grauzone wrote:
 Andrei Alexandrescu wrote:
 Georg Wrede wrote:
 Andrei Alexandrescu wrote:
 void foo_templated(T...)(T args)
 {
    Variant[args.length] dynatyped;
    foreach (i, arg; args) dynatyped[i] = arg;
    return foo(dynatyped);
 }
 void foo(Variant[] dynatyped...)
 {
    ...
 }

Hmm. An idiom. My sweet tooth just got thirsty for syntactic sugar!

When opImplicitCastFrom will be implemented, foo's definition will be enough. You'll write foo(1, 2.3, "a") and the compiler will rewrite it to foo(Variant(1), Variant(2.3), Variant("a")).

This looks very good. How exactly will it work?

Variant will implement opImplicitCastFrom(T) for all types T. The "..." in foo's declaration already allows variadic arguments. The rest will be taken care of by the compiler. So IMHO: (a) Variadics with templates are good; (b) Variadics with uniform-type arrays are good; (c) We should avoid variadics with void*. Andrei
Apr 04 2009
parent reply grauzone <none example.net> writes:
Andrei Alexandrescu wrote:
 grauzone wrote:
 Andrei Alexandrescu wrote:
 Georg Wrede wrote:
 Andrei Alexandrescu wrote:
 void foo_templated(T...)(T args)
 {
    Variant[args.length] dynatyped;
    foreach (i, arg; args) dynatyped[i] = arg;
    return foo(dynatyped);
 }
 void foo(Variant[] dynatyped...)
 {
    ...
 }

Hmm. An idiom. My sweet tooth just got thirsty for syntactic sugar!

When opImplicitCastFrom will be implemented, foo's definition will be enough. You'll write foo(1, 2.3, "a") and the compiler will rewrite it to foo(Variant(1), Variant(2.3), Variant("a")).

This looks very good. How exactly will it work?

Variant will implement opImplicitCastFrom(T) for all types T. The "..." in foo's declaration already allows variadic arguments. The rest will be taken care of by the compiler.

I see. Consider if opImplicitCastFrom was implemented and you had a function "void foo(Variant[] x...)", would it be possible to pass a Variant[] to it, that will be directly used as "x"? For example: void foo(Variant[] x...) { writefln(x); } Variant[] t = [1,2,3]; foo(t); Possible output 1: [1, 2, 3] Possible output 2: [[1, 2, 3]] This is a slight ambiguity. Will "t" be packed into a Variant, or will it directly assign "t" to "x" as if there wasn't an opImplicitCastFrom(T)? Would it be a compilation error? Anyway. If this would work, chaining function calls would be simple, and wouldn't require additional wrapper functions.
 So IMHO:
 
 (a) Variadics with templates are good;

The "void foo(Variant[] dynatyped...)" isn't a template. But if on the caller side you can use it as if it was a "void foo(T)(T...)" or a "void foo(...)", everything is fine.
 (b) Variadics with uniform-type arrays are good;

Sure.
 (c) We should avoid variadics with void*.

Well, it isn't really important whether you use a (TypeInfo, void*) tuple, or a Variant for passing around data of a dynamic type unknown at compile time. Of course, Variant is much nicer. I was only suggesting void*, because for classic variadics, it's probably simpler to implement on the compiler side. (Although, as you said, it's hard to convert a (TypeInfo, void*) to Variant.)
 
 Andrei

Apr 04 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
grauzone wrote:
 Consider if opImplicitCastFrom was implemented and you had a function 
 "void foo(Variant[] x...)", would it be possible to pass a Variant[] to 
 it, that will be directly used as "x"?
 
 For example:
 
 void foo(Variant[] x...) {
     writefln(x);
 }
 
 Variant[] t = [1,2,3];
 foo(t);

Yah, in fact this compiles and runs: import std.variant; import std.stdio; void foo(Variant[] x...) { writeln(x); } void main() { auto t = variantArray(1,2,3); foo(t); }
 Possible output 1:
 [1, 2, 3]
 
 Possible output 2:
 [[1, 2, 3]]

The former is correct.
 This is a slight ambiguity. Will "t" be packed into a Variant, or will 
 it directly assign "t" to "x" as if there wasn't an 
 opImplicitCastFrom(T)? Would it be a compilation error?
 
 Anyway. If this would work, chaining function calls would be simple, and 
 wouldn't require additional wrapper functions.
 
 So IMHO:

 (a) Variadics with templates are good;

The "void foo(Variant[] dynatyped...)" isn't a template. But if on the caller side you can use it as if it was a "void foo(T)(T...)" or a "void foo(...)", everything is fine.
 (b) Variadics with uniform-type arrays are good;

Sure.
 (c) We should avoid variadics with void*.

Well, it isn't really important whether you use a (TypeInfo, void*) tuple, or a Variant for passing around data of a dynamic type unknown at compile time. Of course, Variant is much nicer. I was only suggesting void*, because for classic variadics, it's probably simpler to implement on the compiler side. (Although, as you said, it's hard to convert a (TypeInfo, void*) to Variant.)

The advantage of Variant is a tad subtle. Variant is part of the standard library and as such it's trusted code. Although it internally uses unsafe features, it offers a safe interface. In contrast, void* essentially trusts the application programmer. Andrei
Apr 04 2009
parent grauzone <none example.net> writes:
Andrei Alexandrescu wrote:
 grauzone wrote:
 Consider if opImplicitCastFrom was implemented and you had a function 
 "void foo(Variant[] x...)", would it be possible to pass a Variant[] 
 to it, that will be directly used as "x"?

 For example:

 void foo(Variant[] x...) {
     writefln(x);
 }

 Variant[] t = [1,2,3];
 foo(t);

Yah, in fact this compiles and runs: import std.variant; import std.stdio; void foo(Variant[] x...) { writeln(x); } void main() { auto t = variantArray(1,2,3); foo(t); }

But when opImplicitCastFrom is implemented? I guess the compiler always first tries to pass the array directly? Anyway, you seem to be sure that there's no ambiguity, and that it will actually work. So it's all fine. I'm happy now. PS: it seems in the end, this will work exactly like in Java. Just that Java boxes native types into Object, while D uses Variant. (And opImplicitCastFrom instead of autoboxing.)
 Possible output 1:
 [1, 2, 3]

 Possible output 2:
 [[1, 2, 3]]

The former is correct.
 This is a slight ambiguity. Will "t" be packed into a Variant, or will 
 it directly assign "t" to "x" as if there wasn't an 
 opImplicitCastFrom(T)? Would it be a compilation error?

 Anyway. If this would work, chaining function calls would be simple, 
 and wouldn't require additional wrapper functions.

 So IMHO:

 (a) Variadics with templates are good;

The "void foo(Variant[] dynatyped...)" isn't a template. But if on the caller side you can use it as if it was a "void foo(T)(T...)" or a "void foo(...)", everything is fine.
 (b) Variadics with uniform-type arrays are good;

Sure.
 (c) We should avoid variadics with void*.

Well, it isn't really important whether you use a (TypeInfo, void*) tuple, or a Variant for passing around data of a dynamic type unknown at compile time. Of course, Variant is much nicer. I was only suggesting void*, because for classic variadics, it's probably simpler to implement on the compiler side. (Although, as you said, it's hard to convert a (TypeInfo, void*) to Variant.)

The advantage of Variant is a tad subtle. Variant is part of the standard library and as such it's trusted code. Although it internally uses unsafe features, it offers a safe interface. In contrast, void* essentially trusts the application programmer.

This doesn't really mater, but: you could still allow void* in verifiable safe programs, as long as you tag the pointer with a TypeInfo, and disallow pointer arithmetic.
 
 Andrei

Apr 04 2009
prev sibling next sibling parent Georg Wrede <georg.wrede iki.fi> writes:
Andrei Alexandrescu wrote:
 Christopher Wright wrote:
 Andrei Alexandrescu wrote:
 Jarrett Billingsley wrote:
 On Fri, Apr 3, 2009 at 11:20 AM, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:

 You can't.  D's varargs suck.

void foo(T...)(T args) { bar(args); } void bar(T...)(T args) { foreach (a; args) writeln(a); }

*NOT* the same thing. They are templated, which means a new instantiation for each different combination of argument types, and cannot be virtual. Please keep that in mind.

But my point was that variadic templates can be ideal wrappers for the non-templated variadics, which can be virtual. It's the best of both worlds, you get no bloating and comfortable calling syntax too. I think it's silly to ask "I want to do... xyz... but without templates because templates aren't virtual/cause bloating/bring up painful memories". Templates are an excellent starting point in many designs because they preserve full typing of the parameters. Then, of course you can route them to dynamic functions no problem.

Okay, so before I had to write: void foo(...) { foo(_argptr, _arguments); } void foo(void* argptr, TypeInfo[] argtypes) {} You want me to instead write: // needs a different name due to overload issues void foo_templated(T...)(T args) { foo(args); } void foo(...) { foo(_argptr, _arguments); } void foo(void* argptr, TypeInfo[] argtypes) {} How the hell does that help?

void foo_templated(T...)(T args) { Variant[args.length] dynatyped; foreach (i, arg; args) dynatyped[i] = arg; return foo(dynatyped); } void foo(Variant[] dynatyped...) { ... }

Nice idiom! Wish I could templatize this template, or sugarize this syntax.
Apr 04 2009
prev sibling parent reply Christopher Wright <dhasenan gmail.com> writes:
Andrei Alexandrescu wrote:
 void foo_templated(T...)(T args)
 {
    Variant[args.length] dynatyped;
    foreach (i, arg; args) dynatyped[i] = arg;
    return foo(dynatyped);
 }
 void foo(Variant[] dynatyped...)
 {
    ...
 }
 
 Andrei

Okay, that's a reasonable solution, except that it doesn't work with interfaces. I already submitted a bugzilla entry that you should be able to create a Variant[] from D varargs, but that still requires writing a wrapper function.
Apr 04 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Christopher Wright wrote:
 Andrei Alexandrescu wrote:
 void foo_templated(T...)(T args)
 {
    Variant[args.length] dynatyped;
    foreach (i, arg; args) dynatyped[i] = arg;
    return foo(dynatyped);
 }
 void foo(Variant[] dynatyped...)
 {
    ...
 }

 Andrei

Okay, that's a reasonable solution, except that it doesn't work with interfaces. I already submitted a bugzilla entry that you should be able to create a Variant[] from D varargs, but that still requires writing a wrapper function.

A Variant[] is difficult to create from TypeInfo[] + void* varargs because vital information has already been lost. That's why I'm saying we better fix the situation by e.g. implementing opImplicitCast. Please, let's leave void* alone. Andrei
Apr 04 2009
parent reply Christopher Wright <dhasenan gmail.com> writes:
Andrei Alexandrescu wrote:
 Christopher Wright wrote:
 Andrei Alexandrescu wrote:
 void foo_templated(T...)(T args)
 {
    Variant[args.length] dynatyped;
    foreach (i, arg; args) dynatyped[i] = arg;
    return foo(dynatyped);
 }
 void foo(Variant[] dynatyped...)
 {
    ...
 }

 Andrei

Okay, that's a reasonable solution, except that it doesn't work with interfaces. I already submitted a bugzilla entry that you should be able to create a Variant[] from D varargs, but that still requires writing a wrapper function.

A Variant[] is difficult to create from TypeInfo[] + void* varargs because vital information has already been lost. That's why I'm saying we better fix the situation by e.g. implementing opImplicitCast. Please, let's leave void* alone. Andrei

I don't find that this is the case. I implemented a Variant type that can be created from a void* and a typeinfo -- I had to, since tango's Variant doesn't support this (and, incidentally, because there is no way to get a representation of the data inside a tango Variant). What information did you find that you needed but is not available in TypeInfo?
Apr 04 2009
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Christopher Wright wrote:
 Andrei Alexandrescu wrote:
 Christopher Wright wrote:
 Andrei Alexandrescu wrote:
 void foo_templated(T...)(T args)
 {
    Variant[args.length] dynatyped;
    foreach (i, arg; args) dynatyped[i] = arg;
    return foo(dynatyped);
 }
 void foo(Variant[] dynatyped...)
 {
    ...
 }

 Andrei

Okay, that's a reasonable solution, except that it doesn't work with interfaces. I already submitted a bugzilla entry that you should be able to create a Variant[] from D varargs, but that still requires writing a wrapper function.

A Variant[] is difficult to create from TypeInfo[] + void* varargs because vital information has already been lost. That's why I'm saying we better fix the situation by e.g. implementing opImplicitCast. Please, let's leave void* alone. Andrei

I don't find that this is the case. I implemented a Variant type that can be created from a void* and a typeinfo -- I had to, since tango's Variant doesn't support this (and, incidentally, because there is no way to get a representation of the data inside a tango Variant). What information did you find that you needed but is not available in TypeInfo?

To have constant-type dispatching without limitations you need the static type information. Andrei
Apr 04 2009
parent reply Christopher Wright <dhasenan gmail.com> writes:
Andrei Alexandrescu wrote:
 Christopher Wright wrote:
 What information did you find that you needed but is not available in 
 TypeInfo?

To have constant-type dispatching without limitations you need the static type information.

What do you mean by this? At first I thought you meant that TypeInfo does not contain sufficient information to determine whether something is const or immutable, but that is not the case. If you wish to convert a Variant of a mutable thing to a const version of it, then you can check whether the type you get is convertible, and without any trouble -- the template instantiation to get the appropriate type will provide the TypeInfo you need. So what's the issue?
Apr 05 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Christopher Wright wrote:
 Andrei Alexandrescu wrote:
 Christopher Wright wrote:
 What information did you find that you needed but is not available in 
 TypeInfo?

To have constant-type dispatching without limitations you need the static type information.

What do you mean by this? At first I thought you meant that TypeInfo does not contain sufficient information to determine whether something is const or immutable, but that is not the case. If you wish to convert a Variant of a mutable thing to a const version of it, then you can check whether the type you get is convertible, and without any trouble -- the template instantiation to get the appropriate type will provide the TypeInfo you need. So what's the issue?

You may want to peruse the std.variant implementation. Variant is a struct containing a buffer for the data and a pointer to function. The function is a dispatcher for a handful of primitive operations. The pointer to function is assigned from the address of a function template. To get that address, you need the static type. That's the necessity. It's a very fast and very flexible design, but it does need the static type during initialization. Andrei
Apr 05 2009
next sibling parent reply dsimcha <dsimcha yahoo.com> writes:
== Quote from Andrei Alexandrescu (SeeWebsiteForEmail erdani.org)'s article
 Christopher Wright wrote:
 Andrei Alexandrescu wrote:
 Christopher Wright wrote:
 What information did you find that you needed but is not available in
 TypeInfo?

To have constant-type dispatching without limitations you need the static type information.

What do you mean by this? At first I thought you meant that TypeInfo does not contain sufficient information to determine whether something is const or immutable, but that is not the case. If you wish to convert a Variant of a mutable thing to a const version of it, then you can check whether the type you get is convertible, and without any trouble -- the template instantiation to get the appropriate type will provide the TypeInfo you need. So what's the issue?

struct containing a buffer for the data and a pointer to function. The function is a dispatcher for a handful of primitive operations. The pointer to function is assigned from the address of a function template. To get that address, you need the static type. That's the necessity. It's a very fast and very flexible design, but it does need the static type during initialization. Andrei

Have you used std.variant at all lately? I think it's succumbed to bit rot as D2 has evolved, because various strange things happen when I try to use it. If it's not on the agenda to be completely revamped for the new Phobos anyhow, I'll look into these errors in more detail and file some bug reports and patches.
Apr 05 2009
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
dsimcha wrote:
 == Quote from Andrei Alexandrescu (SeeWebsiteForEmail erdani.org)'s article
 Christopher Wright wrote:
 Andrei Alexandrescu wrote:
 Christopher Wright wrote:
 What information did you find that you needed but is not available in
 TypeInfo?

static type information.

does not contain sufficient information to determine whether something is const or immutable, but that is not the case. If you wish to convert a Variant of a mutable thing to a const version of it, then you can check whether the type you get is convertible, and without any trouble -- the template instantiation to get the appropriate type will provide the TypeInfo you need. So what's the issue?

struct containing a buffer for the data and a pointer to function. The function is a dispatcher for a handful of primitive operations. The pointer to function is assigned from the address of a function template. To get that address, you need the static type. That's the necessity. It's a very fast and very flexible design, but it does need the static type during initialization. Andrei

Have you used std.variant at all lately? I think it's succumbed to bit rot as D2 has evolved, because various strange things happen when I try to use it. If it's not on the agenda to be completely revamped for the new Phobos anyhow, I'll look into these errors in more detail and file some bug reports and patches.

I know std.variant is quintessential in a number of applications and programming styles, but at the moment such are not my focus. Variants have this interesting quirk that they are either vital or not needed at all, indeed I remember people asking on this group "what is a variant good for" etc. It would be great if you submitted whatever failures you find in std.variant. Andrei
Apr 05 2009
prev sibling parent reply Christopher Wright <dhasenan gmail.com> writes:
Andrei Alexandrescu wrote:
 Christopher Wright wrote:
 Andrei Alexandrescu wrote:
 Christopher Wright wrote:
 What information did you find that you needed but is not available 
 in TypeInfo?

To have constant-type dispatching without limitations you need the static type information.

What do you mean by this? At first I thought you meant that TypeInfo does not contain sufficient information to determine whether something is const or immutable, but that is not the case. If you wish to convert a Variant of a mutable thing to a const version of it, then you can check whether the type you get is convertible, and without any trouble -- the template instantiation to get the appropriate type will provide the TypeInfo you need. So what's the issue?

You may want to peruse the std.variant implementation. Variant is a struct containing a buffer for the data and a pointer to function. The function is a dispatcher for a handful of primitive operations. The pointer to function is assigned from the address of a function template. To get that address, you need the static type. That's the necessity. It's a very fast and very flexible design, but it does need the static type during initialization.

Your Variant can be extended more easily than mine. Your Variant is faster than mine. But yours cannot be used everywhere, and mine can. Also, given that only a very few operations can ever be supported by Variant, I think it's not a big deal if Variant never lets you do anything interesting with the stored values besides boxing, unboxing, equality testing, hashing, and any standard reflection stuff that is available.
 Andrei

Apr 05 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Christopher Wright wrote:
 Your Variant can be extended more easily than mine. Your Variant is 
 faster than mine. But yours cannot be used everywhere, and mine can.

But this may be an artificial constraint. For example, you may say "my variant can be used with old-style variadics". Phobos' doesn't, but I don't think that's important. Here's where I think std.variant is limited: 1. In interfacing with equally general variants implemented independently. I know it sounds a bit cryptic, but I couldn't find a simpler way to put it. 2. In storing structs larger than a threshold. I know how to do that and there's a bugzilla on it, just haven't gotten around to implementing it. Other than that it compares very favorably with other implementations I've written and read. In particular it has the same "bigger fish" design that std.algorithm has: it includes alternative designs as particular cases.
 Also, given that only a very few operations can ever be supported by 
 Variant, I think it's not a big deal if Variant never lets you do 
 anything interesting with the stored values besides boxing, unboxing, 
 equality testing, hashing, and any standard reflection stuff that is 
 available.

Not quite getting that. (Extensibility wasn't an essential concern for Phobos' Variant.) So yes, there are some primitives that variants should support. The point is making those primitives fast, otherwise dynamic-typed programming in D wouldn't be compelling. That being said, I never ran speed comparisons, so I expect I'll be thoroughly surprised. Andrei
Apr 05 2009
parent dsimcha <dsimcha yahoo.com> writes:
== Quote from Andrei Alexandrescu (SeeWebsiteForEmail erdani.org)'s
 2. In storing structs larger than a threshold. I know how to do that and
 there's a bugzilla on it, just haven't gotten around to implementing it.

I'm looking at this and thinking about how to implement it and submit it. One issue is how to store the information about whether the payload is stored inline or on the heap. Obvious solutions like sticking an extra bool in the VariantN struct wouldn't work because this thing is supposed to be uber-efficient. Querying the typeinfo every time to compare size information is also a bad idea for similar reasons. It seems a VariantN struct consists (in terms of fields) of a ubyte[size] array and a function pointer for handler. When stuff is allocated on the heap, I can store the pointer to it in the ubyte[size], provided that size >= void*.sizeof. In real world use cases, there aren't too many good reasons to use a Variant with size smaller than a pointer, so I'm thinking just make the minimum size void*.sizeof. The problem, though, is encoding whether the data is on the heap or inline in an efficient way. Any clever hacks like storing this information in some unimportant bits of something else?
Apr 05 2009
prev sibling parent Daniel Keep <daniel.keep.lists gmail.com> writes:
Christopher Wright wrote:
 ...
 
 I don't find that this is the case. I implemented a Variant type that
 can be created from a void* and a typeinfo -- I had to, since tango's
 Variant doesn't support this (and, incidentally, because there is no way
 to get a representation of the data inside a tango Variant).

I'm working on it! It just keeps getting pre-empted by higher priority processes. :P -- Daniel
Apr 04 2009
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Tomas Lindquist Olsen wrote:
 On Fri, Apr 3, 2009 at 5:20 PM, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 Jarrett Billingsley wrote:
 On Fri, Apr 3, 2009 at 10:38 AM, Eljay <eljay adobe.com> wrote:

 Alas, I'm not sure how to pass the variadic arguments through to another
 variadic function, with this
 signature:
 void perform(...)


void foo(T...)(T args) { bar(args); } void bar(T...)(T args) { foreach (a; args) writeln(a); } void main() { foo(3, 4.5); } prints: 3 4.5 Andrei

That's not a D vararg, it's a variadic template!

Well I guess I'll take that as a compliment.
 I've made proposals to allow this properly (without templates) before
 so I'm not going to waste time on that again...

I, too, believe it would be a waste of time, but probably for different reasons. Look at this: void fun(...) { ... use void* _argptr and TypeInfo[] _arguments ... } I'll ignore the fact that binding the arguments to magic, predefined names has the elegance of a fart interrupting a solemn moment. The larger problem is the type of _argptr. No safety can be built into a function that traffics in void*, EVER. No matter what you do. A proverb goes "No matter how nicely you dress a mule, you'll still call it a mule." (It was s/mule/ass/g in Romanian, but ass is ambiguous in English.) So yes, it would be a waste of time to embellish a fundamentally deeply unsafe feature. A better use of time would be to improve its safe counterpart. Andrei
Apr 03 2009
parent reply grauzone <none example.net> writes:
Andrei Alexandrescu wrote:
 Tomas Lindquist Olsen wrote:
 On Fri, Apr 3, 2009 at 5:20 PM, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 Jarrett Billingsley wrote:
 On Fri, Apr 3, 2009 at 10:38 AM, Eljay <eljay adobe.com> wrote:

 Alas, I'm not sure how to pass the variadic arguments through to 
 another
 variadic function, with this
 signature:
 void perform(...)


void foo(T...)(T args) { bar(args); } void bar(T...)(T args) { foreach (a; args) writeln(a); } void main() { foo(3, 4.5); } prints: 3 4.5 Andrei

That's not a D vararg, it's a variadic template!

Well I guess I'll take that as a compliment.
 I've made proposals to allow this properly (without templates) before
 so I'm not going to waste time on that again...

I, too, believe it would be a waste of time, but probably for different reasons. Look at this: void fun(...) { ... use void* _argptr and TypeInfo[] _arguments ... } I'll ignore the fact that binding the arguments to magic, predefined names has the elegance of a fart interrupting a solemn moment. The larger problem is the type of _argptr.

That surprises me. Your string mixin callbacks (or whatever is the correct name for this idiom) in std.algorithm also use magic, predefined names like "a".
 No safety can be built into a function that traffics in void*, EVER. No 
 matter what you do. A proverb goes "No matter how nicely you dress a 
 mule, you'll still call it a mule." (It was s/mule/ass/g in Romanian, 
 but ass is ambiguous in English.) So yes, it would be a waste of time to 
 embellish a fundamentally deeply unsafe feature. A better use of time 
 would be to improve its safe counterpart.

The void* is paired with a TypeInfo. A Variant uses raw data and TypeInfo, and manages to be reasonably safe. If you want guaranteed safety, you must use something like Java (or SafeD vaporware).
 Andrei

Apr 03 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
grauzone wrote:
 Andrei Alexandrescu wrote:
 void fun(...)
 {
     ... use void* _argptr and TypeInfo[] _arguments ...
 }

 I'll ignore the fact that binding the arguments to magic, predefined 
 names has the elegance of a fart interrupting a solemn moment. The 
 larger problem is the type of _argptr.

That surprises me. Your string mixin callbacks (or whatever is the correct name for this idiom) in std.algorithm also use magic, predefined names like "a".

The situations are different. (The "$" in array index is also different.)
 No safety can be built into a function that traffics in void*, EVER. 
 No matter what you do. A proverb goes "No matter how nicely you dress 
 a mule, you'll still call it a mule." (It was s/mule/ass/g in 
 Romanian, but ass is ambiguous in English.) So yes, it would be a 
 waste of time to embellish a fundamentally deeply unsafe feature. A 
 better use of time would be to improve its safe counterpart.

The void* is paired with a TypeInfo. A Variant uses raw data and TypeInfo, and manages to be reasonably safe. If you want guaranteed safety, you must use something like Java (or SafeD vaporware).

I don't want guaranteed safety. I want safety when lack thereof is gratuitous. Andrei
Apr 03 2009
next sibling parent reply grauzone <none example.net> writes:
Andrei Alexandrescu wrote:
 grauzone wrote:
 Andrei Alexandrescu wrote:
 void fun(...)
 {
     ... use void* _argptr and TypeInfo[] _arguments ...
 }

 I'll ignore the fact that binding the arguments to magic, predefined 
 names has the elegance of a fart interrupting a solemn moment. The 
 larger problem is the type of _argptr.

That surprises me. Your string mixin callbacks (or whatever is the correct name for this idiom) in std.algorithm also use magic, predefined names like "a".

The situations are different. (The "$" in array index is also different.)

How are the situations different?
 No safety can be built into a function that traffics in void*, EVER. 
 No matter what you do. A proverb goes "No matter how nicely you dress 
 a mule, you'll still call it a mule." (It was s/mule/ass/g in 
 Romanian, but ass is ambiguous in English.) So yes, it would be a 
 waste of time to embellish a fundamentally deeply unsafe feature. A 
 better use of time would be to improve its safe counterpart.

The void* is paired with a TypeInfo. A Variant uses raw data and TypeInfo, and manages to be reasonably safe. If you want guaranteed safety, you must use something like Java (or SafeD vaporware).

I don't want guaranteed safety. I want safety when lack thereof is gratuitous.

Then what's your problem with using Variant?
 
 Andrei

Apr 03 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
grauzone wrote:
 Andrei Alexandrescu wrote:
 grauzone wrote:
 Andrei Alexandrescu wrote:
 void fun(...)
 {
     ... use void* _argptr and TypeInfo[] _arguments ...
 }

 I'll ignore the fact that binding the arguments to magic, predefined 
 names has the elegance of a fart interrupting a solemn moment. The 
 larger problem is the type of _argptr.

That surprises me. Your string mixin callbacks (or whatever is the correct name for this idiom) in std.algorithm also use magic, predefined names like "a".

The situations are different. (The "$" in array index is also different.)

How are the situations different?

I don't have much time to explain, but the code in which a and b are used (which is restricted to an expression) cannot possibly define its own symbols called a and b. Also, user code can never define $. In contrast, _arguments is a valid, nonreserved D symbol that's just up for grabs.
 No safety can be built into a function that traffics in void*, EVER. 
 No matter what you do. A proverb goes "No matter how nicely you 
 dress a mule, you'll still call it a mule." (It was s/mule/ass/g in 
 Romanian, but ass is ambiguous in English.) So yes, it would be a 
 waste of time to embellish a fundamentally deeply unsafe feature. A 
 better use of time would be to improve its safe counterpart.

The void* is paired with a TypeInfo. A Variant uses raw data and TypeInfo, and manages to be reasonably safe. If you want guaranteed safety, you must use something like Java (or SafeD vaporware).

I don't want guaranteed safety. I want safety when lack thereof is gratuitous.

Then what's your problem with using Variant?

I don't have any problem with using Variant. Andrei
Apr 03 2009
parent reply grauzone <none example.net> writes:
Andrei Alexandrescu wrote:
 grauzone wrote:
 Andrei Alexandrescu wrote:
 grauzone wrote:
 Andrei Alexandrescu wrote:
 void fun(...)
 {
     ... use void* _argptr and TypeInfo[] _arguments ...
 }

 I'll ignore the fact that binding the arguments to magic, 
 predefined names has the elegance of a fart interrupting a solemn 
 moment. The larger problem is the type of _argptr.

That surprises me. Your string mixin callbacks (or whatever is the correct name for this idiom) in std.algorithm also use magic, predefined names like "a".

The situations are different. (The "$" in array index is also different.)

How are the situations different?

I don't have much time to explain, but the code in which a and b are used (which is restricted to an expression) cannot possibly define its own symbols called a and b. Also, user code can never define $. In contrast, _arguments is a valid, nonreserved D symbol that's just up for grabs.

K. Then rename _arguments to $arguments. Problem solved.
Apr 03 2009
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
grauzone wrote:
 Andrei Alexandrescu wrote:
 grauzone wrote:
 Andrei Alexandrescu wrote:
 grauzone wrote:
 Andrei Alexandrescu wrote:
 void fun(...)
 {
     ... use void* _argptr and TypeInfo[] _arguments ...
 }

 I'll ignore the fact that binding the arguments to magic, 
 predefined names has the elegance of a fart interrupting a solemn 
 moment. The larger problem is the type of _argptr.

That surprises me. Your string mixin callbacks (or whatever is the correct name for this idiom) in std.algorithm also use magic, predefined names like "a".

The situations are different. (The "$" in array index is also different.)

How are the situations different?

I don't have much time to explain, but the code in which a and b are used (which is restricted to an expression) cannot possibly define its own symbols called a and b. Also, user code can never define $. In contrast, _arguments is a valid, nonreserved D symbol that's just up for grabs.

K. Then rename _arguments to $arguments. Problem solved.

I said that's not even the problem. Recall I said "I'll ignore the fact that..." Andrei
Apr 03 2009
prev sibling parent reply Christopher Wright <dhasenan gmail.com> writes:
Andrei Alexandrescu wrote:
 No safety can be built into a function that traffics in void*, EVER. 
 No matter what you do. A proverb goes "No matter how nicely you dress 
 a mule, you'll still call it a mule." (It was s/mule/ass/g in 
 Romanian, but ass is ambiguous in English.) So yes, it would be a 
 waste of time to embellish a fundamentally deeply unsafe feature. A 
 better use of time would be to improve its safe counterpart.

The void* is paired with a TypeInfo. A Variant uses raw data and TypeInfo, and manages to be reasonably safe. If you want guaranteed safety, you must use something like Java (or SafeD vaporware).

I don't want guaranteed safety. I want safety when lack thereof is gratuitous.

But your variadic template prohibits polymorphism. Therefore, the lack of safety with variadic arguments is not gratuitous, merely unnecessary in certain situations. It would, however, be quite nice to get a void*[] rather than a void*, since it would provide more safety (array bounds) and ease of use (you can index it or foreach it), while eliminating a standard library module.
Apr 03 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Christopher Wright wrote:
 Andrei Alexandrescu wrote:
 No safety can be built into a function that traffics in void*, EVER. 
 No matter what you do. A proverb goes "No matter how nicely you 
 dress a mule, you'll still call it a mule." (It was s/mule/ass/g in 
 Romanian, but ass is ambiguous in English.) So yes, it would be a 
 waste of time to embellish a fundamentally deeply unsafe feature. A 
 better use of time would be to improve its safe counterpart.

The void* is paired with a TypeInfo. A Variant uses raw data and TypeInfo, and manages to be reasonably safe. If you want guaranteed safety, you must use something like Java (or SafeD vaporware).

I don't want guaranteed safety. I want safety when lack thereof is gratuitous.

But your variadic template prohibits polymorphism.

I explained that that's not the case.
 Therefore, the lack 
 of safety with variadic arguments is not gratuitous, merely unnecessary 
 in certain situations.

Gratuitous.
 It would, however, be quite nice to get a void*[] rather than a void*, 
 since it would provide more safety (array bounds) and ease of use (you 
 can index it or foreach it), while eliminating a standard library module.

No. Andrei
Apr 03 2009
parent Christopher Wright <dhasenan gmail.com> writes:
Andrei Alexandrescu wrote:
 Christopher Wright wrote:
 It would, however, be quite nice to get a void*[] rather than a void*, 
 since it would provide more safety (array bounds) and ease of use (you 
 can index it or foreach it), while eliminating a standard library module.

No.

Yes.
Apr 03 2009
prev sibling next sibling parent eljay <eljay adobe.com> writes:
Hi Jarrett,

Thanks for the information (I've read quite a few responses further into this
thread).

I thought I was missing something, but this appears to be an area in D that
could use some improvement
(although I liked Andrei's template solution, it would not work for forwarding
messages).  I did not mean to
start a flamewar.

In the mean time, I will use a Variant dictionary (Variant[string]) which holds
the message being passed
around.  That will give me the ability to easily forward the message to another
dispatcher.

Just a little messier to specify the parms-as-a-dictionary than using, say,
Python or Lua.

Sincerely,
--Eljay
Apr 03 2009
prev sibling parent Jarrett Billingsley <jarrett.billingsley gmail.com> writes:
On Sat, Apr 4, 2009 at 12:15 PM, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:
 Georg Wrede wrote:
 Andrei Alexandrescu wrote:
 void foo_templated(T...)(T args)
 {
 =A0 Variant[args.length] dynatyped;
 =A0 foreach (i, arg; args) dynatyped[i] =3D arg;
 =A0 return foo(dynatyped);
 }
 void foo(Variant[] dynatyped...)
 {
 =A0 ...
 }

Hmm. An idiom. My sweet tooth just got thirsty for syntactic sugar!

When opImplicitCastFrom will be implemented, foo's definition will be enough. You'll write foo(1, 2.3, "a") and the compiler will rewrite it to foo(Variant(1), Variant(2.3), Variant("a")). Andrei

Oh wow, I missed this in the flood. I'm very happy about this indeed!
Apr 05 2009
prev sibling next sibling parent Jarrett Billingsley <jarrett.billingsley gmail.com> writes:
On Fri, Apr 3, 2009 at 10:38 AM, Eljay <eljay adobe.com> wrote:

 Alas, I'm not sure how to pass the variadic arguments through to another
variadic function, with this
 signature:
 void perform(...)

You can't. D's varargs suck.
 I also tried using Variant array -- wherein I expected automagic Variant
packaging (assuming I
 understood the documentation correctly) -- but that did not work (could be
PEBKAC):
 void perform(Variant[] ar ...)

This is *exactly* what I'd like to replace D's varargs. I've suggested it several times.
Apr 03 2009
prev sibling next sibling parent Tomas Lindquist Olsen <tomas.l.olsen gmail.com> writes:
On Fri, Apr 3, 2009 at 5:20 PM, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:
 Jarrett Billingsley wrote:
 On Fri, Apr 3, 2009 at 10:38 AM, Eljay <eljay adobe.com> wrote:

 Alas, I'm not sure how to pass the variadic arguments through to anothe=



 variadic function, with this
 signature:
 void perform(...)

You can't. =C2=A0D's varargs suck.

Of course you can. Where did that come from? void foo(T...)(T args) { bar(args); } void bar(T...)(T args) { =C2=A0foreach (a; args) writeln(a); } void main() { =C2=A0 =C2=A0foo(3, 4.5); } prints: 3 4.5 Andrei

That's not a D vararg, it's a variadic template! I've made proposals to allow this properly (without templates) before so I'm not going to waste time on that again... It's a silly limitation IMHO.
Apr 03 2009
prev sibling next sibling parent Jarrett Billingsley <jarrett.billingsley gmail.com> writes:
On Fri, Apr 3, 2009 at 11:20 AM, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:

 You can't. =A0D's varargs suck.

Of course you can. Where did that come from? void foo(T...)(T args) { bar(args); } void bar(T...)(T args) { =A0foreach (a; args) writeln(a); }

*NOT* the same thing. They are templated, which means a new instantiation for each different combination of argument types, and cannot be virtual. Please keep that in mind.
Apr 03 2009
prev sibling parent reply Jarrett Billingsley <jarrett.billingsley gmail.com> writes:
On Fri, Apr 3, 2009 at 6:09 PM, eljay <eljay adobe.com> wrote:
 Hi Jarrett,

 Thanks for the information (I've read quite a few responses further into =

 I thought I was missing something, but this appears to be an area in D th=

 (although I liked Andrei's template solution, it would not work for forwa=

 start a flamewar.

Wow, neither did I.
Apr 03 2009
parent superdan <super dan.org> writes:
Jarrett Billingsley Wrote:

 On Fri, Apr 3, 2009 at 6:09 PM, eljay <eljay adobe.com> wrote:
 Hi Jarrett,

 Thanks for the information (I've read quite a few responses further into this
thread).

 I thought I was missing something, but this appears to be an area in D that
could use some improvement
 (although I liked Andrei's template solution, it would not work for forwarding
messages). �I did not mean to
 start a flamewar.

Wow, neither did I.

now i know where the hell mother theresa hid.
Apr 04 2009