Boost logo

Boost :

From: David Abrahams (dave_at_[hidden])
Date: 2004-11-16 08:09:49


"Gennadiy Rozental" <gennadiy.rozental_at_[hidden]> writes:

> An "active" solutions automatically place parameters in appropriate slots by
> single 'apply' call:
>
> class A {
> public:
> template<typename Params>
> A( Params const& p ) { p.apply( *this ); }
> };
>
> Even though "active" solutions in some cases provide an interface look more
> natural. I found that "passive" solution in most cases more flexible and
> easier to implement. While in both cases one may employ runtime
> polymorphism, with "passive interfaces it's more natural. Note that
> parameter may not necessary is just a simple value, it maybe some kind of
> optional feature and 'apply' method maybe not so trivial and even invoke
> some functions. All in all both design seems to have heir advantages and I
> believe we need to provide support for both flavors.

Hmm. AFAICT from your example an "active" solution requires a
custom-built Params class that knows how to "apply" itself (in this
case to class A). Because of that, I can't imagine what it could mean
to "provide support" for an active approach. It doesn't seem like
something a library can help with. Also, it seems as though the
elements of the parameter infrastructure (e.g. a keyword class) will
become special-purpose for the particular "apply" it needs to be able
to perform, rather than reusable for several functions as in our
approach. Am I missing something?

> II Interfaces
>
> a) Call interface and parameters combining
>
> IMO with some very rare exclusions (1/2 optional parameter) the named
> parameter framework shouldn't require/expect multiple overloading of target
> function with different number of parameters. As well as any "impl"
> functions. The only acceptable interface is the one with single Params
> template parameters (in some cases one may provide positional interface as
> well, though it's rather exclusion case). This is applicable both to active
> and passive solutions:
>
> class A {
> A() : ... {}
>
> template<typename Params>
> A( Params const& p ) : m_name( p[name] ), m_gulp( p[gulp] ) {}
> };

Of course our library allows that usage. You can look on the
"multiple overloading" case as though it was generated with varargs
templates, which I hope we're getting in an upcoming version of C++.
Hmm, we'd better make sure that feature can handle this case. ;-)

> This immediately bring a need for automatic arguments combining.
> There are several ways to do so, depending on requirements (BWT I
> liked interface presented in some other post with using ',' as
> combine operator).

That's what our library does.

> b) Optional parameter support
>
> In case of active solutions there should be an interface to check
> presence of specific argument. Submitted solution will cause compile
> time error on access to non-supplied parameter. This may not be
> always most convenient and may not be the case at all with different
> implementation (runtime based)

It's easy enough to check, actually.

     struct nil_t {} nil;
     template <class T> bool is_nil(T&) { return false; }
     bool is_nil(nil_t) { return true; }
     
now

     is_nil(p[name | nil])

will tell you if "name" was supplied. I guess we could build this
into the library (with the corresponding compile-time check, of
course).

> c) Default value support
>
> I specifically dislike an interfaces provided to support default values.
> They are unreasonably cryptic (who would remember difference between | and
> ||; and why | )

That's pretty funny; it's meant to be memnonic.

We use | because it means "or," as in

   either get the name OR if it's not supplied, use this value

We use || to indicate lazy evaluation because in C++, the 2nd argument
to || isn't evaluated unless the first one is supplied.

   either get the name OR if it's not supplied, evaluate this
   function and use the result.

> but also misleading since default value is actually with
> access brackets:

I can't parse that. In what sense is the default value "with access
brackets?" Do you mean it's "accessed with brackets?"

Not only the default, but also the passed value is accessed with
brackets. Why is it misleading?

> what is p["abc"]?

Meaningless. Compiler error. Why should it have a meaning?

> There are several alternatives. My personal preference is :
>
> int name_ = p.is_present<name> ? p[name] : "abc"; // you will need some MP
> trick to make it compilable
> or
> int name_ = p.is_present( name ) ? p[value] : "abc"; // this assumes
                                       ^^^^^
What's the significance of that?-------^^^^^
> runtime based implementation
>
> Active solutions doesn't have such issue. Whatever is there gets set
> by apply call.

Sure, but I don't see how a library can help you with it yet.

> d) Type safety and restrictions
>
> I do not understand why wouldn't we keep data type somewhere around
> keyword type and require one to supply specific type during
> invocation.

Because we looked at the use cases; the major example we could find
was the Boost.Graph library, which is a library of function
templates. We thought it would be bad if the library didn't work
for function templates.

> This library instead invented special mechanism of imposing
> restrictions.

Not really; it used a de-facto standard approach for passing
compile-time functions.

> I do not believe we need that

Disagree.

> (Also my guess is that 8 out of 10 developers unfamiliar with
> library will be left spellbound facing this code)

Possibly so.

> e) Mix of named and positional parameters
>
> I don't believe it's actually a good idea to support such mix. IMO it just
> adding unnecessary confusion and possible source of users errors.. Let's say
> I expect 2 parameters: 1. string name 2. int value. call foo( value = 5,
> "my_name" ) will fail, while foo( "my_name", value = 5 ) and foo( value =
> 5 ) won't. Too much chances to make mistake IMO.

Python and I'm sure quite a few others do it that way; experience has
shown that it causes no confusion.

> III. Implementation
>
> IMO whatever design you prefer implementation shouldn't as complex and
> cumbersome. Named parameters support is pretty simple task. This submission
> presents 1000 lines of tight MP code (and this is without numerous
> includes). In majority of the cases much more simple and straightforward
> solution is possible (with some acceptable IMO tradeoff). Following 80 lines
> (I think it's possible to make it even smaller would I use mpl) should
> basically present solution with similar tradeoff as submitted library
> (without default value support, but IMO it would take another 40-60 lines,
> just save it into keyword struct):

and without the ability to control overload resolution, or to wrap
function templates, and with the problem of keyword coupling (because
of the integer indices). It's no fair claiming the solution should be
simpler until you actually implement it. Toy examples don't count.

> Strict type checking on top of it.

You say that like it's a _good_ thing.

> This code works under gcc 3.4.2. Should work everywhere.

Hah! I'll believe that when you've ported it ;-)

> As you may guess by now my vote is NO to accept this library. I
> personally wouldn't be using it in current form and IMO as it stands
> now it's just another collection of neat MP tricks.

It's really _just_ another collection of neat MP tricks? You mean
it's not even useful? Or what, exactly?

-- 
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk