[Review] Named parameters library

The formal review of the Named Parameters library by David Abrahams and Daniel Wallin starts today and runs until Monday, November 22nd. The Named Parameters library provides a framework for writing functions that can accept arguments by name or by position. The use of named parameters can improve the readability of calls to these functions, especially when the functions have many parameters with default values. The Named Parameters library allows one to write a new_window function that allows a call such as: window* w = new_window("alert2", movable = true); The library is available in the Boost sandbox here: boost/named_params.hpp libs/utility And in the files area (login required): http://groups.yahoo.com/group/boost/files/named_params.zip Reviewers may wish to compare this solution to the named parameters already used by the Boost Graph Library, documented here: http://www.boost.org/libs/graph/doc/bgl_named_params.html Please send your reviews either to the Boost list or to me personally. The former is preferred, because it allows the community to consider your review as well. When writing your review, you may wish to consider the following questions (from http://www.boost.org/more/formal_review_process.htm#Comments) - What is your evaluation of the design? - What is your evaluation of the implementation? - What is your evaluation of the documentation? - What is your evaluation of the potential usefulness of the library? - Did you try to use the library? With what compiler? Did you have any problems? - How much effort did you put into your evaluation? A glance? A quick reading? In-depth study? - Are you knowledgeable about the problem domain? As always, please remember to clearly state whether you believe the library should be accepted into Boost. Thank you Doug Gregor Review Manager, Named Parameters library

Hi Dave & Daniel Here are some general comments on the library: - What is your evaluation of the design? --------- no comments - What is your evaluation of the implementation? --------- no comments - What is your evaluation of the documentation? --------- seems ok, though I miss what impact it would have on performance. - What is your evaluation of the potential usefulness of the library? --------- low - Did you try to use the library? With what compiler? Did you have any problems? --------- no, no. - How much effort did you put into your evaluation? A glance? A quick reading? In-depth study? ------ a glance As always, please remember to clearly state whether you believe the library should be accepted into Boost. -------- I think it should not be accepted. My main motivation is that I think it promotes an unsound growth in function interfaces. I also miss a comparison of other techniques (eg. bgl_named_params, named_constructor_pattern) to do the same and an good explanation why this approach is superior. Let me elaborate. 1. Clearly, this is not nice window* w = new_window("alert", true, true, false, 77, 65); However, the amount of work needed to code parameters for this is quite large. A simple, effective way is just to do it like this: window* w = new_window("alert"); w->set_resizable( true ); w->set_height( 77 ); .... and you might provide chaining to make it more concise. In addition there is no forwarding problems and you might have these functions anyway. 2. what happens, then, if I don't have an object, but a free-standing function? a) wrap the function in an object b) do as in http://www.boost.org/libs/graph/doc/bgl_named_params.html c) provide overloads of functions that pack related parameters together or simply add defaults I must admit I'm a bit sceptical about functions when they start having more than 3-4 arguments; there is almost always some abstraction lurking just waiting to be done. -Thorsten

-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Thorsten Ottosen Sent: Friday, November 12, 2004 2:47 PM To: boost@lists.boost.org Subject: [boost] Re: [Review] Named parameters library
1. Clearly, this is not nice
window* w = new_window("alert", true, true, false, 77, 65);
However, the amount of work needed to code parameters for this is quite large. A simple, effective way is just to do it like this:
window* w = new_window("alert"); w->set_resizable( true ); w->set_height( 77 ); .... and you might provide chaining to make it more concise. In addition there is no forwarding problems and you might have these functions anyway.
What about the situation where you need to specify the parameters at construction, e.g. the RAII paradigm? -- Noah

"Noah Stein" <noah@acm.org> wrote in message news:200411121528426.SM00924@enki... | > -----Original Message----- | > From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] | > On Behalf Of Thorsten Ottosen | > Sent: Friday, November 12, 2004 2:47 PM | > To: boost@lists.boost.org | > Subject: [boost] Re: [Review] Named parameters library | | | > 1. Clearly, this is not nice | > | > window* w = new_window("alert", true, true, false, 77, 65); | > | > However, the amount of work needed to code parameters for this is quite | > large. | > A simple, effective way is just to do it like this: | > | > window* w = new_window("alert"); | > w->set_resizable( true ); | > w->set_height( 77 ); | > .... | > and you might provide chaining to make it more concise. In addition there | > is | > no forwarding problems and you might have these | > functions anyway. | > | | What about the situation where you need to specify the parameters at | construction, e.g. the RAII paradigm? I don't think I understand what you're saying. -Thorsten

-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Thorsten Ottosen Sent: Saturday, November 13, 2004 5:42 AM To: boost@lists.boost.org Subject: [boost] Re: Re: [Review] Named parameters library
"Noah Stein" <noah@acm.org> wrote in message news:200411121528426.SM00924@enki... | > -----Original Message----- | > From: boost-bounces@lists.boost.org [mailto:boost- bounces@lists.boost.org] | > On Behalf Of Thorsten Ottosen | > Sent: Friday, November 12, 2004 2:47 PM | > To: boost@lists.boost.org | > Subject: [boost] Re: [Review] Named parameters library | | | > 1. Clearly, this is not nice | > | > window* w = new_window("alert", true, true, false, 77, 65); | > | > However, the amount of work needed to code parameters for this is quite | > large. | > A simple, effective way is just to do it like this: | > | > window* w = new_window("alert"); | > w->set_resizable( true ); | > w->set_height( 77 ); | > .... | > and you might provide chaining to make it more concise. In addition there | > is | > no forwarding problems and you might have these | > functions anyway. | > | | What about the situation where you need to specify the parameters at | construction, e.g. the RAII paradigm?
I don't think I understand what you're saying.
You presented a counter-example to show how the named parameters is unnecessary. You show that, quite reasonably, you can remove parameters from the constructor by using post-construction functions. The parameters become explicitly identified due to the fact that the function names are identification themselves, e.g. "true" obviously refers to the sizeable attribute in your example although it's not obvious from the original constructor. I agree that your point is well made in the example given. But that's only one example. There are many others. My resulting question is: what's your solution when the parameter list can't be reduced? Frequently, I like to avoid two-step construction and initialization. I don't want every member function to check to see if the object is truly available or uninitialized. There are times that I can't avoid long parameter lists in my constructors, especially when dealing with classes that sit just on top of hardware. The named parameters library looks like it could facilitate construction by enhancing readability and default value handling. I guess my point boils down to the fact that you gave one counter-example, which I believe is not applicable in a number of cases. Given that it's not, it just seems premature to not accept. -- Noah

Noah Stein <noah <at> acm.org> writes:
On Behalf Of Thorsten Ottosen
| What about the situation where you need to specify the parameters at | construction, e.g. the RAII paradigm?
I don't think I understand what you're saying.
I agree that your point is well made in the example given. But that's only one example. There are many others. My resulting question is: what's your solution when the parameter list can't be reduced? Frequently, I like to avoid two-step construction and initialization. I don't want every member function to check to see if the object is truly available or uninitialized.
Well, you could construct a valid object with suitable default values. Such an object is valid by definition.
There are times that I can't avoid long parameter lists in my constructors, especially when dealing with classes that sit just on top of hardware. The named parameters library looks like it could facilitate construction by enhancing readability and default value handling.
Did this library support constructors anyway? Given the answer to that question is yes, I'm not sure why you can't do step-wise constrction. -Thorsten

"Thorsten Ottosen" wrote:
- What is your evaluation of the potential usefulness of the library? --------- low
There are quantities of people who have specialized domain knowledge and "some programming skill". These people could use their knowledge if they are provided with easy to use procedural API infrastructure. Named parameters could help to keep such procedural interface useable. /Pavel

"Pavel Vozenilek" <pavel_vozenilek@hotmail.com> wrote in message news:cn4k2u$a9n$2@sea.gmane.org... | "Thorsten Ottosen" wrote: | | > - What is your evaluation of the potential usefulness of the library? | > --------- | > low | > | > | There are quantities of people who have | specialized domain knowledge | and "some programming skill". | | These people could use their knowledge | if they are provided with easy to use | procedural API infrastructure. | | Named parameters could help to keep | such procedural interface useable. I'm just questioning how much programming skill that is needed to understand object.foo(). I don't think its harder and you don't have forwarding problems which is going to take some time for novices to understand (ie, to learn to use ref()). -Thorsten.

"Thorsten Ottosen" wrote:
| There are quantities of people who have | specialized domain knowledge | and "some programming skill". |
I'm just questioning how much programming skill that is needed to understand object.foo(). I don't think its harder and you don't have forwarding problems which is going to take some time for novices to understand (ie, to learn to use ref()).
Those who had learned FORTRAN may be able to use C++ this way. /Pavel

"Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
I think it should not be accepted. My main motivation is that I think it promotes an unsound growth in function interfaces.
Like having support for derivation promotes an unsound reliance on implementation inheritance and support for operators promotes abuse of operator overloading? Features don't promote practices.
I also miss a comparison of other techniques (eg. bgl_named_params, named_constructor_pattern)
I don't know about the latter. Never heard of it. But I'm guessing you're talking about something like: f(slew_is(.799), score_is(55)) If so, either it allows arbitrary parameter ordering and aside from the use of () instead of = its advantages are equivalent to those of our library, or it does not, and our library offers the advantage of freeing the user/reader/maintainer from having to remember parameter order.
to do the same and an good explanation why this approach is superior.
This approach is superior to bgl_named_params in part because it avoids undue coupling and dependency in a library design. That coupling is discussed in depth in this excerpt from C++ Template Metaprogramming (http://www.boost-consulting.com/mplbook): f(slew(.799).name("z")); Here, the expression slew(.799) would build a instance of class named_params<slew_tag, float, nil_t> having the empty class nil_t as its only base class, and containing the value .799 as a single data member. Then, its name member function would be called with "z" to produce an instance of: named_params< name_tag, char const[2] // .name("z") , named_params< slew_tag, double const // slew(.799) , nil_t >
having a copy of the instance just described as its only base, and containing a reference to "z" as its only data member. We could go into detail about how each tagged value can be extracted from such a structure, but at this point in the book we're sure your brain is already working that out for itself, so we leave it as an exercise. Instead, we'd like to focus on the chosen syntax of the DSL, and what's required to make it work. If you think for a moment about it, you'll see that not only do we need a top-level function for each parameter name (to generate the initial named_params instance in a chain), but named_params must also contain a member function for each of the parameter names we might want to follow it with. After all, we might just as well have written: f(slew(.799).score(55)); Since the named parameter interface pays off best when there are many optional parameters, and because there will probably be some overlap in the parameter names used by various functions in a given library, we're going to end up with a lot of coupling in the design. There will be a single, central named_params definition used for all functions in the library that use named parameter interfaces. Adding a new parameter name to a function declared in one header will mean going back and modifying the definition of named_params, which in turn will cause the recompilation of every translation unit that uses our named parameter interface. While writing this book, we reconsidered the interface used for named function parameter support. With a little experimentation we discovered that it's possible to provide the ideal syntax by using keyword objects with overloaded assignment operators: f(slew = .799, name = "z"); Not only is this syntax nicer for users, but adding a new parameter name is easy for the writer of the library containing f, and it doesn't cause any coupling.
Let me elaborate.
1. Clearly, this is not nice
window* w = new_window("alert", true, true, false, 77, 65);
True, but sometimes it is appropriate. Even then, you only need two parameters whose order is not obviously dictated by function to justify the use of a named parameter interface: paint(hue = 1071, luminance = 10) vs. paint(1071, 10)
However, the amount of work needed to code parameters for this is quite large.
Really? Did you compare how much work it would be to use BOOST_NAMED_PARAMS_FUN vs. what you're suggesting below?
A simple, effective way is just to do it like this:
window* w = new_window("alert"); w->set_resizable( true ); w->set_height( 77 ); ....
How would you apply that idiom to the algorithms of the Boost Graph Library?
and you might provide chaining to make it more concise. In addition there is no forwarding problems and you might have these functions anyway.
What do you mean by "you might have these functions anyway?"
2. what happens, then, if I don't have an object, but a free-standing function?
That's the main use-case of our library!
a) wrap the function in an object b) do as in http://www.boost.org/libs/graph/doc/bgl_named_params.html c) provide overloads of functions that pack related parameters together or simply add defaults
It looks like you didn't really read through the library docs. You just use the library in the normal way.
I must admit I'm a bit sceptical
That's plain! ;-)
about functions when they start having more than 3-4 arguments; there is almost always some abstraction lurking just waiting to be done.
Maybe so, but there are plenty of other good arguments for named parameters. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

"David Abrahams" <dave@boost-consulting.com> wrote in message news:uekiyo6n5.fsf@boost-consulting.com... | "Thorsten Ottosen" <nesotto@cs.auc.dk> writes: | | > I think it should not be accepted. My main motivation is that I | > think it promotes an unsound growth in function interfaces. | | Like having support for derivation promotes an unsound reliance on | implementation inheritance and support for operators promotes abuse | of operator overloading? | | Features don't promote practices. I'm not sure I agree. I can just imagine all those C++ programmers who now thinks its cool to have functions with +5 parameters--afterall a respected library like boost has this cool library that allows it. So the fact that the feature exists and is part of boost will send the wrong message IMO. | > I also miss a comparison of other techniques (eg. bgl_named_params, | > named_constructor_pattern) | | I don't know about the latter. Never heard of it. But I'm guessing | you're talking about something like: | | f(slew_is(.799), score_is(55)) | | If so, either it allows arbitrary parameter ordering and aside from | the use of () instead of = its advantages are equivalent to those of | our library, or it does not, and our library offers the advantage of | freeing the user/reader/maintainer from having to remember parameter | order. yeah, doesn't the bgl solution do that too? | > to do the same and an good explanation why this approach is | > superior. | | This approach is superior to bgl_named_params in part because it | avoids undue coupling and dependency in a library design. | While writing this book, we reconsidered the interface used for named | function parameter support. With a little experimentation we | discovered that it's possible to provide the ideal syntax by using | keyword objects with overloaded assignment operators: | | f(slew = .799, name = "z"); | | Not only is this syntax nicer for users, but adding a new parameter | name is easy for the writer of the library containing f, and it | doesn't cause any coupling. ok, I don't see the simple object.set_XX() to cause any coupling either. | > Let me elaborate. | > | > 1. Clearly, this is not nice | > | > window* w = new_window("alert", true, true, false, 77, 65); | | True, but sometimes it is appropriate. Even then, you only need two | parameters whose order is not obviously dictated by function to | justify the use of a named parameter interface: | | paint(hue = 1071, luminance = 10) | | vs. | | paint(1071, 10) again, you could just say painter p; p.set_hue( 1071 ); p.set_luminance( 10 ); p.paint(); | > However, the amount of work needed to code parameters for this is | > quite large. | | Really? Did you compare how much work it would be to use | BOOST_NAMED_PARAMS_FUN vs. what you're suggesting below? I think I read the tutorial and that function was not mentioned in there, was it? | > A simple, effective way is just to do it like this: | > | > window* w = new_window("alert"); | > w->set_resizable( true ); | > w->set_height( 77 ); | > .... | | How would you apply that idiom to the algorithms of the Boost Graph | Library? make a class like this: class bellman_ford_shortest_paths_executor { // one variable for each paramter, possibly using optional to save // performance void set_weight_map( weight_map& ); void set_distance_map( distance_map& ); ... execute( Graph& g, size_t n ); }; Depending on how it is designed, you can also pass such an object around wihout relying on function<> and you might be able to query about the execution context. Alternatively, some of the last N optional parameters should be encapsulated in some new concept; it might even turn out that this concept can be used many places and will give the library an increased abstraction-level. | > and you might provide chaining to make it more concise. In addition | > there is no forwarding problems and you might have these functions | > anyway. | | What do you mean by "you might have these functions anyway?" I would expect a window class to have a set_height() function no matter what. | > | > 2. what happens, then, if I don't have an object, but a free-standing | > function? | | That's the main use-case of our library! yet you show the construction of an object of type window. | > a) wrap the function in an object | > b) do as in http://www.boost.org/libs/graph/doc/bgl_named_params.html | > c) provide overloads of functions that pack related parameters together or | > simply add defaults | | It looks like you didn't really read through the library docs. You | just use the library in the normal way. I know, but I'm discussing alternatives...something I think the authors of the library should do before submitting. | > about functions when they start having more than 3-4 arguments; | > there is almost always some abstraction lurking just waiting to be | > done. | | Maybe so, but there are plenty of other good arguments for named | parameters. What are they? -Thorsten

"Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
"David Abrahams" <dave@boost-consulting.com> wrote in message news:uekiyo6n5.fsf@boost-consulting.com... | "Thorsten Ottosen" <nesotto@cs.auc.dk> writes: | | > I think it should not be accepted. My main motivation is that I | > think it promotes an unsound growth in function interfaces. | | Like having support for derivation promotes an unsound reliance on | implementation inheritance and support for operators promotes abuse | of operator overloading? | | Features don't promote practices.
I'm not sure I agree. I can just imagine all those C++ programmers who now thinks its cool to have functions with +5 parameters--afterall a respected library like boost has this cool library that allows it.
The C++ language already allows it. Did you complain about the fact that Boost.Function or Boost.Bind or Boost.Python or Boost.Type Traits supports more than five parameters? If not, why not?
So the fact that the feature exists and is part of boost will send the wrong message IMO.
The library isn't designed to allow you to use lots of parameters. It's designed to make code more readable and to allow you to take advantage of default arguments without regard to parameter order.
| > I also miss a comparison of other techniques (eg. bgl_named_params, | > named_constructor_pattern) | | I don't know about the latter. Never heard of it. But I'm guessing | you're talking about something like: | | f(slew_is(.799), score_is(55)) | | If so, either it allows arbitrary parameter ordering and aside from | the use of () instead of = its advantages are equivalent to those of | our library, or it does not, and our library offers the advantage of | freeing the user/reader/maintainer from having to remember parameter | order.
yeah, doesn't the bgl solution do that too?
Yes, but it has associated disadvantages as well, which I explained in my last posting.
| > to do the same and an good explanation why this approach is | > superior. | | This approach is superior to bgl_named_params in part because it | avoids undue coupling and dependency in a library design.
| While writing this book, we reconsidered the interface used for named | function parameter support. With a little experimentation we | discovered that it's possible to provide the ideal syntax by using | keyword objects with overloaded assignment operators: | | f(slew = .799, name = "z"); | | Not only is this syntax nicer for users, but adding a new parameter | name is easy for the writer of the library containing f, and it | doesn't cause any coupling.
ok, I don't see the simple object.set_XX() to cause any coupling either.
It doesn't cause any coupling. But it's uglier than what we're doing, and it requires state, and provides no advantages. One major problem with state (aside from stylistic concerns which I also consider valid) is: f(table = create_a_table(), name = "boxes") if create_a_table returns a vector, it can be passed by reference all the way through to f's implementation. With obj.set_table(create_a_table()), you have to store a copy of the vector.
| > Let me elaborate. | > | > 1. Clearly, this is not nice | > | > window* w = new_window("alert", true, true, false, 77, 65); | | True, but sometimes it is appropriate. Even then, you only need two | parameters whose order is not obviously dictated by function to | justify the use of a named parameter interface: | | paint(hue = 1071, luminance = 10) | | vs. | | paint(1071, 10)
again, you could just say
painter p; p.set_hue( 1071 ); p.set_luminance( 10 ); p.paint();
Yes, but it's ugly, stateful, and comparitively easy to misuse. And it's not even the moral equivalent of having named parameters, as far as I can tell. You're worried about "making it possible" to write functions with >5 parameters, but a class with >5 setter functions that isn't really meant to act like an ordinary class but instead as a poor man's substitute for a function with named parameters is surely worse than just having the feature!
| > However, the amount of work needed to code parameters for this is | > quite large. | | Really? Did you compare how much work it would be to use | BOOST_NAMED_PARAMS_FUN vs. what you're suggesting below?
I think I read the tutorial and that function was not mentioned in there, was it?
That macro is described in section 6.
| > A simple, effective way is just to do it like this: | > | > window* w = new_window("alert"); | > w->set_resizable( true ); | > w->set_height( 77 ); | > .... | | How would you apply that idiom to the algorithms of the Boost Graph | Library?
make a class like this:
class bellman_ford_shortest_paths_executor { // one variable for each paramter, possibly using optional to save // performance void set_weight_map( weight_map& ); void set_distance_map( distance_map& ); ... execute( Graph& g, size_t n ); };
That has all the problems mentioned earlier. It's ugly and stateful, it doesn't say what it means, and it's easy to misuse.
Depending on how it is designed, you can also pass such an object around wihout relying on function<> and you might be able to query about the execution context.
Alternatively, some of the last N optional parameters should be encapsulated in some new concept; it might even turn out that this concept can be used many places and will give the library an increased abstraction-level.
I've no clue what you're referring to in the first of those two paragraphs. I understand the second one, but w.r.t. to both of them it's no fair making arguments based on hypotheticals. Come up with concrete examples if you want to pursue these.
| > and you might provide chaining to make it more concise. In | > addition there is no forwarding problems and you might have | > these functions anyway. | | What do you mean by "you might have these functions anyway?"
I would expect a window class to have a set_height() function no matter what.
Okay. Maybe we should not have picked an example that constructed an object.
| > | > 2. what happens, then, if I don't have an object, but a free-standing | > function? | | That's the main use-case of our library!
yet you show the construction of an object of type window.
You only have to read past the introduction to see that the library applies to regular functions!
| > a) wrap the function in an object | > b) do as in http://www.boost.org/libs/graph/doc/bgl_named_params.html | > c) provide overloads of functions that pack related parameters | > together or | > simply add defaults | | It looks like you didn't really read through the library docs. You | just use the library in the normal way.
I know, but I'm discussing alternatives...something I think the authors of the library should do before submitting.
Oh, please. Do you really think I haven't spent years thinking about and discussing alternative approaches for named parameters?
| > about functions when they start having more than 3-4 arguments; | > there is almost always some abstraction lurking just waiting to be | > done. | | Maybe so, but there are plenty of other good arguments for named | parameters.
What are they?
I already gave them in several ways: they make code more readable and to allow you to take advantage of default arguments without regard to parameter order. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams wrote:
"Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
"David Abrahams" <dave@boost-consulting.com> wrote in message news:uekiyo6n5.fsf@boost-consulting.com...
True, but sometimes it is appropriate. Even then, you only need two parameters whose order is not obviously dictated by function to justify the use of a named parameter interface:
paint(hue = 1071, luminance = 10)
vs.
paint(1071, 10)
again, you could just say
painter p; p.set_hue( 1071 ); p.set_luminance( 10 ); p.paint();
Yes, but it's ugly, stateful, and comparitively easy to misuse. And it's not even the moral equivalent of having named parameters, as far as I can tell. You're worried about "making it possible" to write functions with >5 parameters, but a class with >5 setter functions that isn't really meant to act like an ordinary class but instead as a poor man's substitute for a function with named parameters is surely worse than just having the feature!
void paint( color const & c ); has the advantage that you can return a 'color' from a function or store it. Of course we have now restated the problem in terms of constructor parameters. Does the library support named constructor parameters, by the way? The equivalent window example would be window_desc wd = window_desc() .width( w ) .height( h ) .color( c ) .event_handler( e ); window_ptr pw = create_window( wd ); Again, if all your windows have width w and height h, you can extract this common part into a function: window_desc my_desc() { return window_desc().width( w ).height( h ); } and then use window_desc wd = my_desc() .color( c ) .event_handler( e ); window_ptr pw = create_window( wd );

Peter Dimov wrote: [snip]
Does the library support named constructor parameters, by the way?
Yes, by creating forwarding constructors and delegating to some init function. struct X { typedef keywords<...> init_kw; template<class A0> X(A0 const& a0) { init(init_kw(a0)); } template<class A0, class A1> X(A0 const& a0, A1 const& a1) { init(init_kw(a0, a1)); } .. etc .. private: template<class Args> void init(Args const& args) { .... } }; It's harder if you need to use initializer lists. I guess you can do: template<class A0> X(A0 const& a0) : x(init_kw(a0)[x | 0]) , y(init_kw(a0)[y | 0]) { } template<class A0, class A0> X(A0 const& a0, A1 const& a1) : x(init_kw(a0,a1)[x | 0]) , y(init_kw(a0,a1)[y | 0]) { } Or add another level of inheritance to avoid repeating the extraction and the defaults. -- Daniel Wallin

"Peter Dimov" <pdimov@mmltd.net> writes:
David Abrahams wrote:
"Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
"David Abrahams" <dave@boost-consulting.com> wrote in message news:uekiyo6n5.fsf@boost-consulting.com...
True, but sometimes it is appropriate. Even then, you only need two parameters whose order is not obviously dictated by function to justify the use of a named parameter interface:
paint(hue = 1071, luminance = 10)
vs.
paint(1071, 10)
again, you could just say
painter p; p.set_hue( 1071 ); p.set_luminance( 10 ); p.paint();
Yes, but it's ugly, stateful, and comparitively easy to misuse. And it's not even the moral equivalent of having named parameters, as far as I can tell. You're worried about "making it possible" to write functions with >5 parameters, but a class with >5 setter functions that isn't really meant to act like an ordinary class but instead as a poor man's substitute for a function with named parameters is surely worse than just having the feature!
void paint( color const & c );
has the advantage that you can return a 'color' from a function or store it.
Yes, of course. I like the idea of a color abstraction. But often the parameters don't form a neat little package like "color."
Of course we have now restated the problem in terms of constructor parameters. Does the library support named constructor parameters, by the way?
Of course; an example of that usage is exactly what Thorsten was complaining about in the first place. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams <dave <at> boost-consulting.com> writes:
"Thorsten Ottosen" <nesotto <at> cs.auc.dk> writes:
"David Abrahams" <dave <at> boost-consulting.com> wrote in message
ok, I don't see the simple object.set_XX() to cause any coupling either.
It doesn't cause any coupling. But it's uglier than what we're doing, and it requires state, and provides no advantages.
AFAICT, not more state than that already needed by the parameters of the function.
One major problem with state (aside from stylistic concerns which I also consider valid) is:
f(table = create_a_table(), name = "boxes")
if create_a_table returns a vector, it can be passed by reference all the way through to f's implementation. With obj.set_table(create_a_table()), you have to store a copy of the vector.
why can't you store a reference?
again, you could just say
painter p; p.set_hue( 1071 ); p.set_luminance( 10 ); p.paint();
Yes, but it's ugly, stateful, and comparitively easy to misuse. And it's not even the moral equivalent of having named parameters, as far as I can tell. You're worried about "making it possible" to write functions with >5 parameters, but a class with >5 setter functions that isn't really meant to act like an ordinary class
classes have many roles, its hard to say what constitutes "an ordinary".
but instead as a poor man's substitute for a function with named parameters is surely worse than just having the feature!
I'm trying to discuss alternatives without elevating one to be a superior feature. There is some overlap, but a class approach means that you can do the same operation except for feature X now = something easily. And you can pass the object around without using function<> or bind() (which would loose the defaults and explicit-naming, a significant drawback)
| It looks like you didn't really read through the library docs. You | just use the library in the normal way.
I know, but I'm discussing alternatives...something I think the authors of the library should do before submitting.
Oh, please. Do you really think I haven't spent years thinking about and discussing alternative approaches for named parameters?
No, I believe you have done that. However, I don't know what you're thinking and what alternatives you have been discussing. There is no mention of any alternatives in the docs. The review manager had to mention bgl_named_parameters himself. So here's the situation: you believe named parameters is the thing; I'm not convinced.
I already gave them in several ways: they make code more readable and to allow you to take advantage of default arguments without regard to parameter order.
And the drawbacks are - wierd macro definition must be used for functions - possible performance drop? - advantage is removed when passing the object around via function<> - introduces forwardng problem - can potentially lead to large parameter lists That is enough for me to start looking for alternatives which have the same benefits and a new set of drawbacks. I wouldn't conclude as you have done, that named parameters is the best solution. -Thorsten

Thorsten Ottosen <nesotto@cs.auc.dk> writes:
David Abrahams <dave <at> boost-consulting.com> writes:
"Thorsten Ottosen" <nesotto <at> cs.auc.dk> writes:
"David Abrahams" <dave <at> boost-consulting.com> wrote in message
ok, I don't see the simple object.set_XX() to cause any coupling either.
It doesn't cause any coupling. But it's uglier than what we're doing, and it requires state, and provides no advantages.
AFAICT, not more state than that already needed by the parameters of the function.
Yes, it requires more state. The value passed to set_XXX has to be stored somewhere.
One major problem with state (aside from stylistic concerns which I also consider valid) is:
f(table = create_a_table(), name = "boxes")
if create_a_table returns a vector, it can be passed by reference all the way through to f's implementation. With obj.set_table(create_a_table()), you have to store a copy of the vector.
why can't you store a reference?
Because you'd be storing a reference to a temporary, which would have evaporated by the time you get to actually initiate the call.
again, you could just say
painter p; p.set_hue( 1071 ); p.set_luminance( 10 ); p.paint();
Yes, but it's ugly, stateful, and comparitively easy to misuse. And it's not even the moral equivalent of having named parameters, as far as I can tell. You're worried about "making it possible" to write functions with >5 parameters, but a class with >5 setter functions that isn't really meant to act like an ordinary class
classes have many roles, its hard to say what constitutes "an ordinary".
but instead as a poor man's substitute for a function with named parameters is surely worse than just having the feature!
I'm trying to discuss alternatives without elevating one to be a superior feature.
It's not a question of which feature is superior, but of which one more directly expresses programmer intent. You can write loops with goto, but a looping construct is a better expression of that intent.
There is some overlap, but a class approach means that you can do the same operation except for feature X now = something easily. And you can pass the object around without using function<> or bind()
I don't see what those have to do with anything.
(which would loose the defaults and explicit-naming, a significant drawback)
Not true. Bind would not loose the explicit naming or the defaults. As for function<>, no alternative approach you can name would preserve them, either.
| It looks like you didn't really read through the library | docs. You just use the library in the normal way.
I know, but I'm discussing alternatives...something I think the authors of the library should do before submitting.
Oh, please. Do you really think I haven't spent years thinking about and discussing alternative approaches for named parameters?
No, I believe you have done that.
However, I don't know what you're thinking and what alternatives you have been discussing. There is no mention of any alternatives in the docs.
I didn't think that it was important for the library docs to discuss alternative approaches. We have almost no discussion of alternative approaches in the body of any of our libraries' documentation. Yes, there are occasional rationales for specific design choices.
The review manager had to mention bgl_named_parameters himself.
So here's the situation: you believe named parameters is the thing;
It's _a_ thing.
I'm not convinced.
Okay, I can accept that, and will stop trying to convince you.
I already gave them in several ways: they make code more readable and to allow you to take advantage of default arguments without regard to parameter order.
And the drawbacks are
- wierd macro definition must be used for functions
No, it's not required. That's a shorthand.
- possible performance drop?
Any method that requires stateful storage will incur a greater performance drop than building a struct of references.
- advantage is removed when passing the object around via function<>
What object? Our approach just uses functions. You're the one who keeps wanting to introduce objects ;-) Mr. Gladyshev has suggested that named parameter support ought to be somehow merged with boost::function. We might consider that if the library is accepted.
- introduces forwardng problem
Also solves forwarding problem ;-). There's no problem at all if you just use named parameters.
- can potentially lead to large parameter lists
How?
That is enough for me to start looking for alternatives which have the same benefits and a new set of drawbacks. I wouldn't conclude as you have done, that named parameters is the best solution.
To what? Named parameters aren't as much a solution to anything as an expressive programming idiom that has been used very successfully in all kinds of languages and environments. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

"David Abrahams" <dave@boost-consulting.com> wrote in message news:u4qjqq4is.fsf@boost-consulting.com... | Thorsten Ottosen <nesotto@cs.auc.dk> writes: | > AFAICT, not more state than that already needed by the parameters | > of the function. | | Yes, it requires more state. The value passed to set_XXX has to be | stored somewhere. but one can store a reference if that is beneficial. | >> One major problem with state (aside from stylistic concerns which I | >> also consider valid) is: | >> | >> f(table = create_a_table(), name = "boxes") | >> | >> if create_a_table returns a vector, it can be passed by reference all | >> the way through to f's implementation. With | >> obj.set_table(create_a_table()), you have to store a copy of the | >> vector. | > | > why can't you store a reference? | | Because you'd be storing a reference to a temporary, which would have | evaporated by the time you get to actually initiate the call. you must be confusing move-semantics with this situation, or I'm missing something. there should be no difference between storing a temporary implicitly or explicitly...it's still there and still takes up stack-space no matter what. If the function does return a reference, then simply store that instead of copying the whole object. | > (which would loose the defaults and explicit-naming, a significant | > drawback) | | Not true. Bind would not loose the explicit naming or the defaults. | As for function<>, no alternative approach you can name would | preserve them, either. if you have an object that wraps an algorithms somehow, you can just pass that object around by its name. For example, you can pass the painter object around and change state explicitly by p.set_hue( ... ); So the mecanism that is used to document intend does not disappear as it otherwise would. | >> Oh, please. Do you really think I haven't spent years thinking | >> about and discussing alternative approaches for named parameters? | > | > No, I believe you have done that. | > | > However, I don't know what you're thinking and what alternatives you | > have been discussing. There is no mention of any alternatives in the | > docs. | | I didn't think that it was important for the library docs to discuss | alternative approaches. We have almost no discussion of alternative | approaches in the body of any of our libraries' documentation. Yes, | there are occasional rationales for specific design choices. IMO, any boost library should be able to answer why it is the best library for the given task. Sometimes its easy to do so; sometimes its hard. | > - possible performance drop? | | Any method that requires stateful storage will incur a greater | performance drop than building a struct of references. not if you store big objects by reference. And that has been my point all along. | > That is enough for me to start looking for alternatives which have | > the same benefits and a new set of drawbacks. I wouldn't conclude as | > you have done, that named parameters is the best solution. | | To what? | | Named parameters aren't as much a solution to anything as an | expressive programming idiom that has been used very successfully in | all kinds of languages and environments. Selfdocumenting code is a really fine goal; there are just other ways to do it. My no vote simple reflects that I don't the see the current library's benefit/problem ratio as big enough. For example, the self-documentation of void paint( int luminance, int hue ); can be done in many ways. #1 your library paint( luminance = 42, hue = 10 ); #2 variables int luminance = 42; int hue = 10; paint( luminance, hue ); #3 objects painter p; p.set_luminance( 42 ); p.set_hue( 10 ); p.paint(); Adding to this, many editors can be pursuaded to show argument list so that when you type "paint(", you get to see the arguments which for some makes paint( 42, 10) ok. The other issue related to default arguments, and not to self-documenting function calls. Currently it is really a pain in the *** to have an object with many default arguments. Your library makes that managable; but I argue functions with many arguments should be refactored since their level of abstraction is getting too low; allowing many default arguments juat makes the situation worse. -Thorsten

Thorsten Ottosen wrote:
[...] Currently it is really a pain in the *** to have an object with many default arguments. Your library makes that managable; but I argue functions with many arguments should be refactored since their level of abstraction is getting too low; allowing many default arguments juat makes the situation worse.
You keep saying that, but I wonder if you actually have to write any real-world code, or if perhaps you are simply an academic? I have many instances of functions with numerous arguments for which I offer no apology and would resist any attempt by you or anyone else to refactor into functions with fewer arguments. There is such a thing as too much abstraction. Next you will go around insisting that everyone uses your int wrapper which automatically detects overflow. Sometimes, having a function with many arguments is simply the best solution to a problem, just like sometimes, goto is the best solution. Just because it isn't always the best solution doesn't mean that it is never the best solution. To assert otherwise would require me to throw a logic error. ;) Dave

"David B. Held" <dheld@codelogicconsulting.com> wrote in message news:cndvjs$sm8$1@sea.gmane.org... | Thorsten Ottosen wrote: | > [...] | > Currently it is really a pain in the *** to have an object with many default | > arguments. Your library makes that managable; but I argue | > functions with many arguments should be refactored since their level of | > abstraction is getting too low; allowing many default arguments | > juat makes the situation worse. | | You keep saying that, but I wonder if you actually have to write any | real-world code, or if perhaps you are simply an academic? A have experience with both kinds of code (how you define the difference, I don't know) and have worked with multiple languages that allows default arguments. | I have | many instances of functions with numerous arguments for which I offer | no apology and would resist any attempt by you or anyone else to | refactor into functions with fewer arguments. but you would rather refactor into using named parameters? | There is such a thing as | too much abstraction. perhaps. If you mean having private data and public function in classes without an invariant, I agree. | Next you will go around insisting that everyone | uses your int wrapper which automatically detects overflow. I'm not insisting that you must refactor you N parameter function; I'm merely saying that chances are that it can be done and that it can improve the overall quality of your code. If you refuse to refactor your code, then so be it. -Thorsten

Thorsten Ottosen wrote:
[...] but you would rather refactor into using named parameters?
If the code were used by people other than me, possibly so.
[...] I'm not insisting that you must refactor you N parameter function; I'm merely saying that chances are that it can be done and that it can improve the overall quality of your code. [...]
Of course it can be done. The question all boils down to whether it would improve the quality of the code. Consider the most common example that someone else gave: c'tors. Now, are you suggesting that types not have many data members, and should be decomposed until each type has, say, less than 5 members? If not, are you suggesting that we construct such types partially, and then fill in the rest later, so that we can avoid functions with many arguments? There are many cases where you simply have a lot of data coming from a lot of sources that need to go into one function, and encapsulating that data or refactoring the function is simply not appropriate or ideal. You must write magical code if you have never encountered such situations. Dave

"David B. Held" <dheld@codelogicconsulting.com> wrote in message news:cngf20$cmb$1@sea.gmane.org... | Thorsten Ottosen wrote: | > [...] | > I'm not insisting that you must refactor you N parameter function; I'm merely | > saying that | > chances are that it can be done and that it can improve the overall quality of | > your code. | > [...] | | Of course it can be done. The question all boils down to whether it | would improve the quality of the code. yes. | Consider the most common example | that someone else gave: c'tors. Now, are you suggesting that types not | have many data members, and should be decomposed until each type has, | say, less than 5 members? that seems to be a very rigid system, so "no", I'm not suggesting that. | If not, are you suggesting that we construct | such types partially, and then fill in the rest later, so that we can | avoid functions with many arguments? that's pretty close. For those aguments that has reasonable defaults, I'm suggesting that the defaults can be overridden explicitly later. But to be fair, this idea is far from new and so I'm not the person that should be given credit. | There are many cases where you | simply have a lot of data coming from a lot of sources that need to go | into one function, and encapsulating that data or refactoring the | function is simply not appropriate or ideal. You must write magical | code if you have never encountered such situations. well, I do encounter them (though rarely). Some php stuff I currently work on contains: function leafNode2( $name1, $name2, $link1, $link2, $target1 = selfTarget, $target2 = selfTaget, $title1 = "", $title2 = "", $root = root ); I'm not proud of it, but I just havn't had the time to refactor it yet. It should be obvious that all alguments ending with 1 and 2 should be grouped somehow, maybe as class Link { $name, $url; $target = selfTarget; $title = ""; ... } The interface should probably be function leafNode( $link1, $link2, $root = root ); Doing such a refactoring takes time, but it will surely make the abstraction level of my application much higher. Just for fun, I would like to see what functions you have which you claim should not be refactored. Maybe you're right that doing a refactoring would simply ont be good. -Thorsten

-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Thorsten Ottosen Sent: Wednesday, November 17, 2004 6:23 PM To: boost@lists.boost.org Subject: [boost] Re: [Review] Named parameters library
that's pretty close. For those aguments that has reasonable defaults, I'm suggesting that the defaults can be overridden explicitly later.
But to be fair, this idea is far from new and so I'm not the person that should be given credit.
The point I've tried to make is that sometimes there is no second chance to construct an object. At times, I have one shot at initializing hardware. There is no second chance. I can't just default construct and change the settings later. At lost not without completely shutting down the hardware and restarting it. That's just not an option. I can't refactor the hardware. In addition, what about dealing with libraries? For example, the BGL has a function named brandes_betweenness_centrality that takes 9 parameters. I would be hesitant to try to refactor the BGL locally on my machine. My other option is construct a separate class in order to collect the parameters and make the call later. I think the custom forwarding class is, although similar, inferior. -- Noah

"Noah Stein" <noah@acm.org> wrote in message news:200411171737790.SM00936@enki... | > -----Original Message----- | > From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] | > On Behalf Of Thorsten Ottosen | > Sent: Wednesday, November 17, 2004 6:23 PM | > To: boost@lists.boost.org | > Subject: [boost] Re: [Review] Named parameters library | | > that's pretty close. For those aguments that has reasonable defaults, I'm | > suggesting that | > the defaults can be overridden explicitly later. | > | > But to be fair, this idea is far from new and so I'm not the person that | > should be given credit. | | The point I've tried to make is that sometimes there is no second chance to | construct an object. At times, I have one shot at initializing hardware. | There is no second chance. I can't just default construct and change the | settings later. At lost not without completely shutting down the hardware | and restarting it. That's just not an option. I can't refactor the | hardware. Ok, I believe you, but I don't understand it. Here's why: a constructor is nothing more than a C function in some form. It takes some paramters and perhaps access global data. So what do you mean by "one shot"? Don't you get to decide what call that is the "one shot" (?) like in Foo f; f.bar1(); ... f.barN(); f.commit(); // this can is crusial, the others not | In addition, what about dealing with libraries? For example, the BGL has a | function named brandes_betweenness_centrality that takes 9 parameters. I | would be hesitant to try to refactor the BGL locally on my machine. so would I. | My | other option is construct a separate class in order to collect the | parameters and make the call later. I think the custom forwarding class is, | although similar, inferior. ok. -Thorsten

Thorsten Ottosen wrote:
"David B. Held" <dheld@codelogicconsulting.com> wrote in message news:cngf20$cmb$1@sea.gmane.org... [...] | If not, are you suggesting that we construct | such types partially, and then fill in the rest later, so that we can | avoid functions with many arguments?
that's pretty close.
That's a pretty silly policy. What Noah means by "one shot" is that some classes have little things called "invariants", and deferring initialization till after the c'tor may well break those.
[...] But to be fair, this idea is far from new and so I'm not the person that should be given credit.
Oh, please...take the credit. ;)
[...] function leafNode2( $name1, $name2, $link1, $link2, $target1 = selfTarget, $target2 = selfTaget, $title1 = "", $title2 = "", $root = root );
I'm not proud of it, but I just havn't had the time to refactor it yet. It should be obvious that all alguments ending with 1 and 2 should be grouped somehow, maybe as
class Link { $name, $url; $target = selfTarget; $title = ""; ... }
Maybe so. But suppose you didn't need to pass two links? Then is it so obvious that those arguments need to be encapsulated into a single *EXTRA* class?
[...] Doing such a refactoring takes time, but it will surely make the abstraction level of my application much higher.
That does not imply a benefit to me. If it did, then I would argue that a pointer-to-a-pointer is better than a pointer, because it has a higher level of abstraction (and gives you more things to do with the intermediate pointer). By that reasoning, a pointer-to-an-X+1 is "better" than a pointer-to-an-X, and therefore, we should all use pointers that have an infinite level of nesting, to write truly divine code. Of course, the flaw in this argument is the notion of "overgeneralization."
Just for fun, I would like to see what functions you have which you claim should not be refactored. Maybe you're right that doing a refactoring would simply ont be good.
Consider *gasp* data entry applications, where you have records with many fields, which should all be initialized at once. Here's one for some mail tracking software: bool TInputForm::Add(AnsiString ID, double Weight, AnsiString Zip, byte Zone, AnsiString Extra, bool MakeVisible); Now, you might say: "But you should put all those arguments in some kind of package class before passing it to the Add() function." However, that is exactly the *point* of the Add() function! These pieces of data are collected elsewhere, and it is the responsibility of the Add() function to integrate them into a single package object. Yes, it is possible to refactor it into classes that only take a few fields at a time, but I can guarantee that such a refactoring will not be an improvement. I could give examples from lots of other business apps that need to deal with records, some that have far more fields than this. void __fastcall TInputForm::OnInsert(int ManifestID, MDTP::TMailClass Class, AnsiString ID, double Weight, AnsiString Zip, byte Zone, double Postage, AnsiString Extra, AnsiString Username, TDateTime Entered); Here's an event handler that gets called *before* the Add() function above, and thus gets the raw data, and not the packaged object. It wouldn't even make sense to refactor this function. There are no sensible defaults, since the data is always supposed to be passed from the data entry source. There are 10 arguments, for none of which I make apology. It does not now nor did it ever occur to me to attempt to reduce the number of arguments for this function. Doing so would not improve the quality of the code, because there is absolutely no reason to generalize this function. It has a very specific and proscribed purpose, and there is no anticipation of needing to expand its repertoire. Now, in this case, we are looking at functions that only get called from a small number of places (or even just one). Thus, named parameters don't really help any. But the fact is, long argument lists are not intrinsically evil. I could give numerous other examples, but I hope I've made my point. Dave

"David B. Held" <dheld@codelogicconsulting.com> wrote in message news:cnje4t$j7j$1@sea.gmane.org... | Thorsten Ottosen wrote: | | > "David B. Held" <dheld@codelogicconsulting.com> wrote in message | > news:cngf20$cmb$1@sea.gmane.org... | > [...] | > | If not, are you suggesting that we construct | > | such types partially, and then fill in the rest later, so that we can | > | avoid functions with many arguments? | > | > that's pretty close. | | That's a pretty silly policy. What Noah means by "one shot" is that | some classes have little things called "invariants", and deferring | initialization till after the c'tor may well break those. I doubt that is what he meant. If you have aguments that are optional, then one should be able to set those later. Not providing them cannot possible break an invariant. A major reason for using classes instead of tuples of data is that now one can start controlling invariants. | > [...] | > function leafNode2( $name1, $name2, $link1, $link2, | > $target1 = selfTarget, $target2 = selfTaget, | > $title1 = "", $title2 = "", $root = root ); | > | > I'm not proud of it, but I just havn't had the time to refactor it yet. It | > should be obvious that all alguments ending with 1 and 2 should be grouped | > somehow, maybe as | > | > class Link | > { | > $name, | > $url; | > $target = selfTarget; | > $title = ""; | > ... | > } | | Maybe so. But suppose you didn't need to pass two links? Then is it | so obvious that those arguments need to be encapsulated into a single | *EXTRA* class? It depends, I guess. I think in this case it would be a good candidate. | > [...] | > Doing such a refactoring takes time, but it will surely make the abstraction | > level of my application much higher. | | That does not imply a benefit to me. If it did, then I would argue | that a pointer-to-a-pointer is better than a pointer, because it has | a higher level of abstraction (and gives you more things to do with | the intermediate pointer). that is not the type of abstraction we're talking about | By that reasoning, by your reasoning :-) | a pointer-to-an-X+1 is | "better" than a pointer-to-an-X, and therefore, we should all use | pointers that have an infinite level of nesting, to write truly divine | code. Of course, the flaw in this argument is the notion of | "overgeneralization." | | > Just for fun, I would like to see what functions you have which you claim | > should not be refactored. Maybe you're right that doing a refactoring | > would simply ont be good. | | Consider *gasp* data entry applications, where you have records with | many fields, which should all be initialized at once. Here's one for | some mail tracking software: | | bool TInputForm::Add(AnsiString ID, double Weight, AnsiString Zip, | byte Zone, AnsiString Extra, bool MakeVisible); | | Now, you might say: "But you should put all those arguments in some | kind of package class before passing it to the Add() function." why not only those that tend to go together; it might not be all. | However, that is exactly the *point* of the Add() function! These | pieces of data are collected elsewhere, and it is the responsibility | of the Add() function to integrate them into a single package object. | Yes, it is possible to refactor it into classes that only take a few | fields at a time, but I can guarantee that such a refactoring will | not be an improvement. how can you guarantee that? What is the abstraction that add() package the items together in? Why shouldn't one be able to pass the data around in a that "package" form?
From what I can infer then it's not impossible that one could change the function into
class Package { AnsiString ID; ... public: set_id( AnsiString const& r ); }; bool TInputForm::Add( const Package& p, bool MakeVisible ); | I could give examples from lots of other | business apps that need to deal with records, some that have far | more fields than this. | | void __fastcall TInputForm::OnInsert(int ManifestID, | MDTP::TMailClass Class, AnsiString ID, double Weight, | AnsiString Zip, byte Zone, double Postage, AnsiString Extra, | AnsiString Username, TDateTime Entered); | | Here's an event handler that gets called *before* the Add() function | above, and thus gets the raw data, and not the packaged object. | It wouldn't even make sense to refactor this function. There are | no sensible defaults, since the data is always supposed to be passed | from the data entry source. There are 10 arguments, for none of which | I make apology. I'm not apologizing for all the bad code I have written...but I am trying to do better the next time. | It does not now nor did it ever occur to me to attempt | to reduce the number of arguments for this function. Doing so would | not improve the quality of the code, because there is absolutely no | reason to generalize this function. It has a very specific and | proscribed purpose, and there is no anticipation of needing to expand | its repertoire. | | Now, in this case, we are looking at functions that only get called | from a small number of places (or even just one). Thus, named | parameters don't really help any. But the fact is, long argument | lists are not intrinsically evil. are they intrinsically good? | I could give numerous other | examples, but I hope I've made my point. Thinking more about it I can see that the database layer I have in an application is just a lot of functions with many arguments. So I can find many example too. If I had encapsulated this in classes, what would the effect be? Well, I had to do this: 1. pack the values in an object 2. send that object along to a function 3. unpack the data from the object inside the function And I guess that is what you think is plain stupid. My point has been, however, that once you start making abstractions you 1. simplify interfaces 2. makes it possible to define invariants for your data and hence localize error-checks 3. adds type-safety to the application (but I guess we don't have to go through the benefits of OO here.) So if I were to do it all over, I would probably map my database tables to classes instead of passing tuples of strings and intergers around. -Thorsten

