www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Template default arguments

reply Bill Baxter <Bill_member pathlink.com> writes:
Howdy D folks.
I've been looking over D a bit, just reading through docs and such at this
point.   So far it looks very interesting, but I haven't had time to do any
actual programming with it yet.  Nevertheless, I haven't yet seen mention of the
two things that I would most like to see improved in C++, so I thought I would
post a couple of messages to the list to see if either this is already supported
by D, or could be supported in a future version of D.

My first C++ annoyance is template default arguments.  This is the easy one to
fix.  I once had the misfortune of having to define a non-default allocator for
all of my STL containers that would do allocation using special shared memory
alloc commands on IRIX.  It's great that STL makes it possible to do such a
thing, but it was a royal pain because the allocator is generally the very last
template argument.  So that meant everywhere I was using an STL container I had
to specify EVERY template argument, even ones where I was happy with the
default.  

For instance take the STL map:
map<Key, Data, Compare=someDefault, Alloc=someDefault>

In order to define the Alloc parameter, you have to specify the Compare
parameter too.  That's just a silly syntactical limitation with no real reason
for existing.  I should be able to specify any of the default arguments I want
and leave the others out.  

Two ways to do this I can think of are:
1) Like many scripting languages, support a keyword style syntax for default
arguments.  So I could make my map by typing 
map<MyKey, MyData, Alloc=MyAlloc>

2) Allow a special symbol to be used as an argument that just means "use the
default", like:
map<MyKey, MyData, :default:, MyAlloc>


Actually thinking about it more, the above applies to method calls really too.
I always assumed that C++'s inflexible rules for default arguments on methods
were justified because of some sort of performance concerns, but I don't see why
either of the above couldn't be handled completely at compile time for method
calls as well.  I'm no compiler writer, though.  Maybe there's something I'm
missing?  I guess C++'s crazy Koenig lookup rules make compiler writer's heads
explode quick enough as it is without throwing that into the mix.

So how about it?  Can D already do this?  Could it in the future?

Bill Baxter
May 04 2005
next sibling parent reply "Uwe Salomon" <post uwesalomon.de> writes:
 Actually thinking about it more, the above applies to method calls  
 really too.
 I always assumed that C++'s inflexible rules for default arguments on  
 methods
 were justified because of some sort of performance concerns, but I don't  
 see why
 either of the above couldn't be handled completely at compile time for  
 method
 calls as well.

No. In "The C++ Programming Language" from Stroustrup he says that he did not want something like that to be possible: someFunc(what, , , does, it, , , mean, , 2); Ciao uwe
May 05 2005
parent reply Bill Baxter <Bill_member pathlink.com> writes:
In article <op.sqakdrtq6yjbe6 sandmann.maerchenwald.net>, Uwe Salomon says...

No. In "The C++ Programming Language" from Stroustrup he says that he did  
not want something like that to be possible:

someFunc(what, , , does, it, , , mean, , 2);

Interesting. So you mean Stroustrup says the method calls would be hard to understand if default arguments could specified out of order? Unless there's more to it than you're quoting there, that's a very weak argument. Basically the current default parameter syntax is equivalent to saying: someFunc(what,are,the,rest,of,them,set,to, , , , , ,); except it gets written as someFunc(what,are,the,rest,of,them,set,to); which in my opinion is harder to fully comprehend than your example, because the person who sees the code at the bottom doesn't even have a clue that there *are* a bunch of other hidden parameters being silently tacked on the end. At least the extra commas in your example clue the reader in that there *are* some default values being used. The thing that drives me crazy about using GUI toolkits like wxWidgets or FOX for example, is all the default arguments I have to specify just to modify that one I really want to set. Here's an example widget of a constructor from FOX: FXListBox(FXComposite *p,FXObject* tgt=NULL,FXSelector sel=0,FXuint opts=FRAME_SUNKEN|FRAME_THICK|LISTBOX_NORMAL,FXint x=0,FXint y=0,FXint w=0,FXint h=0,FXint pl=DEFAULT_PAD,FXint pr=DEFAULT_PAD,FXint pt=DEFAULT_PAD,FXint pb=DEFAULT_PAD); Is Bjorn really going to tell me that it's better and more readable if I rummage through the header to dig up all the default values, and type them all in: FXListBox(p,NULL,0,FRAME_SUNKEN|FRAME_THICK|LISTBOX_NORMAL,0,0,0,0,DEFAULT_PAD,DEFAULT_PAD,DEFAULT_PAD,DEFAULT_PAD+1); instead of just doing this: FXListBox(p,,,,,,,,,,,DEFAULT_PAD+1); just so I can change the value of the last default parameter? Personally if I have to read someone else's source code, I'd rather see the latter code than the former. I also don't want to have to be the one that maintains the former and has to keep all those default parameters up to date with the default values as they're specified in the library. --bb
May 05 2005
next sibling parent reply Jan-Eric Duden <jeduden whisset.com> writes:
Bill Baxter wrote:
 In article <op.sqakdrtq6yjbe6 sandmann.maerchenwald.net>, Uwe Salomon says...
 
 
