Boost logo

Boost :

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


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

> "David Abrahams" <dave_at_[hidden]> wrote in message
> news:uk6smkkiq.fsf_at_boost-consulting.com...
>>
>> 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?
>
> There is enough (like parameter combining anf interfaces and keywords) that
> could be reused in a library.

Sure. It's just the "active" part that the library can't support.
You can always use a passive approach and then do the extraction
using the library primitives inside the "apply" function. After all,
why not make "apply" a free function?

    template <class Params>
    void apply(Params& p, Whatever x) {
         // use p's "passive" interface to operate on x
     }

>> > 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.
>
> Hmm. Well, I did not find it nor in docs. Why IMO it's most
> important part of functionality. I see it now in test. Why did you
> name function f_list? Why not just f as rest of them?

Where are you seeing f_list?

>> 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).
>
> It could be usefull.

Yes.

>> > 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?"
>
> I meant "within brackets". My problem is that it's p[name | "abc"] while I
> would prefer something like p[name] | "abc".

That's not doable in general. Suppose the argument was an int? What
is

        p[index] | 3

??

> Or even better something like a nexplicit interface below.

>
>> > 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?-------^^^^^
>
> Could you rephrase the question?

You used value instead of name in the 2nd example.

> It's just an interface I would prefer.

If we provide the interface you requested, to query whether a value
is supplied, then you could have your "explicit interface" too. It's
a little verbose for my tastes, but to each his own I guess.

>> > 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.
>
> It could. I did that.

Please show how the library can help with the "active" part. The
passive library can be used to build an active component easily
enough. I just don't see how the library can help with the apply
call.

>> 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.
>
> Sorry I am not familier with that example. Could you please give more
> details on what exactly is wrong wit hstrict type checking?

If you have a templated function parameter that you want to be able to
name, you can't require that it is one particular type.

  template <class T>
  void f(T x); // what strict type is T?

Now, you *can* leave it completely unchecked, but that's often
overly general and can lead to overload ambiguity/conflicts. Our
mechanism allows you to restrict the types of arguments. It means
that in the trivial case where the argument has to be a particular
type, you write

      is_same<_, SomeType>

instead of

      SomeType

A small price to pay.

>> > This library instead invented special mechanism of imposing
>> > restrictions.
>>
>> Not really; it used a de-facto standard approach for passing
>> compile-time functions.
>
> Yeah. But in C++ de-facto standard approach for enforcing parameters
> types is to specify one.

Unless you're writing a function template.

>> > 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.
>
> I do not know about python, but in c++ I prefer named interface
> wouldn't break by switching order of parameter specification.

What do you mean by "break?" When parameter names are used there is
no restriction on parameter order.

f(arg1, arg2, arg3, key5 = arg5, key6 = arg6, key4 = arg4)
  ^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  positional position-independent.

>> > 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,
>
> What do you mean here? With an example.
>
>> or to wrap function templates
>
> Or this?

These two are related; it should be clear from what I've written
above. Your code can allow at most one of these (I didn't check):
either it allows unchecked types and there's no ability to control
overload resolution, or it doesn't support function templates with
keyword arguments. And unless you have some way to induce SFINAE,
you have no way to distinguish multiple overloads of the same
function name accepting different named parameter sets:

  int foo1( c_str name, float v, int i )
  {
  }

  int foo2( int id )
  {
  }

  template<class Params>
  int foo(Params const& params)
  {
      foo1( params[name], params[value], params[index] );
  }

  template<class Params>
  int foo(Params const& params) // error, foo already defined.
  {
      foo2( params[id] );
  }

>> and with the problem of keyword coupling (because of the integer indices).
>
> What do you mean by coupling? That we need to supply unique integer
> id?

Yes. You can't develop keywords independently.

> I do not see it as a major drawback. And it's definetly justified by
> the simplicity of the solution.

I prefer to have a little more complexity in the library to remove
problems for the user.

>> It's no fair claiming the solution should be simpler until you
>> actually implement it. Toy examples don't count.
>
> I did. And I do not believe it's toy example (at least no more that
> submitted library). It does everything I need from named parameters,
> easy to use and does not cost a lot.

Fair enough.

>> > Strict type checking on top of it.
>>
>> You say that like it's a _good_ thing.
>
> Yes. I do believe strict type checking is good thing for both
> positional and named function parameters.

But it kills function templates. Function templates don't just
accept a single type.

>> > This code works under gcc 3.4.2. Should work everywhere.
>>
>> Hah! I'll believe that when you've ported it ;-)
>
> I did not use any advanced features (I don't think even PS). I did
> ported similar solutions on numerous compilers.

Yeah, but you don't need PS to cause ETI and other issues on VC <= 7.0

>> > 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?
>
> I wouldn't be using it. Why would I use canon to kill sparrow.

Why would anyone kill a sparrow?!! ;-)

-- 
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