Thorsten Ottosen wrote:
"David B. Held" <dheld@codelogicconsulting.com> wrote in message news:cnje4t$j7j$1@sea.gmane.org... | [...] | However, that is exactly the *point* of the Add() function! These | pieces of data are collected elsewhere, and it is the responsibility | of the Add() function to integrate them into a single package object. | Yes, it is possible to refactor it into classes that only take a few | fields at a time, but I can guarantee that such a refactoring will | not be an improvement.
how can you guarantee that? What is the abstraction that add() package the items together in? Why shouldn't one be able to pass the data around in a that "package" form?
Because I have to do processing on the arguments in order to build a proper package object! And that is exactly what Add() does. So you can't build a package object from the arguments and pass it to Add(), because that is the problem Add() is solving in the first place! Any refactoring will simply add unnecessary complexity to a problem. I am arguing that Add(), with its unashamedly numerous arguments, is irreducibly complex. Any attempt to refactor it will *increase* complexity, not reduce it through abstraction.
[...] I'm not apologizing for all the bad code I have written...but I am trying to do better the next time.
I'm not apologizing because I don't recognize that I've written bad code.
| [...] | Now, in this case, we are looking at functions that only get called | from a small number of places (or even just one). Thus, named | parameters don't really help any. But the fact is, long argument | lists are not intrinsically evil.
are they intrinsically good?
Is mowing the lawn intrinsically good or intrinsically evil? If you can confidently answer that question, I know a few philosophers that would like to talk to you. Some things are amoral, and long argument lists is one of them.
[...] 1. pack the values in an object 2. send that object along to a function 3. unpack the data from the object inside the function
And I guess that is what you think is plain stupid.
Yes, because you had to either: pack the values into an object having a c'tor with a lot of arguments (sound familiar?), or you had to create an object and set the members later, which sounds downright foolish to merely avoid long argument lists.
My point has been, however, that once you start making abstractions you
1. simplify interfaces 2. makes it possible to define invariants for your data and hence localize error-checks 3. adds type-safety to the application
(but I guess we don't have to go through the benefits of OO here.)
And my point is, in order to implement those abstractions, you very often encounter the very argument lists that you were trying to avoid!! In fact, preserving invariants implies that you *need* to do all-at-once initialization in the c'tor!
[...] So if I were to do it all over, I would probably map my database tables to classes instead of passing tuples of strings and intergers around.
That depends on the application. If the strings and ints make sense in a class, by all means pack them into a class. But when the data gets entered the first time, from the "outside world", *someone*, logically speaking, has to pack that data into a class the first time; and I don't see anything wrong with that being a single function with a long argument list. Nothing you have said thus far convinces me otherwise. Dave

"David B. Held" <dheld@codelogicconsulting.com> wrote in message news:cnjtn1$ho4$1@sea.gmane.org... | Thorsten Ottosen wrote: | > "David B. Held" <dheld@codelogicconsulting.com> wrote in message | because that is the problem Add() is solving in the first place! Any | refactoring will simply add unnecessary complexity to a problem. I am | arguing that Add(), with its unashamedly numerous arguments, is | irreducibly complex. Any attempt to refactor it will *increase* | complexity, not reduce it through abstraction. I'm just not convinced. | > | Now, in this case, we are looking at functions that only get called | > | from a small number of places (or even just one). Thus, named | > | parameters don't really help any. But the fact is, long argument | > | lists are not intrinsically evil. | > | > are they intrinsically good? | | Is mowing the lawn intrinsically good or intrinsically evil? If you | can confidently answer that question, I know a few philosophers that | would like to talk to you. Some things are amoral, and long argument | lists is one of them. it was you who used the term "intrinsically evil", not me. | > [...] | > 1. pack the values in an object | > 2. send that object along to a function | > 3. unpack the data from the object inside the function | > | > And I guess that is what you think is plain stupid. | | Yes, because you had to either: pack the values into an object having | a c'tor with a lot of arguments (sound familiar?), or you had to create | an object and set the members later, which sounds downright foolish to | merely avoid long argument lists. | > My point has been, however, that once you start making abstractions you | > | > 1. simplify interfaces | > 2. makes it possible to define invariants for your data and hence localize | > error-checks | > 3. adds type-safety to the application | > | > (but I guess we don't have to go through the benefits of OO here.) | | And my point is, in order to implement those abstractions, you very | often encounter the very argument lists that you were trying to avoid!! | In fact, preserving invariants implies that you *need* to do all-at-once | initialization in the c'tor! depends on the invariant, I guess. | > [...] | > So if I were to do it all over, I would probably map my database tables to | > classes instead of passing tuples of strings and intergers around. | | That depends on the application. If the strings and ints make sense | in a class, by all means pack them into a class. But when the data | gets entered the first time, from the "outside world", *someone*, | logically speaking, has to pack that data into a class the first time; | and I don't see anything wrong with that being a single function with | a long argument list. Nothing you have said thus far convinces me | otherwise. I'm not surprised; its pretty hard to convince you :-) -Thorsten

"Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
"David Abrahams" <dave@boost-consulting.com> wrote in message news:u4qjqq4is.fsf@boost-consulting.com... | Thorsten Ottosen <nesotto@cs.auc.dk> writes:
| > AFAICT, not more state than that already needed by the parameters | > of the function. | | Yes, it requires more state. The value passed to set_XXX has to be | stored somewhere.
but one can store a reference if that is beneficial.
| >> One major problem with state (aside from stylistic concerns which I | >> also consider valid) is: | >> | >> f(table = create_a_table(), name = "boxes") | >> | >> if create_a_table returns a vector, it can be passed by reference all | >> the way through to f's implementation. With | >> obj.set_table(create_a_table()), you have to store a copy of the | >> vector. | > | > why can't you store a reference? | | Because you'd be storing a reference to a temporary, which would have | evaporated by the time you get to actually initiate the call.
you must be confusing move-semantics with this situation, or I'm missing something.
You're missing something.
there should be no difference between storing a temporary implicitly or explicitly...it's still there and still takes up stack-space no matter what.
Yes, but your scheme requires copying that temporary into a new object, where it is stored again. The proposed library allows a reference to be bound to the temporary with no copying. And I seriously doubt there is a compiler in existence today that can elide that copy, because of the restrictions on where the object must be stored.
If the function does return a reference, then simply store that instead of copying the whole object.
Yes, but now you have to tell the user "don't call me with anything that returns a temporary, even though I'll compile it without complaint." f_er f; f.set_table(create_a_table()); // whoops, the table disappears f.f() // BOOM or whatever.
| > (which would loose the defaults and explicit-naming, a significant | > drawback) | | Not true. Bind would not loose the explicit naming or the defaults. | As for function<>, no alternative approach you can name would | preserve them, either.
if you have an object that wraps an algorithms somehow, you can just pass that object around by its name.
riiiiiiight.....
For example, you can pass the painter object around and change state explicitly by p.set_hue( ... ); So the mecanism that is used to document intend does not disappear as it otherwise would.
...but that's not holding it inside a boost::function<>. The whole point of function<> is type erasure, and with it goes the information about how to process named parameters. This doesn't seem like a very well-reasoned argument you're making.
| I didn't think that it was important for the library docs to discuss | alternative approaches. We have almost no discussion of alternative | approaches in the body of any of our libraries' documentation. Yes, | there are occasional rationales for specific design choices.
IMO, any boost library should be able to answer why it is the best library for the given task. Sometimes its easy to do so; sometimes its hard.
Oh, that's simple. There aren't any alternative libraries for this task "out there."
| > - possible performance drop? | | Any method that requires stateful storage will incur a greater | performance drop than building a struct of references.
not if you store big objects by reference. And that has been my point all along.
And my point all along is that you can do that but you need to tell users to be careful with temporaries in that case.
| > That is enough for me to start looking for alternatives which | > have the same benefits and a new set of drawbacks. I wouldn't | > conclude as you have done, that named parameters is the best | > solution. | | To what? | | Named parameters aren't as much a solution to anything as an | expressive programming idiom that has been used very successfully in | all kinds of languages and environments.
Self documenting code is a really fine goal; there are just other ways to do it. My no vote simple reflects that I don't the see the current library's benefit/problem ratio as big enough.
Fair enough. You're entitled to it. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

"David Abrahams" <dave@boost-consulting.com> wrote in message news:uwtwlb7yh.fsf@boost-consulting.com... | "Thorsten Ottosen" <nesotto@cs.auc.dk> writes: | | > "David Abrahams" <dave@boost-consulting.com> wrote in message | > news:u4qjqq4is.fsf@boost-consulting.com... | > | Thorsten Ottosen <nesotto@cs.auc.dk> writes: | > | > | > why can't you store a reference? | > | | > | Because you'd be storing a reference to a temporary, which would have | > | evaporated by the time you get to actually initiate the call. | > | > you must be confusing move-semantics with this situation, or I'm | > missing something. | | You're missing something. | | > there should be no difference between storing a temporary implicitly | > or explicitly...it's still there and still takes up stack-space no | > matter what. | | Yes, but your scheme requires copying that temporary into a new | object, where it is stored again. The proposed library allows a | reference to be bound to the temporary with no copying. And I | seriously doubt there is a compiler in existence today that can elide | that copy, because of the restrictions on where the object must be | stored. ah, yes I see it now. That is indeed a major benefit. Where in the standard are those restriction described? I mean I would have suspected that RVO could be extended to support classes like in this: struct X { Foo f; }; Foo foo(); X x; x.f = foo(); If I change the interface to set_foo( const Foo& r ) { f = r; } instead of a public member, it is slower, but otherwise not AFAICT (see attachment). -Thorsten begin 666 copy.cpp M(VEN8VQU9&4@/'9E8W1O<CX-"B-I;F-L=61E(#QI;W-T<F5A;3X-"@T*#0IS M=&0Z.G9E8W1O<CQI;G0^(&9O;R@I.PT*#0IT96UP;&%T93P@8VQA<W,@5B ^ M#0IL;VYG('!R:6YT*"!C;VYS="!6)B!V("D-"GL-"B @("!L;VYG(&P@/2 P M.PT*(" @( T*(" @(&9O<B@@<VEZ95]T(&D@/2 P.R!I("$]('8N<VEZ92@I M.R K*VD@*0T*(" @(" @("!L("L]('9;:5T[#0H-"B @("!R971U<FX@;#L@ M#0I]#0H-"G1E;7!L871E/"!C;&%S<R!6(#X-"G-T<G5C="!&;V\-"GL-"B @ M("!6('8[#0I].PT*#0HC:6YC;'5D92 \8F]O<W0O<')O9W)E<W,N:'!P/@T* M#0II;G0@;6%I;B@I#0I[#0H@(" @8V]N<W0@:6YT('-Z(#T@,3 P,# [#0H@ M(" @( T*(" @('L-"B @(" @(" @8F]O<W0Z.G!R;V=R97-S7W1I;65R('0[ M#0H-"B @(" @(" @;&]N9R!L(#T@,#L-"B @(" @(" @9F]R*"!I;G0@:2 ] M(# [(&D@(3T@<WH[("LK:2 I#0H@(" @(" @(" @("!L("L]('!R:6YT*"!F M;V\H*2 I.PT*#0H@(" @(" @('-T9#HZ8V]U=" \/"!L(#P\("(@(CL-"B @ M("!]#0H-"B @("![#0H@(" @(" @(&)O;W-T.CIP<F]G<F5S<U]T:6UE<B!T M.PT*#0H@(" @(" @(&QO;F<@;" ](# [#0H-"B @(" @(" @9F]R*"!I;G0@ M:2 ](# [(&D@(3T@<WH[("LK:2 I#0H@(" @(" @('L-"B @(" @(" @(" @ M("\O8V]N<W0@<W1D.CIV96-T;W(\:6YT/B8@=B ](&9O;R@I.PT*(" @(" @ M(" @(" @+R]L("L]('!R:6YT*"!V("D[#0H@(" @(" @(" @("!&;V\\('-T M9#HZ=F5C=&]R/&EN=#X@/B!F.PT*(" @(" @(" @(" @9BYV(#T@9F]O*"D[ M#0H@(" @(" @(" @("!L("L]('!R:6YT*"!F+G8@*3L-"@T*(" @(" @("!] M#0H-"B @(" @(" @<W1D.CIC;W5T(#P\(&P@/#P@(B B.PT*(" @('T-"@T* M?0T*#0IS=&0Z.G9E8W1O<CQI;G0^(&9O;U]I;7!L*"D-"GL-"B @("!C;VYS M="!I;G0@<WH@/2 S,# P.PT*(" @('-T9#HZ=F5C=&]R/&EN=#X@=CL-"B @ M("!F;W(H(&EN="!I(#T@,#L@:2 A/2!S>CL@*RMI("D-"B @(" @(" @=BYP M=7-H7V)A8VLH(&D@*3L-"B @("!R971U<FX@=CL-"GT-"@T*<W1D.CIV96-T M;W(\:6YT/B!F;V\H*0T*>PT*(" @('-T871I8R!S=&0Z.G9E8W1O<CQI;G0^ I('8H(&9O;U]I;7!L*"D@*3L-"B @("!R971U<FX@=CL-"GT-"@T*#0H` ` end

"Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
"David Abrahams" <dave@boost-consulting.com> wrote in message news:uwtwlb7yh.fsf@boost-consulting.com... | "Thorsten Ottosen" <nesotto@cs.auc.dk> writes: | | > "David Abrahams" <dave@boost-consulting.com> wrote in message | > news:u4qjqq4is.fsf@boost-consulting.com... | > | Thorsten Ottosen <nesotto@cs.auc.dk> writes: | >
| > | > why can't you store a reference? | > | | > | Because you'd be storing a reference to a temporary, which would have | > | evaporated by the time you get to actually initiate the call. | > | > you must be confusing move-semantics with this situation, or I'm | > missing something. | | You're missing something. | | > there should be no difference between storing a temporary implicitly | > or explicitly...it's still there and still takes up stack-space no | > matter what. | | Yes, but your scheme requires copying that temporary into a new | object, where it is stored again. The proposed library allows a | reference to be bound to the temporary with no copying. And I | seriously doubt there is a compiler in existence today that can elide | that copy, because of the restrictions on where the object must be | stored.
ah, yes I see it now. That is indeed a major benefit.
Where in the standard are those restriction described?
It's described by omission. The standard only gives permission to elide copies and not assignments. Therefore if you construct a subobject and then you assign into it, you have to do the assignment.
I mean I would have suspected that RVO could be extended to support classes like in this:
struct X { Foo f; };
Foo foo();
X x; x.f = foo();
Forget it; it's an assignment.
If I change the interface to set_foo( const Foo& r ) { f = r; } instead of a public member, it is slower, but otherwise not AFAICT (see attachment).
It's still an assignment. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

"David Abrahams" <dave@boost-consulting.com> wrote in message news:usm786nvn.fsf@boost-consulting.com... | "Thorsten Ottosen" <nesotto@cs.auc.dk> writes: | > ah, yes I see it now. That is indeed a major benefit. | > until we have move-semantics, that is :-) | > Where in the standard are those restriction described? | | It's described by omission. The standard only gives permission to | elide copies and not assignments. Therefore if you construct a | subobject and then you assign into it, you have to do the assignment. | | > I mean I would have suspected that RVO could be extended to support classes | > like in this: | > | > struct X | > { | > Foo f; | > }; | > | > Foo foo(); | > | > X x; | > x.f = foo(); | | Forget it; it's an assignment. | | > If I change the interface to set_foo( const Foo& r ) { f = r; } instead of a | > public member, it | > is slower, but otherwise not AFAICT (see attachment). | | It's still an assignment. Hm...I ran the code...it's as fast as passing the object directly to the function. Are the compilers doing anything they shouldn't ??? Here's my take on it: a compiler is allowed to a remove any copy operations as long as the programs observed behavior is the same. AFAICT, copying (or assigning) a vector<int> (compared to not doing it) will never change the observed behavior. -Thorsten

"Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
"David Abrahams" <dave@boost-consulting.com> wrote in message news:usm786nvn.fsf@boost-consulting.com... | "Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
| > ah, yes I see it now. That is indeed a major benefit. | >
until we have move-semantics, that is :-)
| > Where in the standard are those restriction described? | | It's described by omission. The standard only gives permission to | elide copies and not assignments. Therefore if you construct a | subobject and then you assign into it, you have to do the assignment. | | > I mean I would have suspected that RVO could be extended to support classes | > like in this: | > | > struct X | > { | > Foo f; | > }; | > | > Foo foo(); | > | > X x; | > x.f = foo(); | | Forget it; it's an assignment. | | > If I change the interface to set_foo( const Foo& r ) { f = r; } instead of a | > public member, it | > is slower, but otherwise not AFAICT (see attachment). | | It's still an assignment.
Hm...I ran the code...it's as fast as passing the object directly to the function.
Fine, show me your test and I'll show you what's wrong with it.
Are the compilers doing anything they shouldn't ???
Here's my take on it: a compiler is allowed to a remove any copy operations as long as the programs observed behavior is the same. AFAICT, copying (or assigning) a vector<int> (compared to not doing it) will never change the observed behavior.
You're kidding, right? I doubt very much that the compiler can legitimately decide that side effects on the memory subsystem due to allocation are not observable. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams wrote:
"Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
Here's my take on it: a compiler is allowed to a remove any copy operations as long as the programs observed behavior is the same. AFAICT, copying (or assigning) a vector<int> (compared to not doing it) will never change the observed behavior.
You're kidding, right? I doubt very much that the compiler can legitimately decide that side effects on the memory subsystem due to allocation are not observable.
This came up on the GCC lists a while back <http://gcc.gnu.org/ml/gcc/2004-10/msg00424.html>. Aaron W. LaFramboise

"Aaron W. LaFramboise" <aaronrabiddog51@aaronwl.com> wrote in message news:419C1451.9010905@aaronwl.com... | David Abrahams wrote: | | > "Thorsten Ottosen" <nesotto@cs.auc.dk> writes: | | >>Here's my take on it: a compiler is allowed to a remove any copy operations as | >>long as the programs observed behavior is the | >>same. AFAICT, copying (or assigning) a vector<int> (compared to not doing it) | >>will never change the observed behavior. | > | > You're kidding, right? I wasn't :-) | > I doubt very much that the compiler can | > legitimately decide that side effects on the memory subsystem due to | > allocation are not observable. | | This came up on the GCC lists a while back | <http://gcc.gnu.org/ml/gcc/2004-10/msg00424.html>. Reading through that thread, I think must be dependent on how we define "observable behavior". Including the memory system as observable is wierd IMO...just the fact that there are no other portable guarantees of memory layout/memory manager suggest that making 2 instead of 1 heap-allocations cannot be what we mean by observable. So the example from the gcc-thread, free(malloc(15)); should easily be allowed to be removed. -Thorsten

"Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
Reading through that thread, I think must be dependent on how we define "observable behavior". Including the memory system as observable is wierd IMO...just the fact that there are no other portable guarantees of memory layout/memory manager suggest that making 2 instead of 1 heap-allocations cannot be what we mean by observable.
So the example from the gcc-thread,
free(malloc(15));
should easily be allowed to be removed.
Maybe so. But as far as the named params library is concerned, practical considerations have to come into play. It doesn't matter that the optimization is legal if no compilers can do it... or even if it's optional and not even most compilers do it. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

On Nov 17, 2004, at 9:55 PM, David Abrahams wrote:
"Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
Here's my take on it: a compiler is allowed to a remove any copy operations as long as the programs observed behavior is the same. AFAICT, copying (or assigning) a vector<int> (compared to not doing it) will never change the observed behavior.
You're kidding, right? I doubt very much that the compiler can legitimately decide that side effects on the memory subsystem due to allocation are not observable.
Since I've been researching in this area for several years... Dave is correct: no compiler out there is going to optimize this, because the side effects are killer. It is possible to analyze that behavior and perform the optimization Thorsten is describing, but (1) There are better ways than looking at it from the memory subsystem/pointer magic angle (2) This is very much an open research area; don't expect this from a research compiler for at least 2 years. (3) This is very much an open research area; don't expect this from a commercial compiler for at least 10 years. Copy elision and the RVO are all we have. We can't rely on anything more than that. Doug

"David Abrahams" <dave@boost-consulting.com> wrote in message news:uis8350ii.fsf@boost-consulting.com... | "Thorsten Ottosen" <nesotto@cs.auc.dk> writes: | > Hm...I ran the code...it's as fast as passing the object directly to the | > function. | | Fine, show me your test and I'll show you what's wrong with it. Sorry, I thought it was attached earlier. Here it is. -Thorsten begin 666 copy.cpp M(VEN8VQU9&4@/'9E8W1O<CX-"B-I;F-L=61E(#QI;W-T<F5A;3X-"@T*#0IS M=&0Z.G9E8W1O<CQI;G0^(&9O;R@I.PT*#0IT96UP;&%T93P@8VQA<W,@5B ^ M#0IL;VYG('!R:6YT*"!C;VYS="!6)B!V("D-"GL-"B @("!L;VYG(&P@/2 P M.PT*(" @( T*(" @(&9O<B@@<VEZ95]T(&D@/2 P.R!I("$]('8N<VEZ92@I M.R K*VD@*0T*(" @(" @("!L("L]('9;:5T[#0H-"B @("!R971U<FX@;#L@ M#0I]#0H-"G1E;7!L871E/"!C;&%S<R!6(#X-"G-T<G5C="!&;V\-"GL-"B @ M("!6('8[#0I].PT*#0HC:6YC;'5D92 \8F]O<W0O<')O9W)E<W,N:'!P/@T* M#0II;G0@;6%I;B@I#0I[#0H@(" @8V]N<W0@:6YT('-Z(#T@,3 P,# [#0H@ M(" @( T*(" @('L-"B @(" @(" @8F]O<W0Z.G!R;V=R97-S7W1I;65R('0[ M#0H-"B @(" @(" @;&]N9R!L(#T@,#L-"B @(" @(" @9F]R*"!I;G0@:2 ] M(# [(&D@(3T@<WH[("LK:2 I#0H@(" @(" @(" @("!L("L]('!R:6YT*"!F M;V\H*2 I.PT*#0H@(" @(" @('-T9#HZ8V]U=" \/"!L(#P\("(@(CL-"B @ M("!]#0H-"B @("![#0H@(" @(" @(&)O;W-T.CIP<F]G<F5S<U]T:6UE<B!T M.PT*#0H@(" @(" @(&QO;F<@;" ](# [#0H-"B @(" @(" @9F]R*"!I;G0@ M:2 ](# [(&D@(3T@<WH[("LK:2 I#0H@(" @(" @('L-"B @(" @(" @(" @ M("\O8V]N<W0@<W1D.CIV96-T;W(\:6YT/B8@=B ](&9O;R@I.PT*(" @(" @ M(" @(" @+R]L("L]('!R:6YT*"!V("D[#0H@(" @(" @(" @("!&;V\\('-T M9#HZ=F5C=&]R/&EN=#X@/B!F.PT*(" @(" @(" @(" @9BYV(#T@9F]O*"D[ M#0H@(" @(" @(" @("!L("L]('!R:6YT*"!F+G8@*3L-"@T*(" @(" @("!] M#0H-"B @(" @(" @<W1D.CIC;W5T(#P\(&P@/#P@(B B.PT*(" @('T-"@T* M?0T*#0IS=&0Z.G9E8W1O<CQI;G0^(&9O;U]I;7!L*"D-"GL-"B @("!C;VYS M="!I;G0@<WH@/2 S,# P.PT*(" @('-T9#HZ=F5C=&]R/&EN=#X@=CL-"B @ M("!F;W(H(&EN="!I(#T@,#L@:2 A/2!S>CL@*RMI("D-"B @(" @(" @=BYP M=7-H7V)A8VLH(&D@*3L-"B @("!R971U<FX@=CL-"GT-"@T*<W1D.CIV96-T M;W(\:6YT/B!F;V\H*0T*>PT*(" @('-T871I8R!S=&0Z.G9E8W1O<CQI;G0^ I('8H(&9O;U]I;7!L*"D@*3L-"B @("!R971U<FX@=CL-"GT-"@T*#0H` ` end

"Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
"David Abrahams" <dave@boost-consulting.com> wrote in message news:uis8350ii.fsf@boost-consulting.com... | "Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
| > Hm...I ran the code...it's as fast as passing the object directly to the | > function. | | Fine, show me your test and I'll show you what's wrong with it.
Sorry, I thought it was attached earlier. Here it is.
-Thorsten
Here we go:
#include <vector> #include <iostream>
std::vector<int> foo();
template< class V > long print( const V& v ) { long l = 0;
for( size_t i = 0; i != v.size(); ++i ) l += v[i];
Spending time looping here just serves to help erase the differences in time caused by the extra copy. I replaced it with: if (!v.empty()) l += v[v.size() / 2];
return l; }
template< class V > struct Foo { V v; };
#include <boost/progress.hpp>
int main() { const int sz = 10000;
On my 1GhZ PIII with most compilers, that number is fine. With Intel C++ 7/8 it seems to be too low to show the difference. Multiply it by 10.
{ boost::progress_timer t;
long l = 0; for( int i = 0; i != sz; ++i ) l += print( foo() );
std::cout << l << " ";
You're also counting the time it takes to do I/O here, which similarly serves to erase the differences in time. I/O is expensive and it can swamp the costs of allocating the new vector.
}
{ boost::progress_timer t;
long l = 0;
for( int i = 0; i != sz; ++i ) { //const std::vector<int>& v = foo(); //l += print( v ); Foo< std::vector<int> > f; f.v = foo(); l += print( f.v );
}
std::cout << l << " ";
Likewise.
}
}
std::vector<int> foo_impl() { const int sz = 3000; std::vector<int> v; for( int i = 0; i != sz; ++i ) v.push_back( i ); return v; }
std::vector<int> foo() { static std::vector<int> v( foo_impl() ); return v; }
The enclosed will show the difference on any compiler I can get my hands on. I'd be happy to show you the output if you need proof. On most compilers it's a factor of 2. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

