
Richard Smith wrote:
Tobias Schwinger wrote:
Further: none of the reviewers (except Doug Gregor, who was talking about the issue in the very specific context of merger with another library) did answer the following question:
What's wrong with the tag types (other than personal taste) in the first place?
Having just read your other email, I think I now better understand why you want tags -- and why you don't want to go the traits route that I suggested.
What I don't like about the current interface is that it seems to use an odd mixture of traits and tags. Let me try to explain.
The library is supplying an interface to manipulate function types (and function pointer, function reference and member function pointer types). The two most obvious parts of a function type are the return type and the parameter types. These are queried via traits:
result_type< Fn >::type mpl::at_c< parameter_types< Fn >, N >::type
However, on top of this, there are other aspects: calling convention and variadic-ness (both of which are probably relatively rarely required), and cv-quals for member functions (which might be more frequently needed).
Yeah. But cv-qualification for member functions is usually dealt with in form of a qualified class type. The tags are just needed to overide the default -- and for cv-qualified function types (which by the current standard make a program ill-formed).
These are all handled by the tags mechanism.
is_callable_builtin< Fn, variadic >::value
(I'm assuming that is_callable_builtin returns true for functions, references, pointers and members.)
Finally, there's exactly what sort of function type we're dealing with -- function, reference, pointer or member. These are handled by separate traits:
is_function_reference< Fn >::value is_member_function_pointer< Fn >::value
That's three (or four if you include the at_c usage to get the nth parameter, but let's not go there again) subtly different interfaces. I'm not concerned about the difference between ::type and ::value -- as I can put ::type in both cases and get a true_type or false_type.
But that still leaves two different interfaces, which IMO is one too many. My traits suggestion removed one interface by ditching the tags, but it wasn't the tag interface that I disliked /per se/ -- it was the inconsistency.
What if you went the other way and used an exclusively tag-based interface:
namespace bft = boost::function_types;
bft::get< int (), result_type >::type // int
bft::get< int (), parameter_types >::type // an empty MPL sequence
bft::get< void (int*, float), parameter_types >::type // an MPL sequence containing (int*, float)
bft::get< void (int*, float), parameter_type<0> >::type // int* -- optional syntactic sugar
bft::get< void (...), is_variadic >::type // true_type
bft::get< void (...), is_variadic >::value // true
bft::get< void (), is_default_cc >::type // true_type
bft::get< void (*)(), is_pointer >::type // true_type
etc... This is not very dissimilar to the existing is_callable_scalar metafunction, but with addition is_pointer, is_reference, etc... tags folded in.
I've been there. It's very close to the interface of the previous version. The descisions towards the current design and against consistency were a matter of practicability: <cite from yesterday's post> I came to the conclusion that simple and straightforward client code is more important -- and it really did cost me quite an effort to let go of the nagging perfectionism about consistency for consistency's sake. </cite from yesterday's post> Regards, Tobias