Boost logo

Boost :

From: Daniel Walker (daniel.j.walker_at_[hidden])
Date: 2008-05-28 11:37:04


Hello,

Following up on several discussion over the last few weeks, I've
placed an implementation of a polymorphic call wrapper (and associated
utilities) similar to Boost.Function in the file
polymorphic_function.zip in the Function Objects directory on vault at
http://tinyurl.com/56zvo4.

Here's a brief demonstration to give an idea of what's included:

// Work with arbitrary callable objects without loss of polymorphism.
template<class Signature>
void do_division(polymorphic_function<Signature> f)
{
    // Do floating point division.
    float one = 1, two = 2;
    assert(f(one, two) == 0.5);

    if(is_polymorphic_function<Signature>::value) {
        // Now, drop the remainder using integer division.
        int one = 1, two = 2;
        assert(f(one, two) == 0);

        // And now, use high precision floating point division to
        // compute the inverse product of a series of integers.
        int series[3] = { 1, 2, 3 };
        double x = std::accumulate(&series[0], &series[3], 1.0,
                        functional_cast<double(double,double)>(f));
        assert(0.16666666 < x && x < 0.166666667);
    }
}

// A second class builtin function.
float builtin_divide(float x, float y)
{
    return x / y;
}

// A TR1 result_of compatible function object.
struct divide {
    template<class Signature>
    struct result : argument_of<Signature, 0> {
    };

    template<class T>
    T operator()(T x, T y) const
    {
        return x / y;
    }
};

int main()
{
    // Promote builtin functions to first class.
    do_division< float(float,float) >( builtin_divide );

    // Perform type-erasure.
    do_division< float(float,float) >( std::divides<float>() );

    // Use polymorphic function objects polymorphically.
    do_division< divide(_1,_1) >( divide() );
}

The file polymorphic_function.hpp provides the following components:

* signature/call_signature/erase are metafunctions used to describe
polymorphic functions. They allow you to do things like erase the
callable object type of a boost::result_of compatible signature so it
can be used with boost::function.

typedef signature<my_function(int)>::type signature_type;
typedef result_of<signature_type>::type result_type;
typedef function<erase<signature_type>::type> function_object_type;

* is_polymorphic_function is a metafunction used to designate function
prototypes that may have variable return types according to
boost::result_of. These are a subset of the prototypes detected by
boost::is_function. More pertinently, is_polymorphic_function
distinguishes the boost::function protocol for specifying function
signatures - a.k.a. call signatures - from the boost::result_of
protocol - a.k.a. polymorphic signatures.

* is_incomplete_function is a metafunction used to designate
polymorphic functions with variable argument types. These are a subset
of the prototypes detected by is_polymorphic_function. They are
described using Boost.MPL placeholders as arguments in the signature.

* argument_of is a metafunction used to deduce the types of arguments
to functions. It's a more general, nary alternative to the C++98
argument_type member. For example, argument_of can be used to detect
call-by-reference functions:

template<class F>
void g(F f)
{
    int x = 0;
    int& y = f(x);
    if(is_reference<typename result_of<F(int)>::type>::value &&
       is_reference<typename argument_of<F(int), 0>::type>::value)
        assert(&x == &y);
}

int& my_function(int& t) { return t; }
int main() { g(my_function); }

* polymorphic_function is a call wrapper, which can be used in place
of boost::function to allow both builtin functions and function
objects to be used interchangeably without the degradation of
polymorphism incurred by wrapping polymorphic function objects in
boost::function. Essentially, this is achieved by giving the user the
option of either erasing the function object's type as one would with
boost::function or retaining the function object's type as one would
with boost::result_of.

* functional_cast is used to change the signature of a call wrapper.
It can be used with both std/boost::function and polymorphic_function.
For example, by specifying a new signature one may change the arity or
argument types of a call wrapper, so long as the underlying callable
object can be described by the new signature.

Also included is a test set. I tested on gcc 3.4, 4.3 and msvc 9. The
code includes some documentation in the form of inline comments.

As far as future work and known defects, this is the first time I have
seriously played with C++0x forwarding conventions using std::forward
and rvalue references. I'm not confident that I got it perfectly
right, so any corrections/guidance would be greatly appreciated. In
C++98, I use an argument forwarding technique similar to Boost.Bind
and Boost.Lambda - arguments are taken as non-const references and
forwarded as rvalue conversions of the types requested in the
signature. Secondly, type deduction using argument_of could have
better support for function objects in C++98 and currently only
supports member template operator() in C++0x. Finally, I'm sure more
boost::function-like features could be added to polymorphic_function
including members for queryng capacity, comparison operators, an
allocator template parameter, etc.

Thanks for your time and consideration!

Cordially,
Daniel Walker


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