"David Abrahams" <dave@boost-consulting.com> wrote in message news:ur7mr133a.fsf@boost-consulting.com... | "Thorsten Ottosen" <nesotto@cs.auc.dk> writes: | The enclosed will show the difference on any compiler I can get my | hands on. I'd be happy to show you the output if you need proof. On | most compilers it's a factor of 2. indeed. Thanks for fixing the bugs :-) | for( int i = 0; i != sz; ++i ) | { | Foo< std::vector<int> > f; | f.v = foo(); If you try to write Foo< std::vector<int> > f = { foo() }; l += print( f.v ); there should be no difference again. I'm still a little puzzled about why 1. adding an inline constructor to Foo 2. using assignment syntax is not leading to any optimization when the above syntax does. -Thorsten

"Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
"David Abrahams" <dave@boost-consulting.com> wrote in message news:ur7mr133a.fsf@boost-consulting.com... | "Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
| The enclosed will show the difference on any compiler I can get my | hands on. I'd be happy to show you the output if you need proof. On | most compilers it's a factor of 2.
indeed. Thanks for fixing the bugs :-)
| for( int i = 0; i != sz; ++i ) | { | Foo< std::vector<int> > f; | f.v = foo();
If you try to write
Foo< std::vector<int> > f = { foo() }; l += print( f.v );
there should be no difference again.
Depends on how many optimizations your compiler is willing to do. *Entirely* implementation-dependent!
I'm still a little puzzled about why
Because what you wrote is a vector construction, not an assignment. I told you, assignments are not elidable.
1. adding an inline constructor to Foo 2. using assignment syntax
is not leading to any optimization when the above syntax does.
I think I'll leave you to puzzle it out. This has left the realm of relevance to the thread. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams wrote:
This approach is superior to bgl_named_params in part because it avoids undue coupling and dependency in a library design. That coupling is discussed in depth in this excerpt from C++ Template Metaprogramming (http://www.boost-consulting.com/mplbook): [snipped]
This coupling could be avoided by using an overloaded operator to combine the arguments, for example: f(slew(.799) | name("z")); An advantage of this method is that it avoids the need for the forwarding functions.
However, the amount of work needed to code parameters for this is quite large.
Really? Did you compare how much work it would be to use BOOST_NAMED_PARAMS_FUN vs. what you're suggesting below?
Unless I'm mistaken, BOOST_NAMED_PARAMS_FUN can't be used for member functions. It also doesn't allow exception specifications for free functions, although I'm not sure if that's much of a loss. Daniel

Daniel James wrote:
David Abrahams wrote:
This approach is superior to bgl_named_params in part because it avoids undue coupling and dependency in a library design. That coupling is discussed in depth in this excerpt from C++ Template Metaprogramming (http://www.boost-consulting.com/mplbook):
[snipped]
This coupling could be avoided by using an overloaded operator to combine the arguments, for example:
f(slew(.799) | name("z"));
An advantage of this method is that it avoids the need for the forwarding functions.
I was just going over the library and have a related question. Is there a rational as to why the forwarding functions where used instead of overloading the "," operator? -- -- Grafik - Don't Assume Anything -- Redshift Software, Inc. - http://redshift-software.com -- rrivera/acm.org - grafik/redshift-software.com - 102708583/icq

Rene Rivera <grafik.list@redshift-software.com> writes:
Daniel James wrote:
David Abrahams wrote:
This approach is superior to bgl_named_params in part because it avoids undue coupling and dependency in a library design. That coupling is discussed in depth in this excerpt from C++ Template Metaprogramming (http://www.boost-consulting.com/mplbook): [snipped] This coupling could be avoided by using an overloaded operator to combine the arguments, for example: f(slew(.799) | name("z")); An advantage of this method is that it avoids the need for the forwarding functions.
I was just going over the library and have a related question. Is there a rational as to why the forwarding functions where used instead of overloading the "," operator?
What did you have in mind? f(slew = .799, name = "z") ^ The comma above will never be treated as an operator. It's just the way C++ works -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

Rene Rivera wrote:
Daniel James wrote:
David Abrahams wrote:
This approach is superior to bgl_named_params in part because it avoids undue coupling and dependency in a library design. That coupling is discussed in depth in this excerpt from C++ Template Metaprogramming (http://www.boost-consulting.com/mplbook):
[snipped]
This coupling could be avoided by using an overloaded operator to combine the arguments, for example:
f(slew(.799) | name("z"));
An advantage of this method is that it avoids the need for the forwarding functions.
I was just going over the library and have a related question. Is there a rational as to why the forwarding functions where used instead of overloading the "," operator?
operator, *is* overloaded. It's just that if you use it you have to: (1) Add additional parameters around the parameter list. (2) Only use named parameters, position gets no meaning. -- Daniel Wallin

Peter Dimov wrote:
Daniel James wrote:
This coupling could be avoided by using an overloaded operator to combine the arguments, for example:
f(slew(.799) | name("z"));
I've been using essentially the same technique, except that I chose & as a combiner operator. | isn't bad, too.
But it still looks considerably worse than: f(slew = 0.799, name = "z"); And you lose the positional meaning. -- Daniel Wallin

Daniel Wallin wrote:
Peter Dimov wrote:
Daniel James wrote:
This coupling could be avoided by using an overloaded operator to combine the arguments, for example:
f(slew(.799) | name("z"));
I've been using essentially the same technique, except that I chose & as a combiner operator. | isn't bad, too.
But it still looks considerably worse than:
f(slew = 0.799, name = "z");
And you lose the positional meaning.
Yes, I use it in a slightly different context, to pass arbitrary argument tuples via an A const & a templated argument, not as a replacement to ordinary positional calls. The callee then queries 'a' for 'name', for example, if it supports a 'name' attribute. The set of attributes isn't fixed.

Daniel James <daniel@calamity.org.uk> writes:
David Abrahams wrote:
This approach is superior to bgl_named_params in part because it avoids undue coupling and dependency in a library design. That coupling is discussed in depth in this excerpt from C++ Template Metaprogramming (http://www.boost-consulting.com/mplbook): [snipped]
This coupling could be avoided by using an overloaded operator to combine the arguments, for example:
f(slew(.799) | name("z"));
An advantage of this method is that it avoids the need for the forwarding functions.
You could also write (slew = .799) | (name = "z") But the disadvantage of these schemes is that you lose the positional interface to f. Now you're forced to use named parameters.
However, the amount of work needed to code parameters for this is quite large. Really? Did you compare how much work it would be to use BOOST_NAMED_PARAMS_FUN vs. what you're suggesting below?
Unless I'm mistaken, BOOST_NAMED_PARAMS_FUN can't be used for member functions.
I don't see why not. What am I missing? Anyway, it's surely possible to make a BOOST_NAMED_PARAMS_MEMBER_FUN if neccessary.
It also doesn't allow exception specifications for free functions, although I'm not sure if that's much of a loss.
No, I don't think it is much of a loss. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams wrote:
I don't see why not. What am I missing? Anyway, it's surely possible to make a BOOST_NAMED_PARAMS_MEMBER_FUN if neccessary.
I think it isn't working because the implementation function is declared twice in class body. So BOOST_NAMED_PARAMS_MEMBER_FUN should drop the forward declaration. You'll probably also need a BOOST_NAMED_PARAMS_CONST_MEMBER_FUN. I don't think there's a nice solution for constructors. Daniel

David Abrahams wrote:
Daniel James <daniel@calamity.org.uk> writes:
David Abrahams wrote:
This approach is superior to bgl_named_params in part because it avoids undue coupling and dependency in a library design. That coupling is discussed in depth in this excerpt from C++ Template Metaprogramming (http://www.boost-consulting.com/mplbook):
[snipped]
This coupling could be avoided by using an overloaded operator to combine the arguments, for example:
f(slew(.799) | name("z"));
An advantage of this method is that it avoids the need for the forwarding functions.
You could also write
(slew = .799) | (name = "z")
Or: f((slew = .799, name = "z")); With the current library. -- Daniel Wallin

David Abrahams wrote:
Daniel James <daniel@calamity.org.uk> writes:
An advantage of this method is that it avoids the need for the forwarding functions.
But the disadvantage of these schemes is that you lose the positional interface to f. Now you're forced to use named parameters.
Personally, I'd prefer not requiring the forwarding functions. The positional interface doesn't seem that useful. If it was then you wouldn't need the named parameters. But I realise that this is a fairly subjective opinion. Of course, you could create a positional interface by using a forwarding function. But that defeats the point. Daniel

As always, please remember to clearly state whether you believe the library should be accepted into Boost. -------- I think it should not be accepted. My main motivation is that I think it promotes an unsound growth in function interfaces. I also miss a comparison of other techniques (eg. bgl_named_params, named_constructor_pattern) to do the same and an good explanation why this approach is superior.
Let me elaborate.
1. Clearly, this is not nice
window* w = new_window("alert", true, true, false, 77, 65);
However, the amount of work needed to code parameters for this is quite large. A simple, effective way is just to do it like this:
window* w = new_window("alert"); w->set_resizable( true ); w->set_height( 77 ); .... and you might provide chaining to make it more concise. In addition there is no forwarding problems and you might have these functions anyway.
2. what happens, then, if I don't have an object, but a free-standing function? a) wrap the function in an object b) do as in http://www.boost.org/libs/graph/doc/bgl_named_params.html c) provide overloads of functions that pack related parameters together or simply add defaults
In my tiny toy library, any function can be assigned to a named parameters functor with a simple declration. http://tinytl.sourceforge.net/#func_named_params_function However the syntax for invoking it is not very pretty. cw( cw.arg<style>(3) ); cw( cw.arg<title>("hi") ); instead of cw( title = "hi" ); I was going to combine boost::named_params with my named functors (in some way at least) but never got around it... it is still on my list. I agree that the all these steps from #2 just to make a named wrapper is a bit excessive and probably unnecessary.
I must admit I'm a bit sceptical about functions when they start having more than 3-4 arguments; there is almost always some abstraction lurking just waiting to be done.
If you have named parameters with default values, why not... Eugene

"Doug Gregor" wrote:
The formal review of the Named Parameters library ...
Question: was it tested with Intel C++? I get errors (Intel C++ 7.0 plugged inside VC6 IDE with old Dinkumware). All settings default. I use Boost 1.32 prerelease The same code compiled with VC++ 6.5. The documentation claims Intel C++ 5.0, 6.0, 7.1, 8.0 work. Is 7.0 exception? /Pavel ________________________________ named_params_test.cpp C:\work\reviews\boost_1_32_0\boost/mpl/eval_if.hpp(32): error: invalid base class : if_<C,F1,F2>::type ^ detected during: instantiation of class "boost::mpl::eval_if<C, F1, F2> [with C=boost::detail::is_const_reference_wrapper<const boost::reference_wrapper<int>>, F1=const boost::reference_wrapper<int>, F2=boost::mpl::identity<const boost::reference_wrapper <int>>]" at line 431 of "C:\work\reviews\named_params\boost\boost/named_params.hpp" instantiation of class "boost::detail::unwrap_cv_reference<T> [with T=const boost::reference_wrapper<int>]" at line 159 of "C:\work\reviews\named_params\boost\libs\utility\test\named_params_test.cpp" C:\work\reviews\named_params\boost\boost/named_params.hpp(431): error: class "boost::mpl::eval_if<boost::detail::is_const_reference_wrapper<const boost::reference_wrapper<int>>, const boost::reference_wrapper<int>, boost::mpl::identity<const boost:: reference_wrapper<int>>>" has no member "type" >::type type; ^ detected during instantiation of class "boost::detail::unwrap_cv_reference<T> [with T=const boost::reference_wrapper<int>]" at line 159 of "C:\work\reviews\named_params\boost\libs\utility\test\named_params_test.cpp" C:\work\reviews\named_params\boost\libs\utility\test\named_params_test.cpp(1 17): error: no instance of overloaded function "test::equal" matches the argument list argument types are: (const int, const boost::detail::named<test::index_t, <error-type>>::value_type) assert(equal(i, i_)); ^ detected during: instantiation of "void test::values_t<Name, Value, Index>::operator()(const Name_ &, const Value_ &, const Index_ &) const [with Name=char [4], Value=double, Index=int, Name_=boost::detail::named<test::name_t, char [4]>::value_type, Valu e_=boost::_bi::bind_t<double, double (*)(), boost::_bi::list0>::result_type={boost::_bi::result_traits<double, double (*)()>::type={double}}, Index_=boost::detail::named<test::index_t, <error-type>>::value_type]" at line 53 instantiation of "int test::f_impl(const Params &) [with Params=boost::detail::list<boost::detail::named<test::tester_t, const test::values_t<char [4], double, int>>, boost::detail::list<boost::detail::named<test::index_t, <error-type>>, boost::detail::list<boost::detail::named<test::name_t, char [4]>, boost::detail::nil>>>]" at line 67 instantiation of "int test::f(const Tester &, const Name &, const Value &) [with Tester=test::values_t<char [4], double, int>, Name=boost::detail::named<test::index_t, <error-type>>, Value=boost::detail::named<test::name_t, char [4]>]" compilation aborted for C:\work\reviews\named_params\boost\libs\utility\test\named_params_test.cpp (code 2) Error executing cl.exe. test.exe - 3 error(s), 0 warning(s) _________________ EOF

"Pavel Vozenilek" <pavel_vozenilek@hotmail.com> writes:
"Doug Gregor" wrote:
The formal review of the Named Parameters library ...
Question: was it tested with Intel C++?
Yes.
I get errors (Intel C++ 7.0 plugged inside VC6 IDE with old Dinkumware). All settings default. I use Boost 1.32 prerelease
I'll check it again and see what I come up with. What test code are you compiling? -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

- What is your evaluation of the design?
Based on the docs, it appears to address a real problem, and does it in a way that results in a very natural interface when using functions that use the library. Writing such functions looks to be a bit less straight-forward, but no more so than any (less complete) alternative approach I am aware of.
- What is your evaluation of the implementation?
It is a lot better than anything I could have come up with (faint praise indeed). The bulk of the implementation is machinery to provide a useable interface - which it seems to have succeeded in doing.
- What is your evaluation of the documentation?
Pretty good. The automatic overload generation should be added to the tutorial, and a very basic example using it should be added to the tests and referenced from the tutorial.
- What is your evaluation of the potential usefulness of the library?
I wouldn't use it often, but I see its main utility, assuming it obtains widespread use, being a reduction in the variety of ways of (partially) addressing this problem. Unfortunately, there also exists the possibility that it will simply introduce one more variation into the mix. However, unless there are some significant performance issues (are there?) I wouldn't expect this to be the case.
- Did you try to use the library? With what compiler? Did you have any problems?
Yes, but I haven't updated to the pre-release or cvs boost so ran into mpl changes.
- How much effort did you put into your evaluation? A glance? A quick reading? In-depth study?
More than a glance, but not that much more.
- Are you knowledgeable about the problem domain?
I guess so - its a pretty basic one. What I'm not sufficiently familiar with is the solution domain - I don't see any problems with the lib, but that is limited by my ability to even start to look for them.
As always, please remember to clearly state whether you believe the library should be accepted into Boost.
I vote for acceptance.

Doug Gregor wrote:
As always, please remember to clearly state whether you believe the library should be accepted into Boost.
The library is a good start but I don't think that it should be accepted w/o a native support for named parameters functors. The macros are not good enough. Eugene

On Nov 13, 2004, at 3:12 AM, E. Gladyshev wrote:
Doug Gregor wrote:
As always, please remember to clearly state whether you believe the library should be accepted into Boost.
The library is a good start but I don't think that it should be accepted w/o a native support for named parameters functors. The macros are not good enough.
Eugene
You are referring to a facility such as the one you documented here? http://tinytl.sourceforge.net/#func_named_params_function Could you say why you find this feature so important that the library should be rejected due to the lack of the feature? What concerns does this feature address that the existing interface does not? Doug

Doug Gregor wrote:
On Nov 13, 2004, at 3:12 AM, E. Gladyshev wrote:
Doug Gregor wrote:
As always, please remember to clearly state whether you believe the library should be accepted into Boost.
The library is a good start but I don't think that it should be accepted w/o a native support for named parameters functors. The macros are not good enough.
Eugene
You are referring to a facility such as the one you documented here?
Yes, I just call it named parameters functors.
Could you say why you find this feature so important that the library should be rejected due to the lack of the feature? What concerns does this feature address that the existing interface does not?
Well, let me rephrase, I think that the library should not be accepted without at least *considering* named parameters functors. Some of the reasons are: 1. I think that one of the important applications of the library is creating named parameters wrapper around legacy C interfaces with a bunch of parameters. It seems to me that named parameters functors are an elegant way to do it without macros. I personally belong to a large group of developers who don't favor macros unless there are really necessary. Please I don't mean to get into a discussion about macros in general. 2. Most imporantly, named parameters functors extend the library ideas to function objects (function pointers) in general. For instance you can easily declare a named parameter "pointer" and assign different implementations to it. Perhaps it would be even possible to make named parameters functors work with stuff like signals, etc. Here is an example with class members. I think that it is much more fun than macros. :) struct widget { //define a named parameters functor typedef ttl::func::named_params_function< int //the function returns 'int' ( //'argument name', 'argument type' title, const char* , style, ttl::func::numeric_argument<int, 45> //the default is 45 ) > create_functor; // widget() { //initialize the functor with create_impl create = boost::bind( &widget::create_impl, this, _1, _2 ); } create_functor create; private: int create_impl( const char* title, int style ); }; main() { widget w; w.create( w.create.arg<title>("my widget") ); return 0; } Eugene

E. Gladyshev wrote:
1. I think that one of the important applications of the library is creating named parameters wrapper around legacy C interfaces with a bunch of parameters. It seems to me that named parameters functors are an elegant way to do it without macros. I personally belong to a large group of developers who don't favor macros unless there are really necessary. Please I don't mean to get into a discussion about macros in general.
I don't remember there being a requirement to use macros on this named_params library. And I showed how to do exactly that, wrap C style function, in my example (in my review) without macros.
Here is an example with class members. I think that it is much more fun than macros. :)
struct widget { //define a named parameters functor typedef ttl::func::named_params_function< int //the function returns 'int' ( //'argument name', 'argument type' title, const char* , style, ttl::func::numeric_argument<int, 45> //the default is 45 ) > create_functor;
// widget() { //initialize the functor with create_impl create = boost::bind( &widget::create_impl, this, _1, _2 ); }
create_functor create;
private: int create_impl( const char* title, int style ); };
main() { widget w;
w.create( w.create.arg<title>("my widget") );
return 0;
}
It may be "fun", but the overhead is overwhelming. I find this alternative more appealing... namespace { boost::keyword<struct title_t> title; boost::keyword<struct style_t> style; } struct widget { widget() { } int create( const char * title, int style ); template<class Params> inline int create(const Params & params) { return create(params[title], params[style | int(45)); } }; int main() { widget w; w.create((title = "my widget)); return 0; } Especially considering that the overhead is *zero* with release optimizations. And you can also do the more normal construction by adding: template<class Params> widget(const Params& params) { create(params[title], params[style | int(45)); } I fail to see how the functor gives you anything here. Perhaps you need a better counter example? -- -- Grafik - Don't Assume Anything -- Redshift Software, Inc. - http://redshift-software.com -- rrivera/acm.org - grafik/redshift-software.com - 102708583/icq

Rene Rivera wrote: [...]
I don't remember there being a requirement to use macros on this named_params library. And I showed how to do exactly that, wrap C style function, in my example (in my review) without macros.
From the library docs, my impression was that macros is the recommended way to go. [...]
It may be "fun", but the overhead is overwhelming. I find this alternative more appealing...
namespace { boost::keyword<struct title_t> title; boost::keyword<struct style_t> style; } struct widget { widget() { }
int create( const char * title, int style );
template<class Params> inline int create(const Params & params) { return create(params[title], params[style | int(45)); } }; int main() { widget w; w.create((title = "my widget)); return 0; }
Especially considering that the overhead is *zero* with release optimizations. And you can also do the more normal construction by adding:
template<class Params> widget(const Params& params) { create(params[title], params[style | int(45)); }
I fail to see how the functor gives you anything here. Perhaps you need a better counter example?
Well, namespace { boost::keyword<struct title_t> title; boost::keyword<struct style_t> style; } is not very pretty. I don't know how to make a better counter example. Your example is just completely different from mine. You create a normal wrapper, I create a functor (well sort of). It is a standard stuff (function pointers and functions)... It is like saying give me a good counter example as to why a function pointer is better than a function. :) So I am not sure what do I have to counter. Eugene

"Doug Gregor" wrote:
The formal review of the Named Parameters library by David Abrahams and Daniel Wallin starts today
I vote weak YES to accept this library. - Its documentation is very weak and should be expanded *significantly*. More on it in note [8] bellow. - There should be more tests. - Problem with Intel C++ should be investigated. /Pavel _____________________________________________________ 1. Headers should contain #if (defined _MSC_VER) && (_MSC_VER >= 1200) # pragma hdrstop #endif on the beginning. It has effect on VC and Intel C++ users. _____________________________________________________ 2. named_params.hpp: Shouldn't the default for BOOST_NAMED_PARAMS_MAX_ARITY be more than 5? Why not >= 10. _____________________________________________________ 3. named_params.hpp: Shouldn't the sub-namespace be named "named_params_detail" instead of just "detail"? Names like "nil" inside look quite collision prone. _____________________________________________________ 4. docs: the line window* w = new_window("alert2", movability); // error! should say "logical error" or "coding error" or something like that. _____________________________________________________ 5. docs: the chapter "Introduction" may be better named "Problem" but YMMV. _____________________________________________________ 6. could the code snippets be visually separated from the rest, e.g being in boxes and color syntax highlighted? _____________________________________________________ 7. Question: is there possibility BCB could be convinced to compile (part) if the library or is BCB completely without any chance? _____________________________________________________ 8. What I miss in documentation: - Information about overhead: - numbers/graphs for at least two compilers - estimate how the overhead changes with # of parameters and their type - What happen when I change foo_impl() signature? Will it quiely comoile or what kind of error will I get. Is the error understandable? Example. - Few trivial and non-trivial overcommented complete examples. - Info whether there are any additional constructors or assignements called for object as parameters. - Info about exception safety. - Whether it is possible to have both "named params" function foo() and the original signature function "foo" and whether there could be any problems. Can the original signature foo() be externs "C"? - How do the usual conversion rules apply? Exceptions, examples of these, tricks to make it as safe as possible. - Do overloads of "named params" functions work? Example if they do. - How it is with exception specifications - suported, unsupported, dangerous here? - Does "named params" allocate heap memory? If so, in single block? - Who are expected end users of the library, examples. - I would welocme detailed table what features do not work with this or that compiler (more than the single line of text now). - What is overhead of: namespace { boost::keyword<name_t> name; boost::keyword<value_t> value; } being in header? - Is it possible to reuse tags for different functions? Any problems can happen? - Does it work with Boost.Any as parameter? - Does it work when one used non-default calling conventions as __fastcall? - Is it possible to export "named params" function from DLL? Any problems? - Does it work with bind/boost.function/etc? Examples. - If I use "named params" for the same member of both base and derived class: what defaults would be used? Is it consistent with default handling or 'normal' members? - Would it work to provide "named params" overload of existing C functions? E.g. Win32 API functions? - Is it possible to use usual concept checks with "named" function parameters? Will it work? - The macros is underdocumented and never used in tests. - How the library works with namespaces: can I put the foo_impl into its own inner namespace? Example. - What does need to be in header and what can be put in implementation file? Example(s). - Is there way to make definition of pImpled member functions easier? - Iteration over parameters is undocumented. No examples. - There should be info whether the library headers can be part of precompiled headers (= has any compiler problems?) - How does the library deals with std::auto_ptr parameter? Could there be test for it? - typedef void (* foo_t)(const char*, float); foo_t var = &foo_with_named_params; Is this possible/correct/error prone/caught by compiler? - Is it possible to call function with "named params" from another function with "named params" and the same signature and pass these params without need to extract them? void foo_impl(....) { cout << "foo_impl called"; foo2(???); } - Does the library work with <cstdarg>? - Can or can not this library be used in a ScopeGuard implementation? (I do not have exact idea how it would look like.) _____________________________________________________ 9. Number of test should be much higher. These should range from very simple ones to complex. All possibilities asked in [8] should be covered. _____________________________________________________ 10. named_params.hpp: are the yes_t/no_t needed? Can similar types from TypeTraits be used instead? _____________________________________________________ 11. named_params.hpp: the #endif and #else should have comment to what they relate. The code blocks are quite large and its too easy to lost track. # endif // __EDG_VERSION__ > 300 _____________________________________________________ 12. named_params.hpp: #include <boost/detail/workaround.hpp> should be there listed explicitly. _____________________________________________________ 13. debugging support: could there be simple function/macro which iterates over all named parameters and puts them (in readable, pretty form) into single string? Such string can then be printed, shown in IDE tooltip for examination etc. _____________________________________________________ EOF

"Pavel Vozenilek" <pavel_vozenilek@hotmail.com> writes:
"Doug Gregor" wrote:
The formal review of the Named Parameters library by David Abrahams and Daniel Wallin starts today
I vote weak YES to accept this library.
- Its documentation is very weak and should be expanded *significantly*. More on it in note [8] bellow.
- There should be more tests. - Problem with Intel C++ should be investigated.
/Pavel
_____________________________________________________ 1. Headers should contain
#if (defined _MSC_VER) && (_MSC_VER >= 1200) # pragma hdrstop #endif
on the beginning.
Why?
It has effect on VC and Intel C++ users.
What effect does it have?
_____________________________________________________ 2. named_params.hpp:
Shouldn't the default for BOOST_NAMED_PARAMS_MAX_ARITY be more than 5?
Why not >= 10.
It's arbitrary. We could do either.
_____________________________________________________ 3. named_params.hpp:
Shouldn't the sub-namespace be named "named_params_detail" instead of just "detail"?
Yeah, something like that. I guess a pass over this code to strengthen it for Boost needs to be done.
Names like "nil" inside look quite collision prone.
_____________________________________________________ 4. docs: the line
window* w = new_window("alert2", movability); // error!
should say "logical error" or "coding error" or something like that.
It's a type error. Thanks.
_____________________________________________________ 5. docs: the chapter "Introduction" may be better named "Problem" but YMMV.
Noted.
_____________________________________________________ 6. could the code snippets be visually separated from the rest, e.g being in boxes and color syntax highlighted?
They are already in gray boxes. As for color syntax highlighting, that's not easy for us to do unless we convert the documentation source format... which is a possibility.
_____________________________________________________ 7. Question: is there possibility BCB could be convinced to compile (part) if the library or is BCB completely without any chance?
Anything is possible. I personally won't waste any time on BCB.
_____________________________________________________ 8. What I miss in documentation:
- Information about overhead: - numbers/graphs for at least two compilers - estimate how the overhead changes with # of parameters and their type
Are you curious about compile time or runtime?
- What happen when I change foo_impl() signature? Will it quiely comoile or what kind of error will I get. Is the error understandable? Example.
I am not convinced this belongs in the documentation, unless we are going to change our documentation standards drastically. Few other libraries discuss the quality of error message you get when they are misused.
- Few trivial and non-trivial overcommented complete examples.
Agreed.
- Info whether there are any additional constructors or assignements called for object as parameters.
?? It seems to me that section 3 quite clearly describes how the objects are passed. Can you help me understand why the answer to yoiur question wasn't clear from the text?
- Info about exception safety.
What, specifically, are you curious about? There's nothing much to say; all the library does is introduce a forwarding layer.
- Whether it is possible to have both "named params" function foo() and the original signature function "foo" and whether there could be any problems. Can the original signature foo() be externs "C"?
I'm confused by the question. Except for the need to write ref(), the "named params" function foo supports the original calling signature. I concede that if you have questions the docs need to be beefed up, regardless of the questions' validity.
- How do the usual conversion rules apply? Exceptions, examples of these, tricks to make it as safe as possible.
It should be clear from the fact that the forwarding functions are templated that no conversions apply at that point. However, the usual conversions do apply when the forwarding functions pass their results on to the actual implementation function.
- Do overloads of "named params" functions work? Example if they do.
Yes, that's the whole point of section 4, "Controlling Overload Resolution." Can you help me understand why that wasn't clear from the text?
- How it is with exception specifications - suported, unsupported, dangerous here?
Just as dangerous as ever. Supported? I have no idea what you mean. Libraries don't support exception specifications; that's done by the language.
- Does "named params" allocate heap memory?
No.
If so, in single block?
- Who are expected end users of the library, examples.
This is a general-purpose library. I don't think it's fair to demand that docs answer this question any more than you would ask who the users of an iterator library are. The answers are tautological: anyone who wants to provide a named parameter interface would use it.
- I would welocme detailed table what features do not work with this or that compiler (more than the single line of text now).
On the compilers we tested, all the detail is given by the simple list in the doc. Would it really help to draw boxes around those lines?
- What is overhead of: namespace { boost::keyword<name_t> name; boost::keyword<value_t> value; } being in header?
Compile-time overhead? These generate trivial objects with no data members, constructor, or destructor. It ought to be essentially free.
- Is it possible to reuse tags for different functions?
Yes, that's part of the point of the library.
Any problems can happen?
I suppose anything can have problems, but the library is designed to be used this way.
- Does it work with Boost.Any as parameter?
I don't see why not. Specific example, please? Again, this seems like an unfair question to demand that the docs answer. We could ask the same question about every other boost component. Does it work with Boost.Tuple parameters? Yes. etc., etc.
- Does it work when one used non-default calling conventions as __fastcall?
Yes, and that should be obvious from the documentation. There are no function pointers flying around; it just uses simple forwarding. Another unfair question to demand that the docs answer, IMO.
- Is it possible to export "named params" function from DLL? Any problems?
You wouldn't do that. You'd export the implementation function and leave the forwarding function templates in the header. But again, this should be obvious from the documentation if you know how to write a dynamic library that presents a templated interface.
- Does it work with bind/boost.function/etc?
What do you have in mind? Of course it "works with" any component in Boost for some definition of "with."
- If I use "named params" for the same member of both base and derived class: what defaults would be used? Is it consistent with default handling or 'normal' members?
Because the forwarding interface mechanism is completely exposed, the answers should be obvious to anyone who knows C++, it seems to me. These are not magical functions; they're just ordinary templates.
- Would it work to provide "named params" overload of existing C functions? E.g. Win32 API functions?
Again, that overloading works is the point of section 4. Yes.
- Is it possible to use usual concept checks with "named" function parameters? Will it work?
Because the forwarding interface mechanism is completely exposed, the answers should be obvious to anyone who knows C++, it seems to me.
- The macros is underdocumented and never used in tests.
Good point.
- How the library works with namespaces: can I put the foo_impl into its own inner namespace? Example.
Come on, of course you can. This is just standard C++.
- What does need to be in header and what can be put in implementation file? Example(s).
Everything goes in the header. You could use extern declarations for the keyword objects and stick them in an implementation file instead of putting them in an unnammed namespace, but it will be more inconvenient. Again, this is just standard C++; why is there any mystery here?
- Is there way to make definition of pImpled member functions easier?
Specifics, please?
- Iteration over parameters is undocumented.
Why should we document iteration over parameters? Maybe I don't understand what you have in mind.
No examples.
- There should be info whether the library headers can be part of precompiled headers (= has any compiler problems?)
It's standard C++, and subject to the same problems that any other library might have with certain precompiled header implementations, if any such problems exist. I don't use PCHs, so lack the expertise to find the specific information, but would be happy to include it in the docs if someone else supplies it.
- How does the library deals with std::auto_ptr parameter? Could there be test for it?
It's a good question, but I think this falls again under the common rules for C++ and auto_ptrs. You pass ref(x) or you use the named interface (kw = x), because auto_ptr can't be passed by const reference.
- typedef void (* foo_t)(const char*, float); foo_t var = &foo_with_named_params; Is this possible/correct/error prone/caught by compiler?
I don't see a problem. If it compiles, it's correct. It doesn't make sense to me to mention all the things that just work normally in the docs.
- Is it possible to call function with "named params" from another function with "named params" and the same signature and pass these params without need to extract them?
void foo_impl(....) { cout << "foo_impl called"; foo2(???); }
Good question. The answer is no; you have to call foo2_impl if you want to avoid extraction.
- Does the library work with <cstdarg>?
I'm not sure what, specifically, you have in mind, but because the forwarding interface mechanism is completely exposed, the answer should be obvious to anyone who knows C++, it seems to me.
- Can or can not this library be used in a ScopeGuard implementation? (I do not have exact idea how it would look like.)
No offense intended, and I really appreciate your interest in the library, but this seems typical of your questions. The docs shouldn't be expected to answer every vague question that pops into your head.
_____________________________________________________ 9. Number of test should be much higher. These should range from very simple ones to complex.
I agree.
All possibilities asked in [8] should be covered.
I disagree.
_____________________________________________________ 10. named_params.hpp: are the yes_t/no_t needed? Can similar types from TypeTraits be used instead?
Probably. I don't think it would be an advantage, though.
_____________________________________________________ 11. named_params.hpp: the #endif and #else should have comment to what they relate. The code blocks are quite large and its too easy to lost track.
I have mixed feelings about that. Those kind of comments get out-of-date really quickly and my editor, at least, can automatically find the matching #if. Maybe comments should be saved for information that can't be deduced mechanically. Also, it's hard to know whether the comment at the beginning and end of an else clause should match the #if condition or should negate it.
# endif // __EDG_VERSION__ > 300
_____________________________________________________ 12. named_params.hpp: #include <boost/detail/workaround.hpp> should be there listed explicitly.
Good catch.
_____________________________________________________ 13. debugging support: could there be simple function/macro which iterates over all named parameters and puts them (in readable, pretty form) into single string? Such string can then be printed, shown in IDE tooltip for examination etc.
Details, please? -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams wrote:
"Pavel Vozenilek" <pavel_vozenilek@hotmail.com> writes:
4. docs: the line
window* w = new_window("alert2", movability); // error!
Just a quick note, if I may. No "new_X" functions that returns a X* should be allowed into Boost, IMO. Including documentation and tutorials.

"Peter Dimov" <pdimov@mmltd.net> writes:
David Abrahams wrote:
"Pavel Vozenilek" <pavel_vozenilek@hotmail.com> writes:
4. docs: the line
window* w = new_window("alert2", movability); // error!
Just a quick note, if I may. No "new_X" functions that returns a X* should be allowed into Boost, IMO. Including documentation and tutorials.
Okay, very good point. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

On 11/13/04 9:38 AM, "Peter Dimov" <pdimov@mmltd.net> wrote:
David Abrahams wrote:
"Pavel Vozenilek" <pavel_vozenilek@hotmail.com> writes:
4. docs: the line
window* w = new_window("alert2", movability); // error!
Just a quick note, if I may. No "new_X" functions that returns a X* should be allowed into Boost, IMO. Including documentation and tutorials.
I guess you object since, from looks alone, you can neither determine the allocation strategy nor provide a custom allocation procedure. And the earlier fact implies that you can _not_ determine either the de-allocation procedure or actor responsible for de-allocation. If so, then I agree with you on all points. I'm just pointing out the obvious (to you) since it was all implied, so the newbies among us may not realize your (seemingly) mysterious assertions. A hint of the proper solution would be having "new_window" return a "std::auto_ptr<window>", which would be memory- and exception-safe. Of course, you couldn't use that class if allocation doesn't always use the unadorned single-new operator. In that case, something like boost::shared_ptr would be better. -- Daryle Walker Mac, Internet, and Video Game Junkie darylew AT hotmail DOT com

"David Abrahams" wrote: _____________________________________________________
1. Headers should contain
#if (defined _MSC_VER) && (_MSC_VER >= 1200) # pragma hdrstop #endif
It has effect on VC and Intel C++ users.
What effect does it have?
(slight) decrease of compilation time. (Like http://aspn.activestate.com/ASPN/Mail/Message/boost/1798707) _____________________________________________________
6. could the code snippets be visually separated from the rest, e.g being in boxes and color syntax highlighted?
They are already in gray boxes. As for color syntax highlighting, that's not easy for us to do unless we convert the documentation source format... which is a possibility.
(There's easy to use Win32 tool written by Joaqin M Lopez Munoz that does syntax highlighting of small snippets, e.g. as in multi_index container docs.) _____________________________________________________
8. What I miss in documentation:
- Information about overhead: - numbers/graphs for at least two compilers - estimate how the overhead changes with # of parameters and their type
Are you curious about compile time or runtime?
Runtime (plus whether it has noticeable impact on compilation time for this or that compiler).
- What happen when I change foo_impl() signature? Will it quietly compile
I am not convinced this belongs in the documentation, unless we are going to change our documentation standards drastically. Few other libraries discuss the quality of error message you get when they are misused.
Info whether change in foo_impl signature causes compiler error (as if no named_params are used) could be there.
- Info whether there are any additional constructors or assignements called for object as parameters.
??
I mean info whether there's overhead when passing objects through this library.
- Info about exception safety.
What, specifically, are you curious about? There's nothing much to say; all the library does is introduce a forwarding layer.
That.
- Whether it is possible to have both "named params" function foo() and the original signature function "foo" and whether there could be any problems. Can the original signature foo() be externs "C"?
I'm confused by the question. Except for the need to write ref(), the "named params" function foo supports the original calling signature. I concede that if you have questions the docs need to be beefed up, regardless of the questions' validity.
I mean I have function: extern "C" BOOL WINAPI CreateProcess(....many many parameters...); Can I have it and named_params enhanced version in the same TU?
- Do overloads of "named params" functions work? Example if they do.
Yes, that's the whole point of section 4, "Controlling Overload Resolution." Can you help me understand why that wasn't clear from the text?
Bad understanding on my side.
- I would welocme detailed table what features do not work with this or that compiler (more than the single line of text now).
On the compilers we tested, all the detail is given by the simple list in the doc. Would it really help to draw boxes around those lines?
Yes, boxes help quite a lot.
- Would it work to provide "named params" overload of existing C functions? E.g. Win32 API functions?
Again, that overloading works is the point of section 4. Yes.
Was redundant question of me. I think there should be example for a Win32 function. Its very useful feature.
- What does need to be in header and what can be put in implementation file? Example(s).
Everything goes in the header. You could use extern declarations for the keyword objects and stick them in an implementation file instead of putting them in an unnammed namespace, but it will be more inconvenient. Again, this is just standard C++; why is there any mystery here?
While inconvenient it could be done (because of compilation times). My question was more asking for example so reader will register and remember this possibility.
- Is there way to make definition of pImpled member functions easier?
Specifics, please?
Was answered bellow.
- Iteration over parameters is undocumented.
Why should we document iteration over parameters? Maybe I don't understand what you have in mind.
It may be useful for debugging purposes (but it may be hidden by helper function or macro).
- There should be info whether the library headers can be part of precompiled headers (= has any compiler problems?)
It's standard C++, and subject to the same problems that any other library might have with certain precompiled header implementations, if any such problems exist. I don't use PCHs, so lack the expertise to find the specific information, but would be happy to include it in the docs if someone else supplies it.
It was question whether any supported compiler complains now. BCB is known for having insiduous bugs here and solving them makes hair gray. Any info on potential problem helps.
No offense intended, and I really appreciate your interest in the library, but this seems typical of your questions. The docs shouldn't be expected to answer every vague question that pops into your head.
I guess the library would be mostly used to make already written interfaces easier to use. Ending project phases and maintenance are under time pressure and if the docs is detailed it makes changes less stressful.
All possibilities asked in [8] should be covered.
I disagree.
These test may uncover problems with (old) compilers. _____________________________________________________
13. debugging support: could there be simple function/macro which iterates over all named parameters and puts them (in readable, pretty form) into single string? Such string can then be printed, shown in IDE tooltip for examination etc.
Details, please?
My thought was having string s = extract_all_parameters_info_readable_form(...); and then being able to see the values in "s" easily. But it would require formatting or serialization support to be complete. Too much work not worth of the effort, likely. /Pavel

"Pavel Vozenilek" <pavel_vozenilek@hotmail.com> writes:
"David Abrahams" wrote:
_____________________________________________________
1. Headers should contain
#if (defined _MSC_VER) && (_MSC_VER >= 1200) # pragma hdrstop #endif
It has effect on VC and Intel C++ users.
What effect does it have?
(slight) decrease of compilation time.
Are you serious? I can just drop that indiscriminately in every header I write and my compilation times will go down? Why doesn't the compiler just do that for me?
(Like http://aspn.activestate.com/ASPN/Mail/Message/boost/1798707)
?? That message talks about a different pragma.
_____________________________________________________
6. could the code snippets be visually separated from the rest, e.g being in boxes and color syntax highlighted?
They are already in gray boxes. As for color syntax highlighting, that's not easy for us to do unless we convert the documentation source format... which is a possibility.
(There's easy to use Win32 tool written by Joaqin M Lopez Munoz that does syntax highlighting of small snippets, e.g. as in multi_index container docs.)
The documentation is generated, not hand-written, so it would be nontirivial to inject that tool into the build chain.
_____________________________________________________
8. What I miss in documentation:
- Information about overhead: - numbers/graphs for at least two compilers - estimate how the overhead changes with # of parameters and their type
Are you curious about compile time or runtime?
Runtime (plus whether it has noticeable impact on compilation time for this or that compiler).
Hm. The params object just stores references to the outer parameters. There shouldn't be much overhead beyond, possibly, the cost of copying pointers to the actual arguments into the params object.
- What happen when I change foo_impl() signature? Will it quietly compile
I am not convinced this belongs in the documentation, unless we are going to change our documentation standards drastically. Few other libraries discuss the quality of error message you get when they are misused.
Info whether change in foo_impl signature causes compiler error (as if no named_params are used) could be there.
I still don't understand that question. There's no mystery here: foo_impl is defined thus: template<class Params> void foo_impl(const Params& params) { std::cout << params[name | "unnamed"] << " = " << params[value | 0] << "\n"; } and it is invoked directly by the forwarding functions. Everything except the exact type of the Params object that gets passed is completely exposed, so if you know C++ you ought to be able to draw your own conclusions.
- Info whether there are any additional constructors or assignements called for object as parameters.
??
I mean info whether there's overhead when passing objects through this library.
- Info about exception safety.
What, specifically, are you curious about? There's nothing much to say; all the library does is introduce a forwarding layer.
That.
Okay.
I mean I have function: extern "C" BOOL WINAPI CreateProcess(....many many parameters...);
Can I have it and named_params enhanced version in the same TU?
Yes, of course. They're just ordinary function templates, as you can plainly see from the documentation.
- I would welocme detailed table what features do not work with this or that compiler (more than the single line of text now).
On the compilers we tested, all the detail is given by the simple list in the doc. Would it really help to draw boxes around those lines?
Yes, boxes help quite a lot.
How? It's really hard for me to believe that.
- Would it work to provide "named params" overload of existing C functions? E.g. Win32 API functions?
Again, that overloading works is the point of section 4. Yes.
Was redundant question of me. I think there should be example for a Win32 function. Its very useful feature.
It's not a feature of this library. What you're asking is the same as asking whether I can write any other function template and overload it with a Win32 function. There's no magic here.
- What does need to be in header and what can be put in implementation file? Example(s).
Everything goes in the header. You could use extern declarations for the keyword objects and stick them in an implementation file instead of putting them in an unnammed namespace, but it will be more inconvenient. Again, this is just standard C++; why is there any mystery here?
While inconvenient it could be done (because of compilation times).
My question was more asking for example so reader will register and remember this possibility.
Documentation that gives too much information is hard to use, especially if it is not particularly relevant to the library. I think the documentation could be improved, but if we added everything you're asking for I'm sure it would be even less clear than it currently is.
- Iteration over parameters is undocumented.
Why should we document iteration over parameters? Maybe I don't understand what you have in mind.
It may be useful for debugging purposes (but it may be hidden by helper function or macro).
I don't understand what you have in mind. What kind of iteration?
- There should be info whether the library headers can be part of precompiled headers (= has any compiler problems?)
It's standard C++, and subject to the same problems that any other library might have with certain precompiled header implementations, if any such problems exist. I don't use PCHs, so lack the expertise to find the specific information, but would be happy to include it in the docs if someone else supplies it.
It was question whether any supported compiler complains now. BCB is known for having insiduous bugs here and solving them makes hair gray. Any info on potential problem helps.
It makes no sense to me to add documentation about compiler problems that are just as likely to interact with some other library as with this one. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

"David Abrahams" wrote:
(Like http://aspn.activestate.com/ASPN/Mail/Message/boost/1798707)
?? That message talks about a different pragma.
Oh. I thought #pragma once and wrote #pragma hdrstop. Too much immersion in BCB. [syntax highligthing of code snippets]
The documentation is generated, not hand-written, so it would be nontirivial to inject that tool into the build chain.
Ah. OK. [rumtime overhead]
Hm. The params object just stores references to the outer parameters. There shouldn't be much overhead beyond, possibly, the cost of copying pointers to the actual arguments into the params object.
Could this be explicitly stated in docs? [docs look and feel]
Would it really help to draw boxes around those lines?
Yes, boxes help quite a lot.
How? It's really hard for me to believe that.
(OT: I read it in guideline for technical writes (book). Engineering manuals are full of tables. But this is style opinion not really for this maillist.) /Pavel

"Pavel Vozenilek" <pavel_vozenilek@hotmail.com> writes:
[rumtime overhead]
Hm. The params object just stores references to the outer parameters. There shouldn't be much overhead beyond, possibly, the cost of copying pointers to the actual arguments into the params object.
Could this be explicitly stated in docs?
Yes.
[docs look and feel]
Would it really help to draw boxes around those lines?
Yes, boxes help quite a lot.
How? It's really hard for me to believe that.
(OT: I read it in guideline for technical writes (book). Engineering manuals are full of tables. But this is style opinion not really for this maillist.)
Making a table is easy enough. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

Thank you for your comments. Pavel Vozenilek wrote:
"Doug Gregor" wrote:
The formal review of the Named Parameters library by David Abrahams and Daniel Wallin starts today
I vote weak YES to accept this library.
- Its documentation is very weak and should be expanded *significantly*. More on it in note [8] bellow.
We are working on reference docs, but didn't complete it in time for the review.
- There should be more tests. - Problem with Intel C++ should be investigated.
/Pavel
_____________________________________________________ 1. Headers should contain
#if (defined _MSC_VER) && (_MSC_VER >= 1200) # pragma hdrstop #endif
on the beginning. It has effect on VC and Intel C++ users.
Why? What does it do?
_____________________________________________________ 2. named_params.hpp:
Shouldn't the default for BOOST_NAMED_PARAMS_MAX_ARITY be more than 5?
Why not >= 10.
Sure, why not.
_____________________________________________________ 3. named_params.hpp:
Shouldn't the sub-namespace be named "named_params_detail" instead of just "detail"?
Yes.
_____________________________________________________ 4. docs: the line
window* w = new_window("alert2", movability); // error!
should say "logical error" or "coding error" or something like that.
OK.
_____________________________________________________ 6. could the code snippets be visually separated from the rest, e.g being in boxes and color syntax highlighted?
Don't the gray background and indentation suffice?
_____________________________________________________ 7. Question: is there possibility BCB could be convinced to compile (part) if the library or is BCB completely without any chance?
I am not very familiar with BCB, so I don't know.
_____________________________________________________ 8. What I miss in documentation:
- Information about overhead: - numbers/graphs for at least two compilers - estimate how the overhead changes with # of parameters and their type
It shouldn't. There is no inherent overhead in the library. No copies are made, and no heap storage is allocated.
- Info about exception safety.
Everything is nothrow, except when using lazy-defaults that can throw from operator(). But either way, the library doesn't throw.
- Whether it is possible to have both "named params" function foo() and the original signature function "foo" and whether there could be any problems.
Yes, it should be possible.
- How do the usual conversion rules apply? Exceptions, examples of these, tricks to make it as safe as possible.
- Do overloads of "named params" functions work? Example if they do.
Yes, by disabling overloads using SFINAE.
- How it is with exception specifications - suported, unsupported, dangerous here?
- Does "named params" allocate heap memory?
No.
- I would welocme detailed table what features do not work with this or that compiler (more than the single line of text now).
OK.
- What is overhead of: namespace { boost::keyword<name_t> name; boost::keyword<value_t> value; } being in header?
None..?
- Is it possible to reuse tags for different functions? Any problems can happen?
No, that's the intended use.
- Does it work with Boost.Any as parameter?
Yes.
- Does it work when one used non-default calling conventions as __fastcall?
I guess so.
- Is it possible to export "named params" function from DLL? Any problems?
The forwarding functions need to be templated. The actual implementation can be any function, so yes, it can be exported.
- Does it work with bind/boost.function/etc? Examples.
The forwarding functions are templated, so no. They can't be lazily evaluated unless done manually in a functor.
- Would it work to provide "named params" overload of existing C functions? E.g. Win32 API functions?
Yes.
- Is it possible to use usual concept checks with "named" function parameters? Will it work?
It is possible to disable the overload using SFINAE, as described in the documentation under "Controlling Overload Resolution".
- The macros is underdocumented and never used in tests.
- How the library works with namespaces: can I put the foo_impl into its own inner namespace? Example.
- What does need to be in header and what can be put in implementation file? Example(s).
The forwarding functions goes in the header.
- Iteration over parameters is undocumented. No examples.
It isn't possible to iterate over the parameters. It probably could be. Perhaps some fusion integration would be appropriate here.
- How does the library deals with std::auto_ptr parameter?
If the parameter is passed as a keyword argument, it should just work. If it's passed by position, you'd need to boost::ref() it.
Could there be test for it?
Yes.
- typedef void (* foo_t)(const char*, float); foo_t var = &foo_with_named_params; Is this possible/correct/error prone/caught by compiler?
- Is it possible to call function with "named params" from another function with "named params" and the same signature and pass these params without need to extract them?
void foo_impl(....) { cout << "foo_impl called"; foo2(???); }
I don't think it is, but it probably could be without much trouble.
- Does the library work with <cstdarg>?
I'm not sure what that means.
_____________________________________________________ 9. Number of test should be much higher. These should range from very simple ones to complex. All possibilities asked in [8] should be covered.
Agreed.
_____________________________________________________ 11. named_params.hpp: the #endif and #else should have comment to what they relate. The code blocks are quite large and its too easy to lost track.
# endif // __EDG_VERSION__ > 300
OK.
_____________________________________________________ 12. named_params.hpp: #include <boost/detail/workaround.hpp> should be there listed explicitly.
OK.
_____________________________________________________ 13. debugging support: could there be simple function/macro which iterates over all named parameters and puts them (in readable, pretty form) into single string? Such string can then be printed, shown in IDE tooltip for examination etc.
Possibly, but I'm not sure how it would work. -- Daniel Wallin

"Daniel Wallin" wrote: [answers elsewhere this thread omitted] _____________________________________________________
6. could the code snippets be visually separated from the rest, e.g being in boxes and color syntax highlighted?
Don't the gray background and indentation suffice?
I didn't notice the gray background (I have low light intensity settings on my monitor). My personal preference is syntax highlighting but that's opinion.
- Whether it is possible to have both "named params" function foo() and the original signature function "foo" and whether there could be any problems.
Yes, it should be possible.
This is (IMO) worth note in docs. It could be reason to use lib for maintenance. /Pavel

named_params_test.cpp isn't working for me on g++ 3.3.3/3.4.1 on linux. I get the following error: /home/daniel/projects/boost/boost/mpl/eval_if.hpp: In instantiation of `boost::mpl::eval_if<boost::detail::is_const_reference_wrapper<const boost::reference_wrapper<int> >, const boost::reference_wrapper<int>, boost::mpl::identity<const boost::reference_wrapper<int> > >': /home/daniel/src/boost/boost/named_params.hpp:431: instantiated from `boost::detail::unwrap_cv_reference<const boost::reference_wrapper<int> >' named_params_test.cpp:159: instantiated from here /home/daniel/projects/boost/boost/mpl/eval_if.hpp:33: error: base `boost::reference_wrapper<int>' with only non-default constructor in class without a constructor The problem seems to be that eval_if inherits from boost::reference_wrapper<int> which doesn't have a default constructor. Is this a bug in g++? Since eval_if is never constructed I guess it might be? The following patch seems to work. Daniel diff -ru boost/boost/named_params.hpp boost-new/boost/named_params.hpp --- boost/boost/named_params.hpp 2004-11-08 01:20:28.000000000 +0000 +++ boost-new/boost/named_params.hpp 2004-11-13 22:47:07.000000000 +0000 @@ -424,11 +424,11 @@ template <class T> struct unwrap_cv_reference { - typedef typename mpl::eval_if< + typedef typename mpl::if_< is_const_reference_wrapper<T> , T , mpl::identity<T> - >::type type; + >::type::type type; }; } // namespace detail

Daniel James <daniel@calamity.org.uk> writes:
named_params_test.cpp isn't working for me on g++ 3.3.3/3.4.1 on linux. I get the following error:
/home/daniel/projects/boost/boost/mpl/eval_if.hpp: In instantiation of boost::mpl::eval_if<boost::detail::is_const_reference_wrapper<const boost::reference_wrapper<int> >, const boost::reference_wrapper<int>, boost::mpl::identity<const boost::reference_wrapper<int> > >': /home/daniel/src/boost/boost/named_params.hpp:431: instantiated from boost::detail::unwrap_cv_reference<const boost::reference_wrapper<int>
' named_params_test.cpp:159: instantiated from here /home/daniel/projects/boost/boost/mpl/eval_if.hpp:33: error: base boost::reference_wrapper<int>' with only non-default constructor in class without a constructor
The problem seems to be that eval_if inherits from boost::reference_wrapper<int> which doesn't have a default constructor. Is this a bug in g++? Since eval_if is never constructed I guess it might be?
Are you sure that you don't have G++ set to treat warnings as errors? This has shown up as a warning in other cases, but we decided not to apply a workaround to eval_if because that would just propagate the need for a workaround further out. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams wrote:
Are you sure that you don't have G++ set to treat warnings as errors? This has shown up as a warning in other cases, but we decided not to apply a workaround to eval_if because that would just propagate the need for a workaround further out.
I don't think so. I've tried again using: g++ -Wno-error -I ../../.. -I /home/daniel/projects/boost named_params_test.cpp and it still fails. Daniel

Daniel James wrote:
David Abrahams wrote:
Are you sure that you don't have G++ set to treat warnings as errors? This has shown up as a warning in other cases, but we decided not to apply a workaround to eval_if because that would just propagate the need for a workaround further out.
I don't think so. I've tried again using:
g++ -Wno-error -I ../../.. -I /home/daniel/projects/boost named_params_test.cpp
and it still fails.
I checked in a fix for this to the sandbox CVS. Index: named_params.hpp =================================================================== RCS file: /cvsroot/boost-sandbox/boost-sandbox/boost/named_params.hpp,v retrieving revision 1.28 retrieving revision 1.29 diff -u -r1.28 -r1.29 --- named_params.hpp 5 Nov 2004 14:18:21 -0000 1.28 +++ named_params.hpp 14 Nov 2004 08:02:43 -0000 1.29 @@ -418,6 +418,12 @@ typedef mpl::bool_<value> type; }; + template <class T> + struct get_type + { + typedef typename T::type type; + }; + // Produces the unwrapped type to hold a reference to in named<> // Can't use boost::unwrap_reference<> here because it // doesn't handle the case where T = const reference_wrapper<U> @@ -426,7 +432,7 @@ { typedef typename mpl::eval_if< is_const_reference_wrapper<T> - , T + , get_type<T> , mpl::identity<T> >::type type; }; Thanks, -- Daniel Wallin

Daniel James <daniel@calamity.org.uk> writes:
David Abrahams wrote:
Are you sure that you don't have G++ set to treat warnings as errors? This has shown up as a warning in other cases, but we decided not to apply a workaround to eval_if because that would just propagate the need for a workaround further out.
I don't think so. I've tried again using:
g++ -Wno-error -I ../../.. -I /home/daniel/projects/boost named_params_test.cpp
and it still fails.
In that case we'd better consider whether a last-minute patch to MPL is appropriate. Aleksey, in brief: I know we decided not to work around this stupid warning in GCC, but in this case it appears to be coming up as an error (GCC bug) and AFAIK there's no clean workaround for the user other than not using mpl::eval_if. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams writes:
In that case we'd better consider whether a last-minute patch to MPL is appropriate. Aleksey, in brief: I know we decided not to work around this stupid warning in GCC, but in this case it appears to be coming up as an error (GCC bug) and AFAIK there's no clean workaround for the user other than not using mpl::eval_if.
I've put something in (after testing it, of course) before making the tarballs. -- Aleksey Gurtovoy MetaCommunications Engineering

Doug Gregor wrote:
- What is your evaluation of the design?
Very good. I feel that it would better to use a slightly awkward calling syntax to avoid the forwarding functions, but that's not that big a deal. I'm also worried about assignments getting confused with named parameters. Consider the following: bool movable; create_window(movable = true); Here the local variable movable is assigned to and the value true is passed to create_window's first positional argument. This could lead to subtle bugs. I'm not sure that the positional interface is worth it. I would also like to see macros to generate member functions, that support const and static member functions. It would be nice to be able to easily tell if an argument is present or not. Something along the lines of: if(p.find(movable)) { bool x = p[movable]; ... }
- What is your evaluation of the implementation?
Very good.
- What is your evaluation of the documentation?
The tutorial is good. As others have pointed out, it could do with a full reference and more examples. A useful example would be showing how by forwarding to a non-template function, so that the implementation doesn't have to be in the header class or can be a virtual member function. Something along the lines of: window* new_window_concrete(char const* name, bool border, bool opaque, bool movable); BOOST_NAMED_PARAMS_FUN(window*, new_window, 0, 4, new_window_keywords) { new_window_concrete(p[name | 0], p[border | false], p[opaque | true], p[movable | true]); } This may seem obvious, but it took me a little while to realise it.
- What is your evaluation of the potential usefulness of the library?
I think it could be useful when appropriate. I have some sympathy with Thorsten's views about misuse, but I think there are cases when named parameters are appropriate.
- Did you try to use the library? With what compiler? Did you have any problems?
Yes, with gcc and intel on linux. I've reported a problem, and a fix has been checked in (although, I haven't tried that).
- How much effort did you put into your evaluation? A glance? A quick reading? In-depth study?
I've read the documentation, had a look through the code and tried writing a few examples.
- Are you knowledgeable about the problem domain?
I can program in C++ and have some experience with languages that support named parameters. I vote that the library should be accepted into boost. I have some small worries but I think the quality of the library is high enough and boost is the perfect place to find out how well named parameters can work for C++. Daniel

Sorry, another feature request. Would it be possible/a good idea to add a way to get the type of a named parameter? It's possible to call a template function which will then know the type, but it seems like using this library already requires enough forwarding functions. Daniel

Daniel James <daniel@calamity.org.uk> writes:
Sorry, another feature request. Would it be possible/a good idea to add a way to get the type of a named parameter?
Certainly it's possible. Not sure if it's a good idea. Is there a use case? Some people are complaining that the library is already heavy artillery where none is needed.
It's possible to call a template function which will then know the type, but it seems like using this library already requires enough forwarding functions.
-- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams <dave@boost-consulting.com> wrote: Daniel James <daniel@calamity.org.uk> writes:
Sorry, another feature request. Would it be possible/a good idea to add a way to get the type of a named parameter?
Certainly it's possible. Not sure if it's a good idea. Is there a use case? Some people are complaining that the library is already heavy artillery where none is needed.
A use case I have that I'm not sure how best to approach, but would find incredibly useful, with named_params is using a compile time policy to support switching between: 1. direct function call 2. an indirection to the function call, such as the marshalling and transmission of the function parameters, including formal parameter names, to a target. To do this, named_params needs to support an interface for iterating over the parameters. Getting type info and formal parameter names may help some styles of indirection. For example, matching the type to a specific transport's marshalling routine for the same type. The goal is being able to re-architect applications by simply changing policies at key workflow points in an application. Change from a function call, to a threaded work pool, to a call to a separate process, to a call to a separate machine via a udp/tcp or even tib rv. I see named_params as a key piece of the puzzle in enabling this. matt.

Matt Hurd <matt.hurd@gmail.com> writes:
David Abrahams <dave@boost-consulting.com> wrote: Daniel James <daniel@calamity.org.uk> writes:
Sorry, another feature request. Would it be possible/a good idea to add a way to get the type of a named parameter?
Certainly it's possible. Not sure if it's a good idea. Is there a use case? Some people are complaining that the library is already heavy artillery where none is needed.
A use case I have that I'm not sure how best to approach, but would find incredibly useful, with named_params is using a compile time policy to support switching between: 1. direct function call 2. an indirection to the function call, such as the marshalling and transmission of the function parameters, including formal parameter names, to a target.
To do this, named_params needs to support an interface for iterating over the parameters.
Why iterating?
Getting type info and formal parameter names may help some styles of indirection.
"may help some styles...?" Doesn't sound like a strong argument for introducing a new feature, to me.
For example, matching the type to a specific transport's marshalling routine for the same type.
The goal is being able to re-architect applications by simply changing policies at key workflow points in an application. Change from a function call, to a threaded work pool, to a call to a separate process, to a call to a separate machine via a udp/tcp or even tib rv.
I see named_params as a key piece of the puzzle in enabling this.
I don't understand the relationship at all. Why not just use an explicitly-secified function template parameter? -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams <dave@boost-consulting.com> wrote: Matt Hurd <matt.hurd@gmail.com> writes:
David Abrahams <dave@boost-consulting.com> wrote: Daniel James <daniel@calamity.org.uk> writes:
Sorry, another feature request. Would it be possible/a good idea to add a way to get the type of a named parameter?
Certainly it's possible. Not sure if it's a good idea. Is there a use case? Some people are complaining that the library is already heavy artillery where none is needed.
A use case I have that I'm not sure how best to approach, but would find incredibly useful, with named_params is using a compile time policy to support switching between: 1. direct function call 2. an indirection to the function call, such as the marshalling and transmission of the function parameters, including formal parameter names, to a target.
To do this, named_params needs to support an interface for iterating over the parameters.
Why iterating?
Getting type info and formal parameter names may help some styles of indirection.
"may help some styles...?" Doesn't sound like a strong argument for introducing a new feature, to me.
For example, matching the type to a specific transport's marshalling routine for the same type.
The goal is being able to re-architect applications by simply changing policies at key workflow points in an application. Change from a function call, to a threaded work pool, to a call to a separate process, to a call to a separate machine via a udp/tcp or even tib rv.
I see named_params as a key piece of the puzzle in enabling this.
I don't understand the relationship at all. Why not just use an explicitly-secified function template parameter?
I would like to have a generic way of injecting before a function call a call to marshalling code for a suitable transport. For some transports/protocols, such as tibco's rendezvous, having parameter names in addition to the data and any index is preferred. I was thinking that being able to interpose a policy that would interate over the parameters to the fn call and package them for transport to the policies destination would win most of the battle. Something along the lines of a functor that used named_params and some policies that would inject marshalling / sending to target, and, receiving / unmarshalling code. A null marshalling policy just calls the function without doing anything. A thread_pool marshalling policy might queue param info onto the work queue. A tibco rendezvous policy would include the formal argument names along with the parameter data..., etc... matt.

Matt Hurd <matt.hurd@gmail.com> writes:
David Abrahams <dave@boost-consulting.com> wrote: Matt Hurd <matt.hurd@gmail.com> writes:
David Abrahams <dave@boost-consulting.com> wrote: Daniel James <daniel@calamity.org.uk> writes:
Sorry, another feature request. Would it be possible/a good idea to add a way to get the type of a named parameter?
Certainly it's possible. Not sure if it's a good idea. Is there a use case? Some people are complaining that the library is already heavy artillery where none is needed.
A use case I have that I'm not sure how best to approach, but would find incredibly useful, with named_params is using a compile time policy to support switching between: 1. direct function call 2. an indirection to the function call, such as the marshalling and transmission of the function parameters, including formal parameter names, to a target.
To do this, named_params needs to support an interface for iterating over the parameters.
Why iterating?
I'd still like to know. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams wrote:
Matt Hurd <matt.hurd@gmail.com> writes:
David Abrahams <dave@boost-consulting.com> wrote: Matt Hurd <matt.hurd@gmail.com> writes:
David Abrahams <dave@boost-consulting.com> wrote: Daniel James <daniel@calamity.org.uk> writes:
Sorry, another feature request. Would it be possible/a good idea to add a way to get the type of a named parameter?
Certainly it's possible. Not sure if it's a good idea. Is there a use case? Some people are complaining that the library is already heavy artillery where none is needed.
A use case I have that I'm not sure how best to approach, but would find incredibly useful, with named_params is using a compile time policy to support switching between: 1. direct function call 2. an indirection to the function call, such as the marshalling and transmission of the function parameters, including formal parameter names, to a target.
To do this, named_params needs to support an interface for iterating over the parameters.
Why iterating?
I'd still like to know.
Here's a use case: I'd like to add support for named interfaces in phoenix. This is only possible if I can extract the params' types and values through iteration, or better yet, by packaging them in a tuple. -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

David Abrahams wrote:
Daniel James <daniel@calamity.org.uk> writes:
Sorry, another feature request. Would it be possible/a good idea to add a way to get the type of a named parameter?
Certainly it's possible. Not sure if it's a good idea. Is there a use case? Some people are complaining that the library is already heavy artillery where none is needed.
Mostly it would just be for convenience. For example, if you want to copy a parameter to a local variable, or get the iterator type for a container parameter, you can move the implementation into a separate template function which will know the type. It looks like the library is designed so that if you want to do anything non-trivial, you need to do a further function forward. I guess this is not necessarily a bad thing, since it keeps it simpler. The one use that I can think of that can't be done this way is if you want the return type of the function to be dependant on the parameters. Daniel

| -----Original Message----- | From: boost-bounces@lists.boost.org | [mailto:boost-bounces@lists.boost.org] On Behalf Of Doug Gregor | Sent: 12 November 2004 13:44 | To: Boost mailing list | Cc: boost-announce@lists.boost.org | Subject: [boost] [Review] Named parameters library | | The formal review of the Named Parameters library by David | Abrahams and | Daniel Wallin starts today and runs until Monday, November 22nd. | | - What is your evaluation of the design? Looks as though it works for most situations. | - What is your evaluation of the implementation? - but IMHO a klever kludge. | - What is your evaluation of the documentation? OK. | - What is your evaluation of the potential usefulness | of the library? Well there is DEFINITELY a need for named parameters. Why didn't the language have it to start with? | - Did you try to use the library? NO | - How much effort did you put into your evaluation? A glance. | - Are you knowledgeable about the problem domain? No | As always, please remember to clearly state whether you believe the | library should be accepted into Boost. I don't really like it - because I would VERY MUCH prefer the C++ Language to do this job properly. I don't image it will _improve_ compile times :-( Meanwhile, I suppose this may be popular with some, (even if some people may get into trouble with it), and it may show how much people like using named parameters, which may in turn provide a incentive to get it into the language proper. And those that don't like it, need not use it, so it seems fairly low risk. Paul Paul A Bristow Prizet Farmhouse, Kendal, Cumbria UK LA8 8AB +44 1539 561830 +44 7714 330204 mailto: pbristow@hetp.u-net.com

I tried to follow along with the docs... But I can't get very far. With CW8.3 I get: ### mwcc Compiler: # File: named_params_ex1.cpp # ----------------------------- # 42: std::cout << params[name] << " = " << params[value] << "\n"; # Error: ^ # pointer/array required # (point of instantiation: 'foo()') # (instantiating: 'foo_impl<boost::detail::nil>(const boost::detail::nil &)') And to see if it was just a CW problem, I also tried VC71: ..\..\temp\named_params\named_params_ex1.cpp(42) : error C2784: 'Default::result_type boost::detail::nil::operator [](const boost::detail::lazy_named_default<K,Default> &) const' : could not deduce template argument for 'const boost::detail::lazy_named_default<KW,DefaultFn> &' from 'boost::keyword<Tag>' with [ Tag=name_t ] C:\CVSROOTs\Boost-Sandbox\boost-sandbox\boost\named_params.hpp(186) : see declaration of 'boost::detail::nil::operator`[]'' ..\..\temp\named_params\named_params_ex1.cpp(24) : see reference to function template instantiation 'void foo_impl<boost::detail::nil>(const Params &)' being compiled with [ Params=boost::detail::nil ] ..\..\temp\named_params\named_params_ex1.cpp(42) : error C2784: 'Default &boost::detail::nil::operator [](const boost::detail::named_default<K,Default> &) const' : could not deduce template argument for 'const boost::detail::named_default<KW,Default> &' from 'boost::keyword<Tag>' with [ Tag=name_t ] C:\CVSROOTs\Boost-Sandbox\boost-sandbox\boost\named_params.hpp(172) : see declaration of 'boost::detail::nil::operator`[]'' ..\..\temp\named_params\named_params_ex1.cpp(42) : error C2676: binary '[' : 'const boost::detail::nil' does not define this operator or a conversion to a type acceptable to the predefined operator ..\..\temp\named_params\named_params_ex1.cpp(42) : error C2784: 'Default::result_type boost::detail::nil::operator [](const boost::detail::lazy_named_default<K,Default> &) const' : could not deduce template argument for 'const boost::detail::lazy_named_default<KW,DefaultFn> &' from 'boost::keyword<Tag>' with [ Tag=value_t ] C:\CVSROOTs\Boost-Sandbox\boost-sandbox\boost\named_params.hpp(186) : see declaration of 'boost::detail::nil::operator`[]'' ..\..\temp\named_params\named_params_ex1.cpp(42) : error C2784: 'Default &boost::detail::nil::operator [](const boost::detail::named_default<K,Default> &) const' : could not deduce template argument for 'const boost::detail::named_default<KW,Default> &' from 'boost::keyword<Tag>' with [ Tag=value_t ] C:\CVSROOTs\Boost-Sandbox\boost-sandbox\boost\named_params.hpp(172) : see declaration of 'boost::detail::nil::operator`[]'' ..\..\temp\named_params\named_params_ex1.cpp(42) : error C2676: binary '[' : 'const boost::detail::nil' does not define this operator or a conversion to a type acceptable to the predefined operator Makes it's kinda hard to understand when it fails like this :-( #include <boost/named_params.hpp> #include <iostream> struct name_t; // tag types struct value_t; namespace { boost::keyword<name_t> name; // keyword objects boost::keyword<value_t> value; } struct foo_keywords : boost::keywords< name_t , value_t > {}; template<class Params> void foo_impl(const Params&); void foo() { foo_impl(foo_keywords()()); } template<class A0> void foo(const A0& a0) { foo_impl(foo_keywords()(a0)); } template<class A0, class A1> void foo(const A0& a0, const A1& a1) { foo_impl(foo_keywords()(a0, a1)); } template<class Params> void foo_impl(const Params& params) { std::cout << params[name] << " = " << params[value] << "\n"; } int main() { foo("bar", 3.14f); foo(value = 6.28f, "baz"); return 0; }

Oops.. The example in the docs seems to be wrong. It's suppose to be: int main() { foo("bar", 3.14f); foo("baz", value = 6.28f); return 0; } As written in the docs the "name" parameter doesn't get a value, thus the compilation error. To get it to compile, the nullary foo_impl() overload needs to be removed as well. It can never be valid since neither of the arguments have default values. Sorry about that. Rene Rivera wrote:
I tried to follow along with the docs... But I can't get very far. With CW8.3 I get:
### mwcc Compiler: # File: named_params_ex1.cpp # ----------------------------- # 42: std::cout << params[name] << " = " << params[value] << "\n"; # Error: ^ # pointer/array required # (point of instantiation: 'foo()') # (instantiating: 'foo_impl<boost::detail::nil>(const boost::detail::nil &)')
And to see if it was just a CW problem, I also tried VC71:
..\..\temp\named_params\named_params_ex1.cpp(42) : error C2784: 'Default::result_type boost::detail::nil::operator [](const boost::detail::lazy_named_default<K,Default> &) const' : could not deduce template argument for 'const boost::detail::lazy_named_default<KW,DefaultFn> &' from 'boost::keyword<Tag>' with [ Tag=name_t ]
C:\CVSROOTs\Boost-Sandbox\boost-sandbox\boost\named_params.hpp(186) : see declaration of 'boost::detail::nil::operator`[]'' ..\..\temp\named_params\named_params_ex1.cpp(24) : see reference to function template instantiation 'void foo_impl<boost::detail::nil>(const Params &)' being compiled with [ Params=boost::detail::nil ] ..\..\temp\named_params\named_params_ex1.cpp(42) : error C2784: 'Default &boost::detail::nil::operator [](const boost::detail::named_default<K,Default> &) const' : could not deduce template argument for 'const boost::detail::named_default<KW,Default> &' from 'boost::keyword<Tag>' with [ Tag=name_t ]
C:\CVSROOTs\Boost-Sandbox\boost-sandbox\boost\named_params.hpp(172) : see declaration of 'boost::detail::nil::operator`[]'' ..\..\temp\named_params\named_params_ex1.cpp(42) : error C2676: binary '[' : 'const boost::detail::nil' does not define this operator or a conversion to a type acceptable to the predefined operator ..\..\temp\named_params\named_params_ex1.cpp(42) : error C2784: 'Default::result_type boost::detail::nil::operator [](const boost::detail::lazy_named_default<K,Default> &) const' : could not deduce template argument for 'const boost::detail::lazy_named_default<KW,DefaultFn> &' from 'boost::keyword<Tag>' with [ Tag=value_t ]
C:\CVSROOTs\Boost-Sandbox\boost-sandbox\boost\named_params.hpp(186) : see declaration of 'boost::detail::nil::operator`[]'' ..\..\temp\named_params\named_params_ex1.cpp(42) : error C2784: 'Default &boost::detail::nil::operator [](const boost::detail::named_default<K,Default> &) const' : could not deduce template argument for 'const boost::detail::named_default<KW,Default> &' from 'boost::keyword<Tag>' with [ Tag=value_t ]
C:\CVSROOTs\Boost-Sandbox\boost-sandbox\boost\named_params.hpp(172) : see declaration of 'boost::detail::nil::operator`[]'' ..\..\temp\named_params\named_params_ex1.cpp(42) : error C2676: binary '[' : 'const boost::detail::nil' does not define this operator or a conversion to a type acceptable to the predefined operator
Makes it's kinda hard to understand when it fails like this :-(
------------------------------------------------------------------------
#include <boost/named_params.hpp> #include <iostream>
struct name_t; // tag types struct value_t;
namespace { boost::keyword<name_t> name; // keyword objects boost::keyword<value_t> value; }
struct foo_keywords : boost::keywords< name_t , value_t > {};
template<class Params> void foo_impl(const Params&);
void foo() { foo_impl(foo_keywords()()); }
template<class A0> void foo(const A0& a0) { foo_impl(foo_keywords()(a0)); }
template<class A0, class A1> void foo(const A0& a0, const A1& a1) { foo_impl(foo_keywords()(a0, a1)); }
template<class Params> void foo_impl(const Params& params) { std::cout << params[name] << " = " << params[value] << "\n"; }
int main() { foo("bar", 3.14f); foo(value = 6.28f, "baz");
return 0; }
------------------------------------------------------------------------
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
-- Daniel Wallin

"Doug Gregor" <dgregor@cs.indiana.edu> wrote in message news:E19435DE-34B0-11D9-A026-000A95B0EC64@cs.indiana.edu... The formal review of the Named Parameters library by David Abrahams and Daniel Wallin starts today and runs until Monday, November 22nd. - What is your evaluation of the design? Overall I like the idea of named parameters, basically because they are an aid to code legibility as staed in the docs. Anything that provides in place documentation is extremely useful facility. Therefore In contrast to the BGL approach I think this is more legible. I am assuming that the BGL approach must uses member functions in some class ruling out the assignment style syntax, and presumably requiring the class to be written, so this must be more 'generic'.... After trying out the library to see if it might help make initialisation of a transform matrix more legible. (Note obviously more efficient to pass in the matrix as a non const reference, but the legibility starts to suffer. OTOH Ideally It woould be nice to use something similar to named params in a constructor... but I assume that the lib wont support that ?. However at the end of the day Some equivalent to this libraray might actually be more useful in constructors than for functions ) //bgl style ? tx_matrix<units> t = make_matrix( translation (xy_pair<units>(units(1),units(1))). rotation (angle::rad(1)). scaling (.5) ); tx_matrix<units> t = make_matrix( translation = xy_pair<units>(units(1),units(1)), rotation = angle::rad(1), scaling = .5 ); I come to the conclusion that the second format is aesthetically better than the first, which I guess covers the design. I cant see a better way to do it. - What is your evaluation of the implementation? As far as looking into the implementation is concerned I was interested to see a custom list used. I guess there are good reasons for this, but I thought mpl and tuple were designed for this type of job?. Whatever I dont really have the experience to make any assessment of the implementation The Helper macro BOOST_NAMED_PARAMS_FUN is very useful as far as it goes. Of course in the make_matrix function above the return type would be a template type. Obviously the macro cant deal with that or can it?, which is annoying as implementing the thing manually is relatively time consuming. OTOH I have no idea what the alternative might be though. Although it makes life easy for users, at the implementation level a fair amount of work is required once you stray from simple use. - What is your evaluation of the documentation? Well I followed through on the examples and finally got it to run. It is slightly *amusing* to keep writing the code for the examples and then be told 'but of course that doesnt work' (2.3 Defining the implementation function) - What is your evaluation of the potential usefulness of the library? I think it is very useful for increasing source code legibility. Though bear in mind that a reasonable amount of time needs to be spent by the implementor to make the users life easy ( I suspect the BOOST_NAMED_PARAMS_FUN MACRO doesnt work except in the simplest cases). No gain no pain I guess.. - Did you try to use the library? With what compiler? Tested on VC7.1 Did you have any problems? I had some problems with the boost::mpl::_ widget ... but I guess this shouldnt be used with the BOOST_NAMED_PARAMS_FUN macro.. not further explored for lack of time. The SFINAE looks like it may get quite hairy, where a number of types need to be restricted, though I'm not sure that this cant be done simply by using enable_if in the return type of the function?:-) - How much effort did you put into your evaluation? A glance? A quick reading? In-depth study? I played around for an hour or so. - Are you knowledgeable about the problem domain? If the domain is function parameters ... I guess I use them from tine to time .. As always, please remember to clearly state whether you believe the library should be accepted into Boost. Yes. Thank you Doug Gregor Review Manager, Named Parameters library _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

"Andy Little" <andy@servocomm.freeserve.co.uk> wrote
tx_matrix<units> t = make_matrix( translation = xy_pair<units>(units(1),units(1)), rotation = angle::rad(1), scaling = .5 );
Of course The order of the paramaters in the above example is important, hence rendering the named parameters library unsuitable for this particular purpose. Apologies for that example regards Andy Little

"Andy Little" <andy@servocomm.freeserve.co.uk> writes:
"Andy Little" <andy@servocomm.freeserve.co.uk> wrote
tx_matrix<units> t = make_matrix( translation = xy_pair<units>(units(1),units(1)), rotation = angle::rad(1), scaling = .5 );
Of course The order of the paramaters in the above example is important, hence rendering the named parameters library unsuitable for this particular purpose.
Apologies for that example
Well, it brings up an interesting point; maybe some sort of visitation interface that could explore all parameters in order would be appropriate. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

"Andy Little" <andy@servocomm.freeserve.co.uk> writes:
"Andy Little" <andy@servocomm.freeserve.co.uk> wrote
tx_matrix<units> t = make_matrix( translation = xy_pair<units>(units(1),units(1)), rotation = angle::rad(1), scaling = .5 );
Of course The order of the paramaters in the above example is important, hence rendering the named parameters library unsuitable for this
"David Abrahams" <dave@boost-consulting.com> wrote particular
purpose.
Apologies for that example
Well, it brings up an interesting point; maybe some sort of visitation interface that could explore all parameters in order would be appropriate.
I have a feeling its a red herring unfortunately... not really in the same ballpark as the named parameters library... eg This should be legal : // matrix for rotation about (1,1).... tx_matrix<units> t = make_matrix( translation = make_xy_pair(units( -1),units(-1)), // to origin rotation = angle::rad(1), translation = make_xy_pair(units(1),units(1)) // ...Oops! ); The simple,obvious solution would work here I guess: tx_matrix<units> t ( translation( units( -1),units(-1)), rotation(angle::rad(1)), translation( units(1), units(1)) ); regards Andy Little

- What is your evaluation of the design?
Good
- What is your evaluation of the implementation?
Good
- What is your evaluation of the documentation?
Insufficient I had some initial problems with following the example in the docs, as it has bugs. But after that was cleared up by Daniel I was able to play around with what the library can do. There are some things that the documentation is lacking: a) A reference section describing what the types are and what operations are possible. If it wasn't for Daniel responding that there was an operator, present it would have taken a close look at the source to figure out it was and how it could be used. b) Coverage of some alternate ways to use the library. For example... For me I don't really care about providing a positional interface when the named interface is available. But I do care about not having the implementation visible, and get duplicated for each argument ordering. So I would not use the forwarding functions as given. Instead I would use the named params interface as the forwarding layer. Hence the example that I would find useful would go something like: #include <iostream> void foo(const char * name, float value) { std::cout << name << " = " << value << "\n"; } #include <boost/named_params.hpp> struct name_t; // tag types struct value_t; namespace { boost::keyword<name_t> name; // keyword objects boost::keyword<value_t> value; } template<class Params> inline void foo(const Params& params) { foo(params[name],params[value]); } int main() { foo((name = "bar", value = 3.14f)); foo((value = 5.2f, name = "baz")); return 0; } c) Mention of the drawback of the larger number of function definitions that the library will create. And approach as to minimizing the code size impact. Hence my example in (b) of forwarding to a classic function which has the single real implementation.
- What is your evaluation of the potential usefulness of the library?
High This is functionality that I've been waiting for since growing up using Amiga TAGARGS. The use of this library has the potential to drive changes to C++ to support named args in a nicer way, which is what's really needed.
- Did you try to use the library?? With what compiler?? Did you have any problems?
Yes, with CW83 and VC71. Only problem was with the broken example.
- How much effort did you put into your evaluation? A glance? A quick reading? In-depth study?
Somewhere in the close study, but not near in-depth study.
- Are you knowledgeable about the problem domain?
Yes. Have used named args of one sort of other for many years, and implemented some in the past.
As always, please remember to clearly state whether you believe the library should be accepted into Boost.
YES. (Assuming the documentation will improve in the future) -- -- Grafik - Don't Assume Anything -- Redshift Software, Inc. - http://redshift-software.com -- rrivera/acm.org - grafik/redshift-software.com - 102708583/icq

Rene Rivera wrote: [...]
#include <boost/named_params.hpp>
struct name_t; // tag types struct value_t;
namespace { boost::keyword<name_t> name; // keyword objects
boost::keyword<struct name_t> name; is even better. ;-)
boost::keyword<value_t> value; }
template<class Params> inline void foo(const Params& params) { foo(params[name],params[value]); }
int main() { foo((name = "bar", value = 3.14f)); foo((value = 5.2f, name = "baz"));
return 0; }
FWIW, I also like this style.

Peter Dimov wrote:
Rene Rivera wrote:
[...]
#include <boost/named_params.hpp>
struct name_t; // tag types struct value_t;
namespace { boost::keyword<name_t> name; // keyword objects
boost::keyword<struct name_t> name;
is even better. ;-)
Yes it is :-) -- But I was going under the suggestion in the docs: [Note: the tag types should generally not be declared in an unnamed namespace] It would be nice to see a rational for that note in the docs. Only one I can think of is the increased number of keyword objects, and possibly increased number of the function instantiations. -- -- Grafik - Don't Assume Anything -- Redshift Software, Inc. - http://redshift-software.com -- rrivera/acm.org - grafik/redshift-software.com - 102708583/icq

Rene Rivera <grafik.list@redshift-software.com> writes:
Peter Dimov wrote:
Rene Rivera wrote: [...]
#include <boost/named_params.hpp>
struct name_t; // tag types struct value_t;
namespace { boost::keyword<name_t> name; // keyword objects boost::keyword<struct name_t> name; is even better. ;-)
Yes it is :-) -- But I was going under the suggestion in the docs:
[Note: the tag types should generally not be declared in an unnamed namespace]
It would be nice to see a rational for that note in the docs. Only one I can think of is the increased number of keyword objects, and possibly increased number of the function instantiations.
It's an ODR thing. If you then use the parameter names in a function template that's not in an unnamed namespace but _is_ in multiple TU's, you have technically violated the ODR. In general, types in header files had better stay out of the unnamed namespace. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams <dave <at> boost-consulting.com> writes:
Rene Rivera <grafik.list <at> redshift-software.com> writes:
It's an ODR thing. If you then use the parameter names in a function template that's not in an unnamed namespace but _is_ in multiple TU's, you have technically violated the ODR.
In general, types in header files had better stay out of the unnamed namespace.
In fact, it looks like to be really safe from ODR violations we need to require the keyword objects to be declared as _references_ in the unnamed namespace using the unique_instance trick, per http://thread.gmane.org/gmane.comp.lib.boost.devel/126455 because a function template using named parameters and instantiated in different translation units might otherwise use different objects. It's very tempting to ignore this problem, because IMO it will *never* cause a problem in practice, and because it complicates the usage and documentation of the parameter library. We also need someplace to put unique_instance. It's too general to go in boost::parameter and it's not suitable for boost::detail since users have to touch it. Any helpful ideas about all this would be appreciated, especially if you can find standard text that makes it all go away ;-) For more context, please see my Nov 16th reply to Rene in http://thread.gmane.org/gmane.comp.lib.boost.devel/113158 GMane's posting nanny won't let me quote very much. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams wrote:
For more context, please see my Nov 16th reply to Rene in http://thread.gmane.org/gmane.comp.lib.boost.devel/113158 GMane's posting nanny won't let me quote very much.
Since it took me a bit to find that reply in the long thread here's a direct link: http://article.gmane.org/gmane.comp.lib.boost.devel/113401 -- -- Grafik - Don't Assume Anything -- Redshift Software, Inc. - http://redshift-software.com -- rrivera/acm.org - grafik/redshift-software.com -- 102708583/icq - grafikrobot/aim - Grafik/jabber.org

David Abrahams <dave@boost-consulting.com> writes:
David Abrahams <dave <at> boost-consulting.com> writes:
Rene Rivera <grafik.list <at> redshift-software.com> writes:
It's an ODR thing. If you then use the parameter names in a function template that's not in an unnamed namespace but _is_ in multiple TU's, you have technically violated the ODR.
In general, types in header files had better stay out of the unnamed namespace.
In fact, it looks like to be really safe from ODR violations we need to require the keyword objects to be declared as _references_ in the unnamed namespace using the unique_instance trick, per http://thread.gmane.org/gmane.comp.lib.boost.devel/126455 because a function template using named parameters and instantiated in different translation units might otherwise use different objects. It's very tempting to ignore this problem, because IMO it will *never* cause a problem in practice, and because it complicates the usage and documentation of the parameter library. We also need someplace to put unique_instance. It's too general to go in boost::parameter and it's not suitable for boost::detail since users have to touch it.
Any helpful ideas about all this would be appreciated, especially if you can find standard text that makes it all go away ;-)
For more context, please see my Nov 16th reply to Rene in http://thread.gmane.org/gmane.comp.lib.boost.devel/113158 GMane's posting nanny won't let me quote very much.
So far, the best I can come up with is: namespace { using namespace boost::parameters keyword<foo_> foo& = instance(); ... } But the conversion from instance() to keyword<foo_>& doesn't port to broken compilers (vc6), which leaves me with: namespace { using namespace boost::parameters keyword<foo_> foo& = keyword<foo_>::get(); ... } -- Dave Abrahams Boost Consulting www.boost-consulting.com

"Peter Dimov" <pdimov@mmltd.net> writes:
Rene Rivera wrote:
[...]
#include <boost/named_params.hpp> struct name_t; // tag types struct value_t; namespace { boost::keyword<name_t> name; // keyword objects
boost::keyword<struct name_t> name;
is even better. ;-)
You mean, leave out the struct name_t; // tag types struct value_t; declarations? I didn't even know you could do that! You are truly an evil genius. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

Huzzah, my first review!
- What is your evaluation of the design?
Groovy, but not quite the schiznit. A few major questions that will pop up include how users should name the keyword objects for their functions, and where these should be declared. Take the headers-only BGL as an example: should the keyword objects be in an unnamed namespace, according to the named-params docs? Since they may be used so often, should they be defined in a single library-specific header, e.g. <boost/graph/keywords.hpp>? And would it help if each name had an appropriate prefix, like "bgl_", to help avoid object collisions? Also, I second the feature request of getting the type of a named parameter. Numerous BGL algorithms require a graph, an index map that defaults to get(vertex_index_t(), graph), and a color/rank/distance map that defaults to a random_access_iterator_property_map over the index map. Without the type-getting ability (and thus the ability to make temporary assignments), I may have to duplicate the default-assigning code over several places, like I'm doing now retooling my graph algorithms in the sandbox. What I'm really asking for is an elegant way to assign a parameter a default value that is dependent on another parameter. May I safely assume that we plan on replacing the bgl_named_params mechanism with this one in the not-too-distant future? If so, then I feel that these issues need to be addressed.
- What is your evaluation of the implementation?
Mmmm, preprocessing and MPL, like spaghetti and meatballs. So difficult to cook up, but so delicious when served. On the plus side, it beats having to use the undocumented choose_pmap and choose_const_pmap functions to extract property maps from bgl_named_params, among other things. I find operator| to be the most logical operator for assigning defaults, more so than operator& or operator=. The major stumbling block concerns BOOST_NAMED_PARAMS_MAX_ARITY. It looks like it is set arbitrarily to 5, but setting it any higher (before including any header files, as suggested) led to errors like "no such type named boost::mpl::apply9". This meant that BOOST_NAMED_PARAMS_MAX_ARITY is actually tied to something in the MPL library, namely BOOST_MPL_LIMIT_METAFUNCTION_ARITY. IIRC in addtion declaring *that* value, BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS has to be set before including a library header. I doubt an ordinary user would relish following these steps just to pass in more than 5 arguments. And many of the BGL algorithms take in more than 5 arguments. I believe the modified <boost/named_params.hpp> header attached to this message solves this problem, but I'm not sure.
- What is your evaluation of the documentation?
The autogenerating macro should have a more prominent place in the docs. Potential users might get turned off by the number of function overloads they see first, before they get to see the usefulness of this macro. I believe the "best practices" section that someone else came up with (or was that my imagination?) should include suggested ways to resolve the keyword object issues I raised.
- What is your evaluation of the potential usefulness of the library?
It would definitely be useful even outside the BGL. In my multistate mazes project, for example, I have a Parallelepiped class template whose constructor and initializing member function take in something akin to a grid-bag layout (i.e. cell-padding, cell-spacing, and cell-size parameters). This named-parameters mechanism might just supplant the monolithic class template I'm currently using to represent this layout. That my multistate mazes project also uses the BGL is somewhat beside the point...
- Did you try to use the library? With what compiler? Did you have any problems?
I used my main development compiler, GCC 3.2 (MinGW, Dev-C++ 4.9.8.5). The docs state it as a supported compiler, but the test program did not compile until Daniel Wallin's workaround was applied. Oh, well.
- How much effort did you put into your evaluation? A glance? A quick reading? In-depth study?
My effort is in-depth, but not as much as I'd like. (I have yet to test any function that might require boost::ref(), for instance).
- Are you knowledgeable about the problem domain?
I'm familiar with the principle of named parameters, but not with many of the technical challenges.
As always, please remember to clearly state whether you believe the library should be accepted into Boost.
The resolution of the design issue concerning parameter-dependent defaults bears more weight on my vote than any of the other pluses or minuses I've mentioned so far. If it can be resolved easily, then I vote yes. Otherwise, not until it is resolved. Cromwell Enage __________________________________ Do you Yahoo!? The all-new My Yahoo! - Get yours free! http://my.yahoo.com

Cromwell Enage <sponage@yahoo.com> writes:
Huzzah, my first review!
- What is your evaluation of the design?
Groovy, but not quite the schiznit.
Wow, and the first review I've seen that had real street cred.
A few major questions that will pop up include how users should name the keyword objects for their functions, and where these should be declared. Take the headers-only BGL as an example: should the keyword objects be in an unnamed namespace, according to the named-params docs?
Yes.
Since they may be used so often, should they be defined in a single library-specific header, e.g. <boost/graph/keywords.hpp>?
Not neccessarily. They should probably be decoupled and included as needed in each header.
And would it help if each name had an appropriate prefix, like "bgl_", to help avoid object collisions?
Not much; I would nest the unnamed namespace inside boost (or a sub-namespace thereof).
Also, I second the feature request of getting the type of a named parameter. Numerous BGL algorithms require a graph, an index map that defaults to get(vertex_index_t(), graph), and a color/rank/distance map that defaults to a random_access_iterator_property_map over the index map. Without the type-getting ability (and thus the ability to make temporary assignments), I may have to duplicate the default-assigning code over several places, like I'm doing now retooling my graph algorithms in the sandbox. What I'm really asking for is an elegant way to assign a parameter a default value that is dependent on another parameter.
I don't understand. Why would you need to be able to get the type in order to do that? p[dependent | some_expression.involving(p[dependency])] Nevermind, I think I understand now: p[dependency | default1] p[dependent | some_expression.involving(p[dependency | default1])] I agree that you ought to that capability.
May I safely assume that we plan on replacing the bgl_named_params mechanism with this one in the not-too-distant future?
That's really up to the BGL maintainers, but last I heard yes.
If so, then I feel that these issues need to be addressed.
- What is your evaluation of the implementation?
Mmmm, preprocessing and MPL, like spaghetti and meatballs. So difficult to cook up, but so delicious when served.
On the plus side, it beats having to use the undocumented choose_pmap and choose_const_pmap functions to extract property maps from bgl_named_params, among other things.
I find operator| to be the most logical operator for assigning defaults, more so than operator& or operator=.
I'm not positively sure what you're saying here. Our design does use operator| for "providing" defaults. They're never "assigned." To do that, you would (tautologically) have to use operator=.
The major stumbling block concerns BOOST_NAMED_PARAMS_MAX_ARITY. It looks like it is set arbitrarily to 5, but setting it any higher (before including any header files, as suggested) led to errors like "no such type named boost::mpl::apply9". This meant that BOOST_NAMED_PARAMS_MAX_ARITY is actually tied to something in the MPL library, namely BOOST_MPL_LIMIT_METAFUNCTION_ARITY. IIRC in addtion declaring *that* value, BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS has to be set before including a library header. I doubt an ordinary user would relish following these steps just to pass in more than 5 arguments. And many of the BGL algorithms take in more than 5 arguments.
Thanks, I'm glad you tracked that down. We need to figure out what to do about it.
I believe the modified <boost/named_params.hpp> header attached to this message solves this problem, but I'm not sure.
Hey, nifty!
- What is your evaluation of the documentation?
The autogenerating macro should have a more prominent place in the docs. Potential users might get turned off by the number of function overloads they see first, before they get to see the usefulness of this macro.
Yeah, we probably ought to document the operator, usage up front, too.
As always, please remember to clearly state whether you believe the library should be accepted into Boost.
The resolution of the design issue concerning parameter-dependent defaults bears more weight on my vote than any of the other pluses or minuses I've mentioned so far. If it can be resolved easily, then I vote yes. Otherwise, not until it is resolved.
I'm positive we can do something about it without too much trouble. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

On Nov 17, 2004, at 6:41 PM, David Abrahams wrote:
Cromwell Enage <sponage@yahoo.com> writes:
May I safely assume that we plan on replacing the bgl_named_params mechanism with this one in the not-too-distant future?
That's really up to the BGL maintainers, but last I heard yes.
Yes. I'm looking for the proper migration path so we don't have a mess when we try to bash the two systems together. Doug

--- David Abrahams <dave@boost-consulting.com> wrote:
Cromwell Enage <sponage@yahoo.com> writes:
Since they may be used so often, should they be defined in a single library-specific header, e.g. <boost/graph/keywords.hpp>?
Not neccessarily. They should probably be decoupled and included as needed in each header.
And would it help if each name had an appropriate prefix, like "bgl_", to help avoid object collisions?
Not much; I would nest the unnamed namespace inside boost (or a sub-namespace thereof).
Okay, I'll try to organize my code accordingly.
I find operator| to be the most logical operator for assigning defaults, more so than operator& or operator=.
I'm not positively sure what you're saying here. Our design does use operator| for "providing" defaults. They're never "assigned." To do that, you would (tautologically) have to use operator=.
Bad wording on my part.
The resolution of the design issue concerning parameter-dependent defaults bears more weight on my vote than any of the other pluses or minuses I've mentioned so far. If it can be resolved easily, then I vote yes. Otherwise, not until it is resolved.
I'm positive we can do something about it without too much trouble.
Very well. You have my accept vote! Cromwell Enage __________________________________________________ Do You Yahoo!? Tired of spam? Yahoo! Mail has the best spam protection around http://mail.yahoo.com

On 11/12/04 8:43 AM, "Doug Gregor" <dgregor@cs.indiana.edu> wrote:
The formal review of the Named Parameters library by David Abrahams and Daniel Wallin starts today and runs until Monday, November 22nd.
Did you put this notice on the announce list?
The Named Parameters library provides a framework for writing functions that can accept arguments by name or by position. The use of named parameters can improve the readability of calls to these functions, especially when the functions have many parameters with default values. The Named Parameters library allows one to write a new_window function that allows a call such as:
window* w = new_window("alert2", movable = true);
The library is available in the Boost sandbox here:
boost/named_params.hpp libs/utility
And in the files area (login required):
http://groups.yahoo.com/group/boost/files/named_params.zip
Reviewers may wish to compare this solution to the named parameters already used by the Boost Graph Library, documented here:
This new solution looks nicer that what's given in Boost.Graph. I haven't looked at the implementation of Boost.Graph's solution, though.
Please send your reviews either to the Boost list or to me personally. The former is preferred, because it allows the community to consider your review as well. When writing your review, you may wish to consider the following questions (from http://www.boost.org/more/formal_review_process.htm#Comments)
- What is your evaluation of the design?
It looks OK.
- What is your evaluation of the implementation?
I don't really understand it (yet) since it's heavy with Boost's buzzword-worthy libraries.
- What is your evaluation of the documentation?
It's OK.
- What is your evaluation of the potential usefulness of the library?
It only needs to be used for functions with many arguments, especially if the order was arbitrary. Code that otherwise doesn't use Boost's heavy-hitter libraries may want to skip it.
- Did you try to use the library? With what compiler? Did you have any problems?
It seems to have worked on my system (Mac OS X 10.3.6 using Apple's custom GCC).
- How much effort did you put into your evaluation? A glance? A quick reading? In-depth study?
A quick reading
- Are you knowledgeable about the problem domain?
Not really, unless you count how my proposed basic_ios_form works
As always, please remember to clearly state whether you believe the library should be accepted into Boost.
Yes. -- Daryle Walker Mac, Internet, and Video Game Junkie darylew AT hotmail DOT com

On Nov 17, 2004, at 5:30 PM, Daryle Walker wrote:
On 11/12/04 8:43 AM, "Doug Gregor" <dgregor@cs.indiana.edu> wrote:
The formal review of the Named Parameters library by David Abrahams and Daniel Wallin starts today and runs until Monday, November 22nd.
Did you put this notice on the announce list?
<Digs through junk mail box> Oops, looks like it got rejected from the announce list. I'll get it out there, too. Doug

Hi, I vote to accept the Named parameters library into boost. It fills a need that is lacking in C++.
- What is your evaluation of the design?
Very good. Some comments: 1) WRT DSL, I'm not quite sure about the syntax for defaults. Example: params[name | "unnamed"] I have a strong feeling that there's a better syntax but I am not sure what it is now. I read in the thread that the syntax params[name] | "unnamed" is not doable, but I'm not quite sure. Perhaps a clarification will set me straight. Having the default in the brackets seems to rather awkward. Perhaps: params[name].defaults_to("unnamed") is better? 2) Automatic Overload Generation Is it not possible to elide the use of macros? Instead of: template<class Params> void foo_impl(const Params&); BOOST_NAMED_PARAMS_FUN( return_type, function_name , min_arity, max_arity, keywords_type ); I'd very much prefer writing something like: struct foo_ { template<class Params> struct result { typedef void type; }; template<class Params> void eval(const Params&) const; }; named_interface<foo_, foo_keywords> const foo = foo_(); 3) Which brings me to my third point: I notice that BOOST_NAMED_PARAMS_FUN has a fixed return type. IMO, this is a big drawback of the macro aproach. The non-macro approach I presented is fully polymorphic WRT its return type. IOTW, the return type can be dependent on the arguments (Params). Oh and, also, I need to be able to generate the overloads through templates instead of auto generatng the overloads through a macro. Why is this important? I like the named interfaces a lot and I'd like to add the facility to Phoenix (BTW, the approach outlined above is reminiscent of phoenix::function). The two issues I mentioned above hinders me from using named interfaces. Fortunately, both appraoches are orthogonal. I can easily add the named_interface<foo_> overload generators from within Phoenix, so this is not such a big issue.
- What is your evaluation of the implementation?
Well written.
- What is your evaluation of the documentation?
Adequate but there's definitely room for improvement. For example, there is a section (2.2 Defining the forwarding functions) without any text at all; just code.
- What is your evaluation of the potential usefulness of the
library? I know the usefulness of named params from other languages. Even the humble HTML language has named parameters. Example: <img src="hello.gif" alt="Hello">
- Did you try to use the library? With what compiler?
Yes. VC7.1.
Did you have any problems?
No.
- How much effort did you put into your evaluation? A glance? A quick reading? In-depth study?
I've been following the development since the beginning.
- Are you knowledgeable about the problem domain?
Yes. -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

Joel de Guzman wrote:
1) WRT DSL, I'm not quite sure about the syntax for defaults. Example:
params[name | "unnamed"]
I have a strong feeling that there's a better syntax but I am not sure what it is now. I read in the thread that the syntax
params[name] | "unnamed"
is not doable, but I'm not quite sure. Perhaps a clarification will set me straight. Having the default in the brackets seems to rather awkward. Perhaps:
params[name].defaults_to("unnamed")
is better?
That has exactly the same problem as having the | outside the brackets. The problem is that operator[] would need to return something other than the actual parameter reference, something that: 1) Is convertible to the parameter type. We still need params[name], with no default to work. 2) Has operator|() or defaults_to() member function. So, we'd need a conversion operator. But if we have a conversion operator, we can't reliably pass the arguments to function templates because the type deduction will go wrong. Does that help to clear things up?
2) Automatic Overload Generation
Is it not possible to elide the use of macros? Instead of:
template<class Params> void foo_impl(const Params&);
BOOST_NAMED_PARAMS_FUN( return_type, function_name , min_arity, max_arity, keywords_type );
I'd very much prefer writing something like:
struct foo_ { template<class Params> struct result { typedef void type; };
template<class Params> void eval(const Params&) const; };
named_interface<foo_, foo_keywords> const foo = foo_();
Yes, it's doable. It's a pretty simple extension to the library. In fact, IIRC, one of the early design ideas was to do something like that. I think we didn't go down that path because someone might want ADL to be enabled for their functions with named params. Maybe we could add something like this after the review. -- Daniel Wallin

Daniel Wallin wrote:
Joel de Guzman wrote:
1) WRT DSL, I'm not quite sure about the syntax for defaults. Example:
params[name | "unnamed"]
I have a strong feeling that there's a better syntax but I am not sure what it is now. I read in the thread that the syntax
params[name] | "unnamed"
is not doable, but I'm not quite sure. Perhaps a clarification will set me straight. Having the default in the brackets seems to rather awkward. Perhaps:
params[name].defaults_to("unnamed")
is better?
That has exactly the same problem as having the | outside the brackets. The problem is that operator[] would need to return something other than the actual parameter reference, something that:
1) Is convertible to the parameter type.
We still need params[name], with no default to work.
2) Has operator|() or defaults_to() member function.
So, we'd need a conversion operator. But if we have a conversion operator, we can't reliably pass the arguments to function templates because the type deduction will go wrong.
Does that help to clear things up?
I see. Yes, that clears things up.
2) Automatic Overload Generation
Is it not possible to elide the use of macros? Instead of:
template<class Params> void foo_impl(const Params&);
BOOST_NAMED_PARAMS_FUN( return_type, function_name , min_arity, max_arity, keywords_type );
I'd very much prefer writing something like:
struct foo_ { template<class Params> struct result { typedef void type; };
template<class Params> void eval(const Params&) const; };
named_interface<foo_, foo_keywords> const foo = foo_();
Yes, it's doable. It's a pretty simple extension to the library. In fact, IIRC, one of the early design ideas was to do something like that. I think we didn't go down that path because someone might want ADL to be enabled for their functions with named params.
Yes, I'm aware of the tradeofs. The good thing is that we can have both. There are clear advantages of functions as objects like foo as an instantiation of named_interface<foo_, foo_keywords>.
Maybe we could add something like this after the review.
Should be pretty straightforward. -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

Daniel Wallin <dalwan01@student.umu.se> writes:
Joel de Guzman wrote:
1) WRT DSL, I'm not quite sure about the syntax for defaults. Example: params[name | "unnamed"] I have a strong feeling that there's a better syntax but I am not sure what it is now. I read in the thread that the syntax params[name] | "unnamed" is not doable, but I'm not quite sure. Perhaps a clarification will set me straight. Having the default in the brackets seems to rather awkward. Perhaps: params[name].defaults_to("unnamed") is better?
That has exactly the same problem as having the | outside the brackets. The problem is that operator[] would need to return something other than the actual parameter reference, something that:
1) Is convertible to the parameter type.
We still need params[name], with no default to work.
2) Has operator|() or defaults_to() member function.
So, we'd need a conversion operator. But if we have a conversion operator, we can't reliably pass the arguments to function templates because the type deduction will go wrong.
One possible alternative is params.defaults_to("unnamed")[name] but I find that highly illogical to read, because the specification of the default precedes the keyword it's defaulting. However, Thorsten's prodding has made me wonder if we need || for the lazy case at all. It seems as though params.has(name) ? params[name] : something_else; is superior in every way except brevity. And there will be substantial cases where it's briefer as well, because there's no need to build a function object for something_else. Am I missing something? -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams wrote:
However, Thorsten's prodding has made me wonder if we need || for the lazy case at all. It seems as though
params.has(name) ? params[name] : something_else;
is superior in every way except brevity. And there will be substantial cases where it's briefer as well, because there's no need to build a function object for something_else.
Am I missing something?
Yes, I think you are. We want params[name] to be a compilation error if there is no such parameter supplied by the user and no default. I don't think the proposed construct can allow that. IMO, a runtime error here is just not acceptable. -- Daniel Wallin

Daniel Wallin <dalwan01@student.umu.se> writes:
David Abrahams wrote:
However, Thorsten's prodding has made me wonder if we need || for the lazy case at all. It seems as though params.has(name) ? params[name] : something_else; is superior in every way except brevity. And there will be substantial cases where it's briefer as well, because there's no need to build a function object for something_else. Am I missing something?
Yes, I think you are. We want params[name] to be a compilation error if there is no such parameter supplied by the user and no default. I don't think the proposed construct can allow that.
Duh, you're right.
IMO, a runtime error here is just not acceptable.
Okay. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

"Daniel Wallin" <dalwan01@student.umu.se> wrote in message news:cnidaq$tim$1@sea.gmane.org...
David Abrahams wrote:
However, Thorsten's prodding has made me wonder if we need || for the lazy case at all. It seems as though
params.has(name) ? params[name] : something_else;
is superior in every way except brevity. And there will be substantial cases where it's briefer as well, because there's no need to build a function object for something_else.
Am I missing something?
Yes, I think you are. We want params[name] to be a compilation error if there is no such parameter supplied by the user and no default. I don't think the proposed construct can allow that.
IMO, a runtime error here is just not acceptable.
I disagree. I don't see compile-time error as an advantage here, but only a burden. I very much prefer to be able to write if( params.has(name) ) f( params[name], ... ) else f( ... )
-- Daniel Wallin
Gennadiy.

Gennadiy Rozental wrote:
"Daniel Wallin" <dalwan01@student.umu.se> wrote in message news:cnidaq$tim$1@sea.gmane.org...
IMO, a runtime error here is just not acceptable.
I disagree. I don't see compile-time error as an advantage here, but only a burden. I very much prefer to be able to write
I'm sorry but that just doesn't make sense... You are basically saying that, straight C++, if you have... void foo(int arg1, int arg2); And I use it as... foo(1); That C++ should give me a runtime error instead of the compiler doing the syntax checking. Are you serious? -- -- Grafik - Don't Assume Anything -- Redshift Software, Inc. - http://redshift-software.com -- rrivera/acm.org - grafik/redshift-software.com - 102708583/icq

IMO, a runtime error here is just not acceptable.
I disagree. I don't see compile-time error as an advantage here, but only a burden. I very much prefer to be able to write
I'm sorry but that just doesn't make sense... You are basically saying that, straight C++, if you have...
void foo(int arg1, int arg2);
And I use it as...
foo(1);
That C++ should give me a runtime error instead of the compiler doing the syntax checking.
Are you serious?
I am discribing following sotuation: void foo(int arg1, int arg2) {...} void foo(int arg1) {...} template<typename Params> foo1( Params const& p) { if( p.has(asrg2) ) foo( p[arg1], p[arg2] ); else foo( p[arg1] ); } I think it's very convinient to be able to call different overloads of the same function without using any MP tricks. Gennadiy.

On Nov 18, 2004, at 9:38 AM, David Abrahams wrote:
Daniel Wallin <dalwan01@student.umu.se> writes: One possible alternative is
params.defaults_to("unnamed")[name]
but I find that highly illogical to read, because the specification of the default precedes the keyword it's defaulting.
However, Thorsten's prodding has made me wonder if we need || for the lazy case at all. It seems as though
params.has(name) ? params[name] : something_else;
is superior in every way except brevity. And there will be substantial cases where it's briefer as well, because there's no need to build a function object for something_else.
Am I missing something?
"something_else" still has to compile, whereas with "|| something_lazy", "something_lazy()" does not have to compile. Doug

Doug Gregor <dgregor@cs.indiana.edu> writes:
On Nov 18, 2004, at 9:38 AM, David Abrahams wrote:
Daniel Wallin <dalwan01@student.umu.se> writes: One possible alternative is
params.defaults_to("unnamed")[name]
but I find that highly illogical to read, because the specification of the default precedes the keyword it's defaulting.
However, Thorsten's prodding has made me wonder if we need || for the lazy case at all. It seems as though
params.has(name) ? params[name] : something_else;
is superior in every way except brevity. And there will be substantial cases where it's briefer as well, because there's no need to build a function object for something_else.
Am I missing something?
"something_else" still has to compile, whereas with "|| something_lazy", "something_lazy()" does not have to compile.
That too. Okay, I was missing a lot. That's what happens when there's a long gap between initial design and the review. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams wrote:
Doug Gregor <dgregor@cs.indiana.edu> writes:
On Nov 18, 2004, at 9:38 AM, David Abrahams wrote:
Daniel Wallin <dalwan01@student.umu.se> writes: One possible alternative is
params.defaults_to("unnamed")[name]
but I find that highly illogical to read, because the specification of the default precedes the keyword it's defaulting.
However, Thorsten's prodding has made me wonder if we need || for the lazy case at all. It seems as though
params.has(name) ? params[name] : something_else;
is superior in every way except brevity. And there will be substantial cases where it's briefer as well, because there's no need to build a function object for something_else.
Am I missing something?
"something_else" still has to compile, whereas with "|| something_lazy", "something_lazy()" does not have to compile.
That too. Okay, I was missing a lot. That's what happens when there's a long gap between initial design and the review.
Here's another possibility: declare the defaults in the foo_keywords class: struct foo_keywords : boost::keywords< name_t , value_t > { static string default_(name_t) { return "duh"; } static int default_(value_t) { return 123; } }; With this approach, all default extraction is lazy. Admitedly, it's more verbose. However, like in straight c++, the defaults are part of the interface, not the implementation. IMO, foo_impl's body is not a good place to place the defaults. I imagine that in many (all?) cases, you'd want the implementation to be hidden. Yet, doing so will also hide the defaults. With this approach, the defaults can be placed in header files as part of the interface. -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

Joel de Guzman wrote: Re: defaults handling:
Here's another possibility: declare the defaults in the foo_keywords class:
struct foo_keywords : boost::keywords< name_t , value_t > { static string default_(name_t) { return "duh"; } static int default_(value_t) { return 123; } };
With this approach, all default extraction is lazy.
Admitedly, it's more verbose. However, like in straight c++, the defaults are part of the interface, not the implementation. IMO, foo_impl's body is not a good place to place the defaults. I imagine that in many (all?) cases, you'd want the implementation to be hidden. Yet, doing so will also hide the defaults. With this approach, the defaults can be placed in header files as part of the interface.
Hi, Hmmm. I wonder why I got no response. Am I not making sense? I think this solution is doable. You still get a compiler error when a default is not available when unsupplied by the caller and IMO, it is superior because the default handling does not clutter the function implementation. Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

Joel wrote:
Joel de Guzman wrote:
Re: defaults handling:
Here's another possibility: declare the defaults in the foo_keywords class:
struct foo_keywords : boost::keywords< name_t , value_t > { static string default_(name_t) { return "duh"; } static int default_(value_t) { return 123; } };
With this approach, all default extraction is lazy.
Admitedly, it's more verbose. However, like in straight c++, the defaults are part of the interface, not the implementation. IMO, foo_impl's body is not a good place to place the defaults. I imagine that in many (all?) cases, you'd want the implementation to be hidden. Yet, doing so will also hide the defaults. With this approach, the defaults can be placed in header files as part of the interface.
Hi,
Hmmm. I wonder why I got no response. Am I not making sense? I think this solution is doable. You still get a compiler error when a default is not available when unsupplied by the caller and IMO, it is superior because the default handling does not clutter the function implementation.
I agree that it would be nice. But there are several problem with this. First, I think you'd need to explicitly declare the return type of those functions somehow, because the result actually has to pass through operator[]. Also, it's a bit limiting since you don't have access to the argument tuple. For instance: void f(int width, int height = width); Or worse: template<class T> void f(T first, T second = first); For the second one, foo_keywords would definately need a metafunction that can determine the type of the default given the argument tuple. Doing this with the current library is very simple: template<class P> void f_impl(P const& args) { f(args[first], args[second | args[first]]); } -- Daniel Wallin

On Nov 22, 2004, at 12:16 AM, Joel wrote:
Joel de Guzman wrote:
Re: defaults handling:
Here's another possibility: declare the defaults in the foo_keywords class: struct foo_keywords : boost::keywords< name_t , value_t > { static string default_(name_t) { return "duh"; } static int default_(value_t) { return 123; } }; With this approach, all default extraction is lazy. Admitedly, it's more verbose. However, like in straight c++, the defaults are part of the interface, not the implementation. IMO, foo_impl's body is not a good place to place the defaults. I imagine that in many (all?) cases, you'd want the implementation to be hidden. Yet, doing so will also hide the defaults. With this approach, the defaults can be placed in header files as part of the interface.
Hi,
Hmmm. I wonder why I got no response. Am I not making sense? I think this solution is doable. You still get a compiler error when a default is not available when unsupplied by the caller and IMO, it is superior because the default handling does not clutter the function implementation.
But on the other hand, with this method you don't have the ability to make the defaults depend on the other parameters passed to the function. In the BGL, we have lots of dependencies between parameters where we'll build, e.g., a property map based on the vertex index parameter (which may be defaulted) and the value type of a weight map (which may be defaulted). This approach also forces me to name the result type, which can be rather annoying. Doug

Doug Gregor wrote:
On Nov 22, 2004, at 12:16 AM, Joel wrote:
Re: alternative defaults approach.
But on the other hand, with this method you don't have the ability to make the defaults depend on the other parameters passed to the function. In the BGL, we have lots of dependencies between parameters where we'll build, e.g., a property map based on the vertex index parameter (which may be defaulted) and the value type of a weight map (which may be defaulted).
This approach also forces me to name the result type, which can be rather annoying.
I see. I understand now (yours and Daniels replies). I take it that we agree that a declarative interface for defaults handling is better than an imperative interface, but that we are limited by the language. Seems like a challenge to me :-) Thanks! -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

Joel de Guzman wrote:
1) WRT DSL, I'm not quite sure about the syntax for defaults. Example:
params[name | "unnamed"]
I have a strong feeling that there's a better syntax but I am not sure what it is now. I read in the thread that the syntax
params[name] | "unnamed"
is not doable, but I'm not quite sure. Perhaps a clarification will set me straight. Having the default in the brackets seems to rather awkward. Perhaps:
params[name].defaults_to("unnamed")
is better?
FWIW, I like the current syntax.

My review of the named parameters library follows:
- What is your evaluation of the design?
The design is good. The library improves the syntax of named parameters greatly relative to existing solutions, makes it trivial to define new keywords, provides a simple DSL for specifying default parameters, and has some useful shortcuts. I have a few issues with the interface: 1) We need a params.has(k) member, that returns mpl::true_ when the parameter is there and mpl::false_ when the parameter is not there. That lets us dispatch either statically or dynamically based on the existence of an argument for keyword k. 2) The macro BOOST_NAMED_PARAMS_FUN is okay when all parameters may be named parameters. In my use cases, there are always several positional-only parameters for which I do *not* want the option to rename them, and may want to be more picky about how they are passed to the function. For instance, I want to be able to take a "Graph& g" as the first parameter. One way to address this would be another variant of this macro, which I'll lamely call BOOST_NAMED_PARAMS_FUN_EXT, that might work like this: BOOST_NAMED_PARAMS_FUN_EXT(void, page_rank, 0, 3, detail::page_rank_keywords, (2, (typename Graph, typename RankMap)), (2, ((Graph&, g), (RankMap rank)))) The first new argument is a preprocessor array giving the extra template parameters that should be introduced into the function. The second new argument is also a preprocessor array, this one containing the parameter types and names (each as tuples). I believe there is enough information here to generate, e.g., template<typename Graph, typename RankMap> void page_rank(Graph& g, RankMap rank) { return page_rank_with_named_params(g, rank, detail::page_rank_keywords()()); } template<typename Graph, typename RankMap, typename T0> void page_rank(Graph& g, RankMap rank, const T0& a0) { return page_rank_with_named_params(g, rank, detail::page_rank_keywords()(a0)); } etc... 3) Partly due to the missing "params.has(p)", we're lacking the ability to determine the type of an expression like "params[index_map | get(vertex_index, g)]" without passing it to another function. I'd gladly give up some clarity in the handling of default parameters if I could get at this information. I'd even go for an auto-like macro: BOOST_NAMED_PARAMS_AUTO(index, index_map, get(vertex_index, g)); If we had both (2) and (3), it would be feasible to implement algorithms such as those in the graph library without manually introducing several layers of forwarding functions used only to determine types. 4) Using the name "restrict" in a public interface feels like a really bad idea given its status in C99... 5) I haven't yet tried controlling overload resolution, but I know I'll be needing this in the future if the BGL migrates to this library. We've used the same ideas before to integrate the Parallel BGL with the (sequential) BGL, where one takes the same algorithm---breadth_first_search, for instance---and selects the right overload based on the concepts the graph models (in our case, Distributed Graph vs. Graph generally makes the difference).
- What is your evaluation of the implementation?
The implementation is very good. I did not study the details in depth, but the parts I looked at were well-designed. Especially appreciated is the ability to scale up to many more parameters (although I see that there was a patch required for this to work nicely).
- What is your evaluation of the documentation?
The documentation was a little bit lacking. There were a few specific things I would like to see: 1) A "guide for library writers" that helps one organize a library properly for named parameters. For instance, where should we put keywords? I know we should use an anonymous namespace, but should that be in namespace boost? In a "keywords" subnamespace? 2) Keywords are stated to be of type "boost::keyword<keyword type>". Can we derive from the appropriate boost::keyword<...> instead? This is important to the BGL for backward compatibility reasons. 3) Describing that operator, exists would be helpful.
- What is your evaluation of the potential usefulness of the library?
The syntax is much improved over existing solutions. As is, the library is good enough that I would recommend phasing it into the Graph library.
- Did you try to use the library? With what compiler? Did you have any problems?
Yes, Apple GCC 3.3, No.
- How much effort did you put into your evaluation? A glance? A quick reading? In-depth study?
In-depth study. I implemented the PageRank algorithm for the BGL, first with only the Named Parameters library under review and then, later, allowing both the traditional BGL named parameters and the named parameters from this library. I'm confident that the two named parameters mechanisms can coexist within the BGL. The PageRank algorithm, with both named parameters interfaces, is here: http://lists.boost.org/MailArchives/boost/msg75005.php
- Are you knowledgeable about the problem domain?
Yes; I've dealt with the BGL named parameters mechanism for quite a while. Doug

