
After the great success of Eric's puzzle on the common type of a given types set here it is another puzzle for you C++ wizards. This one arise from the multi signature boost function implementation recently posted to this list. Let's have two boost.function objects, each one defined on a different function signature Sig_1, Sig_2 boost::function<Sig_1> boost_fun_1; boost::function<Sig_2> boost_fun_2; Then have two overloaded template functions called "set" to assign a function/functor to either one of the above boost.function objects template<typename Fun> void set(Fun const& fun, typename enable_if<is_compatible<Fun, Sig_1>
::type* = 0) { boost_fun_1 = fun; }
template<typename Fun> void set(Fun const& fun, typename enable_if<is_compatible<Fun, Sig_2>
::type* = 0) { boost_fun_2 = fun; }
To disambiguate between the two a SFINAE technique is used on the second (hidden) argument of "set" Where struct is_compatible is defined as: /* Check if a function/functor Fun has a given signature Sig */ template<typename Fun, typename Sig> struct is_compatible { /* Check for a function */ template<class U> static yes_type check(typename enable_if<is_same<U, Sig> >::type*); /* Check for a functor */ template<class U, Sig U::*> struct helper; template<class U> static yes_type check(helper<U, &U::operator()>*); /* Default */ template<class U> static no_type check(...); typedef typename boost::remove_pointer<Fun>::type F; static bool const value = (sizeof(check<F>(0)) == sizeof(yes_type)); }; Now the puzzle is: There is a way to remove struct is_compatible altogether ? or at least greatly simplify it ? Constrains are - Public API must be set(fun) where fun is any functor/function for which either expression "boost_fun_1 = fun" or "boost_fun_2 = fun" compiles. - If both "boost_fun_1 = fun" and "boost_fun_2 = fun" are not compilable then a compile error should raise Hint: an almost working solution could have been template<typename Fun> void set(Fun const& fun, bool = (boost::function<Sig_1>() == fun)) { boost_fun_1 = fun; } template<typename Fun> void set(Fun const& fun, bool = (boost::function<Sig_2>() == fun)) { boost_fun_2 = fun; } But unfortunately it is forbidden to refer to an argument (fun in this case) in the expression of the default value of another argument. Have fun Marco

AMDG Marco Costalba wrote:
To disambiguate between the two a SFINAE technique is used on the second (hidden) argument of "set"
Where struct is_compatible is defined as:
/* Check if a function/functor Fun has a given signature Sig */
template<typename Fun, typename Sig> struct is_compatible { /* Check for a function */ template<class U> static yes_type check(typename enable_if<is_same<U, Sig> >::type*);
/* Check for a functor */ template<class U, Sig U::*> struct helper;
template<class U> static yes_type check(helper<U, &U::operator()>*);
This is not correct. It does not handle struct f { void operator()() *const* {} }; Further, it cannot handle implicit conversions of the argument types and return types.
/* Default */ template<class U> static no_type check(...);
typedef typename boost::remove_pointer<Fun>::type F;
static bool const value = (sizeof(check<F>(0)) == sizeof(yes_type)); };
In Christ, Steven Watanabe

On Sun, Apr 27, 2008 at 4:22 PM, Steven Watanabe <watanabesj@gmail.com> wrote:
Further, it cannot handle implicit conversions of the argument types and return types.
What seems is needed is a template is_assignable<boost_fun, fun> that evaluates to true iff an instance of type fun is assignable to a boost::function object of type boost_fun Of course it should work also if fun is a function signature. You have found two shortcomings of struct is_compatible, any solution ? Thanks Marco

AMDG Marco Costalba wrote:
On Sun, Apr 27, 2008 at 4:22 PM, Steven Watanabe <watanabesj@gmail.com> wrote:
Further, it cannot handle implicit conversions of the argument types and return types.
What seems is needed is a template is_assignable<boost_fun, fun> that evaluates to true iff an instance of type fun is assignable to a boost::function object of type boost_fun
Of course it should work also if fun is a function signature.
You have found two shortcomings of struct is_compatible, any solution ?
There's a page in the proto documentation that describes a solution. In Christ, Steven Watanabe

On Mon, Apr 28, 2008 at 6:48 PM, Steven Watanabe <watanabesj@gmail.com> wrote:
There's a page in the proto documentation that describes a solution.
If the page you refer to is: "Detecting the Arity of Function Objects" under the link: http://boost-sandbox.sourceforge.net/libs/proto/doc/html/boost_proto/appendi... Then I have to say: WOW !!!!!! this is metaprogramming virtuosism to the highest level. Thanks for you hint. Marco

