Boost logo

Boost :

From: Jaakko Jarvi (jajarvi_at_[hidden])
Date: 2002-02-26 11:10:20


> If you are allowed to wrap the functions, the solution is to use a
> polymorphic function object:
>
> struct foo
> {
> int operator()(int x) const { return 2 * x; }
> double operator()(double x) const { return 3.0 * x; }
> };
>
> _but_ this 'foo' won't work with the short form of boost::bind because its
> return type cannot be deduced automatically (no typeof!).

The lambda library does have a way to define a function object, which
overloads the operator() and still defines the return types correctly.
The foo struct needs to define a traits template that maps the
function argument types to the return type.
Smaragdakis and McNamara are using this technique in their FC++ library.

With this technique, foo would look like:

struct foo : public has_sig
{

  template<class Arg>
  struct sig {
    typedef typename boost::tuples::element<1>::type arg_type;
    typedef typename boost::remove_reference<arg_type>::type arg_type1;

    typedef typename boost::remove_cv<arg_type>::type arg_type2;

    typedef typename boost::IF<
      boost::is_integral<arg_type2>::value,
      int,
      typename boost::IF<
        boost::is_float<arg_type2>::value,
        double,
        some_error_type
>::RET,
>::RET type;
  
  };
  
  int operator()(int x) const { return 2 * x; }
  double operator()(double x) const { return 3.0 * x; }
};

The explanation:

sig<Args>::type implements a mapping from a tuple type Args to the
return type of a call to the function object.
The element types of the tuple type Args are the types of the arguments.
The first element (at index 0) is the type of the function object itself
(foo in this case). All types in Args can have cv-qualifiers and are
references.
Note further, that for techincal reasons foo has to inherit from the class
has_sig.

So the above case, the sig template exampines the second element of the
Args tuple, that is the first (and only) argument to the operator().
If it is an integral type, overload resolution will select
int operator()(int x) and the return type will be int.
If it is a floating point type, overload resolution will select
double operator()(double x) and the return type will be double.
If it is something else, we return an error.

Note that this is not perfect, implicit conversions make things
complicated. It should be possible to take these into considerations as
well, if the is_convertible template is used.
The above should be enough for demonstrating the technique.

Providing such a mapping may be a bit tedious, but nevertheless doable.

Cheers, Jaakko


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