No. In "The C++ Programming Language" from Stroustrup he says that he did  
not want something like that to be possible:

someFunc(what, , , does, it, , , mean, , 2);

Interesting. So you mean Stroustrup says the method calls would be hard to understand if default arguments could specified out of order? Unless there's more to it than you're quoting there, that's a very weak argument. Basically the current default parameter syntax is equivalent to saying: someFunc(what,are,the,rest,of,them,set,to, , , , , ,); except it gets written as someFunc(what,are,the,rest,of,them,set,to); which in my opinion is harder to fully comprehend than your example, because the person who sees the code at the bottom doesn't even have a clue that there *are* a bunch of other hidden parameters being silently tacked on the end. At least the extra commas in your example clue the reader in that there *are* some default values being used. The thing that drives me crazy about using GUI toolkits like wxWidgets or FOX for example, is all the default arguments I have to specify just to modify that one I really want to set. Here's an example widget of a constructor from FOX: FXListBox(FXComposite *p,FXObject* tgt=NULL,FXSelector sel=0,FXuint opts=FRAME_SUNKEN|FRAME_THICK|LISTBOX_NORMAL,FXint x=0,FXint y=0,FXint w=0,FXint h=0,FXint pl=DEFAULT_PAD,FXint pr=DEFAULT_PAD,FXint pt=DEFAULT_PAD,FXint pb=DEFAULT_PAD); Is Bjorn really going to tell me that it's better and more readable if I rummage through the header to dig up all the default values, and type them all in: FXListBox(p,NULL,0,FRAME_SUNKEN|FRAME_THICK|LISTBOX_NORMAL,0,0,0,0,DEFAULT_PAD,DEFAULT_PAD,DEFAULT_PAD,DEFAULT_PAD+1); instead of just doing this: FXListBox(p,,,,,,,,,,,DEFAULT_PAD+1); just so I can change the value of the last default parameter? Personally if I have to read someone else's source code, I'd rather see the latter code than the former. I also don't want to have to be the one that maintains the former and has to keep all those default parameters up to date with the default values as they're specified in the library.

If I were Stoustroup I would blame the designers of the library. If you need to pass that many parameters a parameter class would certainly help. I'm not sure whats better - a paramter class or named parameters.
May 06 2005
parent reply Bill Baxter <Bill_member pathlink.com> writes:
In article <427B4FA9.5000003 whisset.com>, Jan-Eric Duden says...
Bill Baxter wrote:
 
 Here's an example widget of a constructor from FOX:
 
 FXListBox(FXComposite *p,FXObject* tgt=NULL,FXSelector sel=0,FXuint
 opts=FRAME_SUNKEN|FRAME_THICK|LISTBOX_NORMAL,FXint x=0,FXint y=0,FXint
w=0,FXint
 h=0,FXint pl=DEFAULT_PAD,FXint pr=DEFAULT_PAD,FXint pt=DEFAULT_PAD,FXint
 pb=DEFAULT_PAD);
 
 Is Bjorn really going to tell me that it's better and more readable if I
rummage
 through the header to dig up all the default values, and type them all in:
 


If I were Stoustroup I would blame the designers of the library.

If you need to pass that many parameters a parameter class would 
certainly help.

I'm not sure whats better - a paramter class or named parameters.