on Mon Apr 28 2008, "Marco Costalba" <mcostalba-AT-gmail.com> wrote:
On Mon, Apr 28, 2008 at 6:48 PM, Steven Watanabe <watanabesj@gmail.com> wrote:
There's a page in the proto documentation that describes a solution.
If the page you refer to is: "Detecting the Arity of Function Objects"
under the link:
http://boost-sandbox.sourceforge.net/libs/proto/doc/html/boost_proto/appendi...
Then I have to say: WOW !!!!!! this is metaprogramming virtuosism to the highest level.
Isn't there the issue that passing non-PODs through ... is undefined behavior (note that the standard doesn't say you have to execute the code to cause the undefined behavior, and GCC will warn about it IIRC)? I avoided it for that reason in boost/detail/is_incrementable.hpp. -- Dave Abrahams Boost Consulting http://boost-consulting.com

David Abrahams wrote:
on Mon Apr 28 2008, "Marco Costalba" <mcostalba-AT-gmail.com> wrote:
On Mon, Apr 28, 2008 at 6:48 PM, Steven Watanabe <watanabesj@gmail.com> wrote:
There's a page in the proto documentation that describes a solution.
If the page you refer to is: "Detecting the Arity of Function Objects"
under the link:
http://boost-sandbox.sourceforge.net/libs/proto/doc/html/boost_proto/appendi...
Then I have to say: WOW !!!!!! this is metaprogramming virtuosism to the highest level.
Isn't there the issue that passing non-PODs through ... is undefined behavior (note that the standard doesn't say you have to execute the code to cause the undefined behavior, and GCC will warn about it IIRC)? I avoided it for that reason in boost/detail/is_incrementable.hpp.
If it's in a non-evaluated context (e.g., sizeof expressions), GCC doesn't issue a warning. I think C++03 is silent on this matter, and I remember some discussion of the issue for C++0x, the result of which was that doing this in non-evaluated contexts was OK, but I could be misremembering. -- Eric Niebler Boost Consulting www.boost-consulting.com

On Sun, Apr 27, 2008 at 4:22 PM, Steven Watanabe <watanabesj@gmail.com> wrote:
/* Check if a function/functor Fun has a given signature Sig */
template<typename Fun, typename Sig> struct is_compatible { /* Check for a function */ template<class U> static yes_type check(typename enable_if<is_same<U, Sig> >::type*);
/* Check for a functor */ template<class U, Sig U::*> struct helper;
template<class U> static yes_type check(helper<U, &U::operator()>*);
This is not correct. It does not handle
struct f { void operator()() *const* {} };
Further, it cannot handle implicit conversions of the argument types and return types.
I have thought again on this issue and it come up to the conclusion that this is really a feature :-) In the context of multisignature boost.function the implicit conversion of the argument could lead to misbehaviour. As example define the following signatures: typedef boost::mpl::vector < int(double) , int(int) > Signatures; And define the multi-signature function on them boost::multi_signature_function::function<Signatures> f; Now you have two functions int foo1(double); int foo2(int); Without implicit conversion of type arguments the following works as expected: f = &foo1; f = &foo2; But if implicit conversion is in then, because int(double) is declared BEFORE int(int) then the first will shadow the second in the overload hierarchy and what we end up is that f = &foo2; overwrites the previous assignment so that the internal boost::function corresponding to int(double) is assigned to &foo2, while the other boost::function corresponding to int(int) is still empty. So the bottom line is that implicit conversion of the argument types is not good in our context. What the user should do is to wrap the function in a boost function BEFORE to assign to multi-signature. As example int foo(int); boost::multi_signature_function::function<int(double)> f; boost::function<int(double)> wrapper; wrapper = &foo; // now it works because there is no ambiguity for a standard boost::function... f = wrapper; // ... and it's assigned to the multi-signature f also if foo has different signature. Thanks Marco P.S: So now the only remaining issue (the other was promoted to a feature!) is the handling of pointers to const member functions. BTW note that the wrapping trick would work also to handle this case, but in this case a 'native' solution would be preferable.
participants (4)
-
David Abrahams
-
Eric Niebler
-
Marco Costalba
-
Steven Watanabe