From: Marco Costalba (mcostalba_at_[hidden])
Date: 2008-04-27 08:09:26


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