Yeh, I'm not particularly wild about the design of the widget constructors in FOX either, but I understand the designer's reasoning. It is nice in some ways to be able to have 1 line of code <=> one widget. That way when you go shuffling your UI around moving a widget from here to there is just a matter of moving one line of code. A parameter class would require extra lines of code to set up the parameters and just generally introduce extra superfluous classes. Of course in D you could use a static array initializer and still have one line of code (or are those not implemented yet?). If the D sig was: : FXListBox(FXComposite parent, FXListBoxOptions opts); Then you could call it like: : new FXListBox(parent, [pb:DEFAULT_PAD+1]); Assuming FXListBoxOptions is a struct that has a pb member. That's why I think Eugene's suggestion was so great. Why bother with the struct at all in the above example? FXListBoxOptions is just a placeholder that doesn't really need to be a named entity. It serves no purpose outside of calling this constructor, so why not just do away with it? : new FXListBox(parent, pb:DEFAULT_PAD+1); And use the original signature with all those default values. It acknowledges the fact that both argument lists and structs are lists of type-identifier pairs. (Although structs can contain other structs, whereas argument lists can't contain argument lists.) If you can initialize a struct with identifier-value pairs in D, why not argument lists, too? There is one other small difference, in that argument list generally has some elements that must be 'initialized' (i.e. no default value), whereas structs don't have that. But I don't see why it couldn't work, and work just as well for template argument lists as for method argument lists. Does it make the compiler's job too hard to match calls with the method they invoke when overloading is in the mix? It does mean that these two methods could no longer coexist: real fun(int a=5,string b="hello"); real fun(string b="hello", int a=5); In current C,C++,D those are different methods but a call like fun(a:2,b:"no good") would now be ambiguous. Although I suppose if you just changed the argument names in the second example it wouldn't be ambiguous: real fun(int a=5,string b="hello"); real fun(string c="hello", int d=5); So that's only going to be a problem if you write code that has the exact same types and argument names in different orders, which is probably something you shouldn't be doing anyway. One potential downside is that it would mean that the name of arguments suddenly has significance, unlike in C where you can leave them out entirely. That means user code can break if a library maintainer just renames an argument. Is this one of those cases like the switch statement where Walter doesn't want it to look just like C but act differently? --bb
May 08 2005
parent "Walter" <newshound digitalmars.com> writes:
"Bill Baxter" <Bill_member pathlink.com> wrote in message
news:d5m8a3$2mc6$1 digitaldaemon.com...
 Does it make the compiler's job too hard to match calls with the method

 invoke when overloading is in the mix?  It does mean that these two

 could no longer coexist:

 real fun(int a=5,string b="hello");
 real fun(string b="hello", int a=5);

 In current C,C++,D those are different methods but a call like
 fun(a:2,b:"no good") would now be ambiguous.

I hadn't thought of that, but you're right. It's not an insoluble problem, but it does make the compiler's job of disambiguating overloaded functions significantly harder.
May 09 2005
prev sibling parent "Walter" <newshound digitalmars.com> writes:
"Bill Baxter" <Bill_member pathlink.com> wrote in message
news:d5d3dc$12jv$1 digitaldaemon.com...
 Is Bjorn really going to tell me that it's better and more readable if I

 through the header to dig up all the default values, and type them all in:

AD,DEFAULT_PAD,DEFAULT_PAD,DEFAULT_PAD+1);
 instead of just doing this:
 FXListBox(p,,,,,,,,,,,DEFAULT_PAD+1);
 just so I can change the value of the last default parameter?

 Personally if I have to read someone else's source code, I'd rather see

 latter code than the former.  I also don't want to have to be the one that
 maintains the former and has to keep all those default parameters up to

 with the default values as they're specified in the library.

I agree that stinks, but I also put the blame on the design of FXListBox. It should provide a few overloads of that functions that minimize the number of default parameters.
May 09 2005
prev sibling next sibling parent reply Bill Baxter <Bill_member pathlink.com> writes:
Unless I missed something in the doc, it looks like D handles default template
arguments just like C++.  I.e. you can't specify the Nth without specifying all
the ones before it too.

So c'mon people, I can't be the only one who finds having to manually specify
lots of default parameters to be irksome and a completely unnecessary practice
in the 21st century.  Can I?  Can I get at least an 'amen' from the audience?
Anyone?  Or failing that, at least a compelling argument as to why I'm an idiot.
Uwe's appeal-to-authority argument didn't really convince me.

--bb

In article <d5bt8k$3bd$1 digitaldaemon.com>, Bill Baxter says...
My first C++ annoyance is template default arguments.  This is the easy one to
fix.  

May 05 2005
parent Matthias Becker <Matthias_member pathlink.com> writes:
Unless I missed something in the doc, it looks like D handles default template
arguments just like C++.  I.e. you can't specify the Nth without specifying all
the ones before it too.