Doug Gregor <dgregor@cs.indiana.edu> writes:
My review of the named parameters library follows:
- What is your evaluation of the design?
The design is good. The library improves the syntax of named parameters greatly relative to existing solutions, makes it trivial to define new keywords, provides a simple DSL for specifying default parameters, and has some useful shortcuts.
I have a few issues with the interface:
1) We need a params.has(k) member, that returns mpl::true_ when the parameter is there and mpl::false_ when the parameter is not there. That lets us dispatch either statically or dynamically based on the existence of an argument for keyword k.
Seems like that interface makes static dispatch unneccessarily hard. How about a metafunction: has_param<Params, k> such that has_param<Params, k> is derived from either mpl::true_ or mpl::false_ and has_param<Params, k>::type is one of mpl::true_ or mpl::false_. ??
2) The macro BOOST_NAMED_PARAMS_FUN is okay when all parameters may be named parameters. In my use cases, there are always several positional-only parameters for which I do *not* want the option to rename them, and may want to be more picky about how they are passed to the function. For instance, I want to be able to take a "Graph& g" as the first parameter. One way to address this would be another variant of this macro, which I'll lamely call BOOST_NAMED_PARAMS_FUN_EXT, that might work like this:
BOOST_NAMED_PARAMS_FUN_EXT(void, page_rank, 0, 3, detail::page_rank_keywords, (2, (typename Graph, typename RankMap)), (2, ((Graph&, g), (RankMap rank))))
The first new argument is a preprocessor array giving the extra template parameters that should be introduced into the function. The second new argument is also a preprocessor array, this one containing the parameter types and names (each as tuples). I believe there is enough information here to generate, e.g.,
template<typename Graph, typename RankMap> void page_rank(Graph& g, RankMap rank) { return page_rank_with_named_params(g, rank, detail::page_rank_keywords()()); }
template<typename Graph, typename RankMap, typename T0> void page_rank(Graph& g, RankMap rank, const T0& a0) { return page_rank_with_named_params(g, rank, detail::page_rank_keywords()(a0)); }
etc...
Probably something like that could work. But my intuition tells me this is a good candidate for vertical repetition: #ifndef BOOST_PP_IS_ITERATING ... # define BOOST_NAMED_PARAMS_SETUP(whatever.hpp, 2, 5) # include BOOST_NAMED_PARAMS_GENERATE #else template <class Graph, class RankMap BOOST_NAMED_PARAMS_TYPES> void page_rank(Graph& g, RankMap rank BOOST_NAMED_PARAMS_ARGS) { return page_rank_with_named_params( g, rank, BOOST_NAMED_PARAMS_KEYWORDS); } #endif Otherwise it's just too ugly and unreadable, not to mention what happens when types contain commas.
3) Partly due to the missing "params.has(p)", we're lacking the ability to determine the type of an expression like "params[index_map | get(vertex_index, g)]" without passing it to another function. I'd gladly give up some clarity in the handling of default parameters if I could get at this information. I'd even go for an auto-like macro:
BOOST_NAMED_PARAMS_AUTO(index, index_map, get(vertex_index, g));
Yikes; that one is hard without some kind of typeof support. How about: BOOST_NAMED_PARAMS_AUTO(index, index_map, some-type-expression); ?? You already have some way to look up the type of get(vertex_index, g) right?
If we had both (2) and (3), it would be feasible to implement algorithms such as those in the graph library without manually introducing several layers of forwarding functions used only to determine types.
4) Using the name "restrict" in a public interface feels like a really bad idea given its status in C99...
Yeah, we should change that.
The documentation was a little bit lacking. There were a few specific things I would like to see:
1) A "guide for library writers" that helps one organize a library properly for named parameters. For instance, where should we put keywords? I know we should use an anonymous namespace, but should that be in namespace boost? In a "keywords" subnamespace?
Okay.
2) Keywords are stated to be of type "boost::keyword<keyword type>". Can we derive from the appropriate boost::keyword<...> instead? This is important to the BGL for backward compatibility reasons.
I honestly don't know. If we can, we should enable it.
3) Describing that operator, exists would be helpful.
Definitely. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

