Boost logo

Boost :

From: Daniel Wallin (dalwan01_at_[hidden])
Date: 2004-11-21 17:45:39


Gennadiy Rozental wrote:
>>>Since you as usual did not provide an example, I may only guess that by
>>>"SFINAE support" you mean the way to implement kind of type checking. In
>
> my
>
>>>case I do not need that, cause I would be using typed keyword instead.
>>
>>Just read the documentation.
>
>
> I did. And it does not make too much sense to me. From what I gather you are
> using it to control overload resolution and /or to restrict types of a
> parameter.

Right.

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

   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?

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.

>>>>and the fatal design mistake of
>>>>treating missing required arguments as runtime errors.
>>>
>>>
>>>Well, I believe that compile time errors are at least an inconvenience
>
> and
>
>>>at most "fatal design mistake". Here is couple reasons why (you 'forgot'
>
> to
>
>>>mention any reasons for you opinion, but still)
>>
>>Yeah well, the reasons should be obvious for anyone that uses C++. As
>>Rene has already pointed out in another thread.
>
>
> 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?

>>>1. Named parameter interface is assumes optional parameter in most
>
> cases.
>
>>>IOW we have function with variable optional number of parameters. Fact
>
> that
>
>>>one is not present is not an error, but a valid call. Now lets look how
>>>would you implement this function:
>>>
>>>template<Params>
>>>foo( Params const& p ) {
>>>
>>> Here you either use MP if or regular if to switch to appropriate
>>>implementation. I find regular if *much* more convenient and easier to
>>>understand and implement
>>
>>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:

   template<typename Params>
   void foo( Params const& p ) {
       if(has(p, i2))
           foo_impl(p[i1], p[i2 | 0]);
       else
           foo_impl(p[i1]);
   }

has() isn't part of the library:

   struct nil_ {};

   bool has_impl(nil_)
   {
       return false;
   }

   template<class T>
   bool has_impl(T const&)
   {
       return true;
   }

   template<class P, class T>
   bool has(P const& args, T const& tag)
   {
       return has_impl(args[tag | nil_()]);
   }

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.

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

>> 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. And of course,
as said before, missing a required parameter will cause a runtime 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?

>>>IMO this is bad assumption in general and may not
>>>be the case in a future (with different implementation if templates).
>
> Don't
>
>>>take me wrong there some things we do want to check at compile time. But
>>>here we talking abut function implementation details, which caller do
>
> not
>
>>>need to know.
>>
>>No, we are talking about the function interface, which the caller
>>most likely should know about.
>
>
> 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(( more =WAIT,priority=2,prefer_output=true));
>
> You have no way to know that timeout is missing, while in following call
>
> select(( more =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.

>>>>I'm done in this thread now.
>>
>>.. I couldn't restrain myself.
>
>
> I like this practice: during review just ignore all negative reviewers,
> hopefully people would missed it. Sorry, couldn't restrain myself.

I have read and considered everything you have written.

-- 
Daniel Wallin

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