Boost logo

Boost :

From: Gennadiy Rozental (gennadiy.rozental_at_[hidden])
Date: 2004-11-21 19:37:48


> > What overloads are you trying to resolve here:
> >
> > template<class A0>
> > void foo(
> > const A0& a0
> > , foo_keywords::restrict<A0>::type x = foo_keywords()
> > )
> > {
> > foo_impl(x(a0));
> > }
> >
> > template<class A0, class A1>
> > void foo(
> > const A0& a0, const A1& a1
> > , foo_keywords::restrict<A0,A1>::type x = foo_keywords()
> > )
> > {
> > foo_impl(x(a0, a1));
> > }
>
> There are no more overloads here. But foo is only enabled when the
> "name" parameter is convertible to char const*:

Wait, this is an exact copy of an example you have in "controlling overload
resolution" section. Any why two functions then?
So can you gove me an example of "controlling overload resolution" feature?

> struct foo_keywords
> : boost::keywords<
> boost::named_param<
> name_t
> , mpl::false_
> , is_convertible<mpl::_, const char*>
> >
> , value_t
> >
> {};
>
> > As for type restriction I am using typed keywords.
>
> But that's not enough! It's too naive. What if you want a function
> overload:
>
> template<class Value>
> void f(Value);
>
> Where Value is restricted to types that are Movable, detectable with
> is_movable<>.
>
> How would you do this?

First of all this is comparatevly rarely needed. Accordingly I do not see a
reasons to supply library solution for this problem. Whould I ever need
something like this, I would do:

template<typename Value>
enable_if<Value is movable,void>
foo_impl( Value const&, std::string name, Mymode mode )
{
}

template<typename Params>
void foo(Params const& p)
{
   foo_impl( p[value], p[name], p[mode] );
}

value above is typelees keyword in my terminology. And no need to mix
everything into one bowl.

> And again, the restrictions should **NOT** be part of the keywords.
> Keywords are naturally reusable components that can be shared by
> multiple functions. This has been pointed out several times in these
> discussions.

It depends IMO. I personally in most cases prefer keyword to be typed. My
solution also support typeless keywords if you like so. See above for the
possible way to implement a restrictions.

> > I use C++ (surprise, surprise) a lot. And I do not believe that absence
of
> > optional function parameter is compile time error.
>
> It's not optional, it's required. That's the whole point. Did you
> consider Rene's post?

Even though I don't see as a major advantage (you as a programmer will
detect and fix this issue in any case, would it be compile time or runtime
error), my latest attempt does produce a compile time error in case of
missing required parameter (while it does not do that in case of optional)

> >>I would use the normal way of disabling function template overloads;
> >>SFINAE.
> >
> >
> > Ok. Here is an example:
> >
> > void foo_impl( int i1, int i2 ) {...}
> > void foo_impl( int i1 ) {...}
> >
> > template<typename Params>
> > void foo( Params const& p ) {
> > if( p.has(i2) )
> > foo_impl( p[i1], p[i2] );
> > else
> > foo_impl( p[i1] );
> > }
> >
> > Above is my solution to the resolving overloads based on presence of
> > optional named function parameter. Could you type below how would yours
look
> > like?
>
> Actually, we can't do that with the overload restrictions. I guess we
> could support it, and maybe we should..
>
> Anyway, the code you posted is valid with our library as well, if that
> you suppress the compilation error by supplying a default value for i2:

[solution skipped]

This is not exactly the same thing, and clear source of confusion.

> This solution is obviously flawed though, since in general you can't be
> assumed to be able to change foo() to account for every possible
> overload.

Ok. So what is your solution to the problem of supplying single named
parameter based interface for dynamically sized series of functions with
different actual number of arguments?

> >>>int v = params.has(a) ? params[s] : 8;
> >>>Then any other compile time interface you would invent.
> >>
> >>I'm assuming you mistyped that, in which case it's spelled:
> >
> >
> > No, it's exactly like it supposed to be
>
> If the user has supplied parameter "a", set v to parameter s, otherwise
> set it to 8?

Yes. It should be the same keyword of course.

> >> int v = params[a | 8];
> >>
> >>With our library.
> >
> >
> > And now compare: what is more obvious and easier to grasp?
>
> Your version is easier to grasp until you have learned the DSL.

> But the
> cost of it is that your code won't work in a lot of cases. For instance,
> the default value expression always has to be compiled.

Why is that? Why Couldn't I do the same as what you are doing?

int v = params.has(a) ? params[a] : get_a_default_value();

It's even easier since I do not need to wrap it into bind call.

> And of course,
> as said before, missing a required parameter will cause a runtime error.

Compile time error

> >>>3. Separation if implementation and invocation.
> >>>The fact that you want to detect access to missing parameter at compile
> >
> > time
> >
> >>>assumes that during compiling if function invocation you have an access
> >
> > to
> >
> >>>function implementation.
> >>
> >>No of course it doesn't assume that. The keyword parameters is part of
> >>the function *interface*, not the implementation.
> >
> >
> > The complete list of them - yes. But which one of them are optional,
> > required or conditionally required is completely in function
implementation
> > domain.
>
> Really? Is the parameter defaults also an implementation detail? As a
> user, how am I suppose to use your functions if I don't know about these
> things?

Yes. I inclined to think about function parameter default values more in
terms of implementation details rather than part of interface. You as a
function user shouldn't based your decisions on the default value for the
parameter: you have value for this parameter - you supply it. You don't -
function deals with it itself. This is especially true since in many , many
cases you just couldn't put default value into function declaration:

int foo( int a, int b = a ) {...}

What we do instead is somehow mimic similar behavior:

int foo( int a, int b = -1 ) {
    if( b = -1 )
        b = a;
}

So what default value -1 gives you as a function caller?

> > How are you supposed to now from 'select' function interface that
timeout
> > parameter is required iff mode parameter is WAIT? Accordingly in a point
of
> > select function invocation:
> >
> > select(( mode =WAIT,priority=2,prefer_output=true));
> >
> > You have no way to know that timeout is missing, while in following call
> >
> > select(( mode =POOL,priority=0));
> >
> > is not.
>
> Why? It's a simple precondition that might even be enforced at compile
> time with our library if you want to.

How? I would like to see it. I would like to see you to produce compile time
error based on runtime value of the variable. And let then compare it with
how my solution would look like:

int select( Params const& p ) {
      switch( p[mode] ) {
      case WAIT:
            return select_impl( p[priority], p[prefer_output], p[timeout]);
      case BLOCK:
            return select_impl( p[priority], p[prefer_output] );
      case POOL:
            return select_impl( p[priority], p[prefer_output], TimeValue(
0,0 ) );
      }
}

> Daniel Wallin

Gennadiy


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