On Nov 23, 2004, at 2:26 PM, David Abrahams wrote:
1) We need a params.has(k) member, that returns mpl::true_ when the parameter is there and mpl::false_ when the parameter is not there. That lets us dispatch either statically or dynamically based on the existence of an argument for keyword k.
Seems like that interface makes static dispatch unneccessarily hard. How about a metafunction:
has_param<Params, k>
such that
has_param<Params, k>
is derived from either mpl::true_ or mpl::false_ and
has_param<Params, k>::type
is one of mpl::true_ or mpl::false_.
Yes, that's better.
2) The macro BOOST_NAMED_PARAMS_FUN is okay when all parameters may be named parameters. In my use cases, there are always several positional-only parameters for which I do *not* want the option to rename them, and may want to be more picky about how they are passed to the function. For instance, I want to be able to take a "Graph& g" as the first parameter. One way to address this would be another variant of this macro, which I'll lamely call BOOST_NAMED_PARAMS_FUN_EXT, that might work like this:
BOOST_NAMED_PARAMS_FUN_EXT(void, page_rank, 0, 3, detail::page_rank_keywords, (2, (typename Graph, typename RankMap)), (2, ((Graph&, g), (RankMap rank))))
The first new argument is a preprocessor array giving the extra template parameters that should be introduced into the function. The second new argument is also a preprocessor array, this one containing the parameter types and names (each as tuples). I believe there is enough information here to generate, e.g.,
template<typename Graph, typename RankMap> void page_rank(Graph& g, RankMap rank) { return page_rank_with_named_params(g, rank, detail::page_rank_keywords()()); }
template<typename Graph, typename RankMap, typename T0> void page_rank(Graph& g, RankMap rank, const T0& a0) { return page_rank_with_named_params(g, rank, detail::page_rank_keywords()(a0)); }
etc...
Probably something like that could work. But my intuition tells me this is a good candidate for vertical repetition:
#ifndef BOOST_PP_IS_ITERATING
...
# define BOOST_NAMED_PARAMS_SETUP(whatever.hpp, 2, 5) # include BOOST_NAMED_PARAMS_GENERATE #else
template <class Graph, class RankMap BOOST_NAMED_PARAMS_TYPES> void page_rank(Graph& g, RankMap rank BOOST_NAMED_PARAMS_ARGS) { return page_rank_with_named_params( g, rank, BOOST_NAMED_PARAMS_KEYWORDS); }
#endif
Otherwise it's just too ugly and unreadable, not to mention what happens when types contain commas.
I'm thinking both are a bit too ugly... the Graph library might just be weird in this regard (having a single non-named parameter at the beginning of every function). Maybe I'll think of something else.
3) Partly due to the missing "params.has(p)", we're lacking the ability to determine the type of an expression like "params[index_map | get(vertex_index, g)]" without passing it to another function. I'd gladly give up some clarity in the handling of default parameters if I could get at this information. I'd even go for an auto-like macro:
BOOST_NAMED_PARAMS_AUTO(index, index_map, get(vertex_index, g));
Yikes; that one is hard without some kind of typeof support.
How about:
BOOST_NAMED_PARAMS_AUTO(index, index_map, some-type-expression);
??
Well, we'd need to have that get(vertex_index, g) in there somewhere for the auto to expand properly. Just having something like typename param_type<Params, index_map_t, default-type>::type would suffice for most uses.
You already have some way to look up the type of
get(vertex_index, g)
right?
Yes.
2) Keywords are stated to be of type "boost::keyword<keyword type>". Can we derive from the appropriate boost::keyword<...> instead? This is important to the BGL for backward compatibility reasons.
I honestly don't know. If we can, we should enable it.
Well, it seems to work on Apple GCC 3.3 :) Doug

