Boost logo

Boost :

From: Douglas Gregor (gregod_at_[hidden])
Date: 2002-05-12 22:48:38


Boost.Function could _greatly_ benefit from an is_callable type trait.
is_callable would have the following signature:

template<typename Functor, typename R, typename T1, ..., typename TN>
struct is_callable;

If an instance of Functor is callable with arguments of types T1, T2, ...,
TN, then this type trait will return "true".

is_callable is easy for (member) function pointers (if you ignore default
arguments, which is acceptable because they are lost when you create the
pointer), but it is quite hard for arbitrary function objects. I've come up
with a partial solution, based on Mat Marcus's post about is_addable (that
solution also involved Dietmar Kuehl, Jaako Jarvi, and Jeremy Siek). This
solution works for arbitrary function objects such that the best operator():
  1) Is not private
  2) Does not return void (ouch!)
  3) Exists (this can be worked around using the is_member idiom)

I'm hoping this will stimulate more ideas. The eventual goal of is_callable
is to make it possible for Boost.Function objects to only be implicitly
constructed from objects that it can call. This would be a great win from the
usability standpoint (much nicer error messages in most cases), but also
allow overloading of functions that take Boost.Function arguments. Finally,
this would put us one step closer to being able to do concept checking
_without_ throwing errors.

Here is the partial solution:

---------------------------------------------------------------------------
  template<typename T> T value_of_type(type<T>);

  // Assumed to be bigger than any returned type
  struct large_type {
    char big[65535];
  };

  // Allows a fallback to be matched, but in the least desireable way
  struct fallback
  {
    template<typename T> fallback(const T&) {}
  };

  // Fallback matching a two-argument function call
  struct fallback2
  {
    large_type operator()(const fallback&, const fallback&) const;
  };

  // Pull operator()'s from both the actual function object and the
  // fallback
  template<typename F, typename Fallback>
  struct is_functor_callable_helper : public F, public Fallback
  {
    using F::operator();
    using Fallback::operator();
  };

  template<typename F, typename R, typename T1, typename T2>
  class is_functor_callable2
  {
    typedef is_functor_callable_helper<F, fallback2> checker;

  public:
    // Try to call the function object 'checker' that merges operator() from
    // the given function object F and the fallback. The 'checker' function
    // object is called with arguments of the appropriate type. If the best
    // candidate is the fallback we created, the result type will be
    // large_type, whose size is unique. However, if any other operator()
    // exists that matches, the return type will not be large_type (and,
    // by assumption, will not have the same size), so we can return true.
    BOOST_STATIC_CONSTANT(bool,
       value = (sizeof(large_type) !=
                sizeof((*static_cast<checker*>(0))(value_of_type(type<T1>()),
                                                value_of_type(type<T2>())))));
  };
---------------------------------------------------------------------------

I have one alternative approach in mind, which is to simply use the
has_member idiom to check if operator() exists, and then check
&Functor::operator() similarly to any other member function pointer. This
solves the problem with function objects returning void, but it introduces a
new problem: default arguments aren't handled for member function pointers,
so they can't be used.

Any ideas?

        Doug


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