Right.
So c'mon people, I can't be the only one who finds having to manually specify
lots of default parameters to be irksome and a completely unnecessary practice
in the 21st century.  Can I?

In the 21st century you just don't have things with many paramters. So you can't have things with many default paramters. e.g. group some of the parameters to structs and pass them. A function like # void foo (Bar bar, Baz baz, Qux qux, XXX xxx, YYY yyy, ZZZ zzz); gets # void foo (Bla bla, Blub blub); and is called like: # foo (Bla (bar, baz, qux), Blub (xxx, yyy, zzz)); This way you can do things like this: # Bla defaultBla = ...; # Blub defaultBlub = ...; # # ... # # foo (defaultBla, Blub (xxx, yyy, zzz)); But perhaps you should rething your design in general if you have functions with many arguments. -- Matthias Becker
May 10 2005
prev sibling parent reply "Eugene Pelekhay" <pelekhay gmail.com> writes:
Hi
On Thu, 05 May 2005 04:35:16 +0300, Bill Baxter <Bill_member pathlink.com>  
wrote:

 Howdy D folks.
 I've been looking over D a bit, just reading through docs and such at  
 this
 point.   So far it looks very interesting, but I haven't had time to do  
 any
 actual programming with it yet.  Nevertheless, I haven't yet seen  
 mention of the
 two things that I would most like to see improved in C++, so I thought I  
 would
 post a couple of messages to the list to see if either this is already  
 supported
 by D, or could be supported in a future version of D.

 My first C++ annoyance is template default arguments.  This is the easy  
 one to
 fix.  I once had the misfortune of having to define a non-default  
 allocator for
 all of my STL containers that would do allocation using special shared  
 memory
 alloc commands on IRIX.  It's great that STL makes it possible to do  
 such a
 thing, but it was a royal pain because the allocator is generally the  
 very last
 template argument.  So that meant everywhere I was using an STL  
 container I had
 to specify EVERY template argument, even ones where I was happy with the
 default.

 For instance take the STL map:
 map<Key, Data, Compare=someDefault, Alloc=someDefault>

 In order to define the Alloc parameter, you have to specify the Compare
 parameter too.  That's just a silly syntactical limitation with no real  
 reason
 for existing.  I should be able to specify any of the default arguments  
 I want
 and leave the others out.

 Two ways to do this I can think of are:
 1) Like many scripting languages, support a keyword style syntax for  
 default
 arguments.  So I could make my map by typing
 map<MyKey, MyData, Alloc=MyAlloc>

 2) Allow a special symbol to be used as an argument that just means "use  
 the
 default", like:
 map<MyKey, MyData, :default:, MyAlloc>

I think first is more preferrable, but with some minor changes to make it more similar to structure initialization syntax. Something like that: : template TFoo(T1 : int, T2 : long, T3 : SomeClass ) {} : TFoo(T2:float) foo; : void func(int a = 0, float b = 3.14) {} : func(b:4);
 Actually thinking about it more, the above applies to method calls  
 really too.
 I always assumed that C++'s inflexible rules for default arguments on  
 methods
 were justified because of some sort of performance concerns, but I don't  
 see why
 either of the above couldn't be handled completely at compile time for  
 method
 calls as well.  I'm no compiler writer, though.  Maybe there's something  
 I'm
 missing?  I guess C++'s crazy Koenig lookup rules make compiler writer's  
 heads
 explode quick enough as it is without throwing that into the mix.

 So how about it?  Can D already do this?  Could it in the future?

 Bill Baxter

May 06 2005
parent Bill Baxter <Bill_member pathlink.com> writes:
In article <op.sqclqqox5ljmzl thrash>, Eugene Pelekhay says...
 Two ways to do this I can think of are:
 1) Like many scripting languages, support a keyword style syntax for  
 default
 arguments.  So I could make my map by typing
 map<MyKey, MyData, Alloc=MyAlloc>

I think first is more preferrable, but with some minor changes to make it more similar to structure initialization syntax. Something like that: : template TFoo(T1 : int, T2 : long, T3 : SomeClass ) {} : TFoo(T2:float) foo; : void func(int a = 0, float b = 3.14) {} : func(b:4);

That is interesting. Yeh, it is very analogous to the structure initialization situation. There's a list of things, and you want to spefiy some but not others. Is that proper D syntax though? Wouldn't it be: : template TFoo(T1 = int, T2 = long, T3 = SomeClass ) {} : TFoo!(T2:float) foo; --bb
May 06 2005