Comments:
- Are you knowledgeable about the problem domain?
Very. I've used many languages which included named parameters, I've designed and implemented languages with named parameters, I've seen first-hand the benefits that accrued when named parameters where added to a language, and I've been in numerous discussions with other committee members regarding the need to add named parameters to the C++ language.
- What is your evaluation of the potential usefulness of the library?
It is the first C++ named parameter design that I consider easy enough to use that both designers and users will benefit. Long term, I hope named parameters will be added to the language itself, but short-term this library is a nice step in the right direction. I think the library will be useful in more than enough cases to justify its inclusion in Boost. Named parameters are not useful in every interface design. They always seem to me to be most useful in applications which much cope with a lot of real-world complication. Thus they tend to get used more in industrial applications than computer science applications.
- How much effort did you put into your evaluation? A glance? A quick reading? In-depth study?
An hour of reading and looking at reference material.
As always, please remember to clearly state whether you believe the library should be accepted into Boost.
Yes. --Beman
participants (23)
-
Aaron W. LaFramboise
-
Aleksey Gurtovoy
-
Andy Little
-
Beman Dawes
-
Cromwell Enage
-
Daniel James
-
Daniel Wallin
-
Darryl Green
-
Daryle Walker
-
David Abrahams
-
David B. Held
-
Doug Gregor
-
E. Gladyshev
-
Gennadiy Rozental
-
Joel
-
Joel de Guzman
-
Matt Hurd
-
Noah Stein
-
Paul A Bristow
-
Pavel Vozenilek
-
Peter Dimov
-
Rene Rivera
-
Thorsten Ottosen