
Gennadiy Rozental wrote:
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?
I'm sorry, I don't know what that means.
So can you gove me an example of "controlling overload resolution" feature?
I just did. foo is enabled only when 'name' is convertible to char const*.
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.
So we shouldn't care about this use case? Why? To reduce the number of lines of code in the implementation?
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.
But again, you don't generally have the ability to have a universal foo() overload that handles the dispatching. You want one for every overload.
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.
Really? What's the difference?
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?
I would add something to the library to control overload resolution for this case with SFINAE.
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.
No, because get_a_default_value() has to compile. Our defaults can be lazily evaluated, and only compiled when needed.
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?
The value of 'a'? What you were arguing here however is that it shouldn't even be part of the interface whether 'b' has a default value or is a required parameter.
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.
Of course I can't produce compile time errors based on runtime values, but who said anything about mode being a runtime value? The modes could just as well be tag types here, in which case it's trivial. template<class P> int select_impl(wait_t, P const& args) .. template<class P> int select_impl(block_t, P const& args) .. template<class P> int select_impl(pool_t, P const& args) .. template<class P> int select(P const& args) { return select_impl(args[mode], args); } -- Daniel Wallin