Sunday, February 7, 2010

Implementing std::tr1::function - pt 1

This is the first of what will be a series of articles about implementing a general purpose function wrapper that can contain any callable entity that responds to a particular signature. These articles will start with a much simpler problem and slowly add complexity until a full implementation has been created. Some insight will be taken from boost::function, the singly most commonly used implementation.

We'll start with something simple, working with a single, basic signature: void (int).


// abstract invoker
struct impl
{
virtual ~impl() {}
virtual void invoke(int) = 0;
virtual impl * clone() const = 0;
};

// invoker for function pointers.
struct fun_ptr_impl : impl
{
typedef void (*fun_ptr_t)(int);

fun_ptr_impl(fun_ptr_t f) : fun(f) {}
void invoke( int i ) { fun(i); }
impl * clone() const { return new fun_ptr_impl(*this); }

private:
fun_ptr_t fun;
};

// invoker for functors.
template < typename T >
struct object_impl : impl
{
object_impl(T const& t) : object(t) {}

void invoke( int i ) { return object(i); }

impl * clone() const { return new object_impl(object); }

private:
T object;
};

#include <boost/scoped_ptr.hpp>
#include <cassert>
// The function wrapper itself.
struct inheritance_function
{
typedef void (*fun_ptr_t)(int);

// construct with function pointer.
inheritance_function(fun_ptr_t f) : pimpl(new fun_ptr_impl(f))
{}
// construct with functor
template < typename Object >
inheritance_function(Object const& o) : pimpl(new object_impl<Object>(o))
{}
// copy
inheritance_function(inheritance_function const& ifun) : pimpl(ifun.pimpl->clone())
{}

// invoke the stored function.
void operator()(int i) const { return pimpl->invoke(i); }

// assign from inheritance_function
inheritance_function & operator = (inheritance_function const& ifun)
{
pimpl.reset(ifun.pimpl->clone());
return *this;
}
// assign from function pointer
inheritance_function & operator = (fun_ptr_t f)
{
pimpl.reset(new fun_ptr_impl(f));
return *this;
}
// assign from functor
template < typename Object >
inheritance_function & operator = (Object ob)
{
pimpl.reset(new object_impl<Object>(ob));
return *this;
}

private:
boost::scoped_ptr<impl> pimpl;
};

// some test code
#include <iostream>
void function(int x) { std::cout << "function(" << x << ")" << std::endl; }

struct object
{
void operator() (int x) { std::cout << "object::operator()(" << x << ")" << std::endl; }
};

int main()
{
inheritance_function f1 = function;
inheritance_function f2 = object();

f1(5); f2(6);

f2 = function;
f1 = object();

f1(5); f2(6);

f2 = f1;

f1(5); f2(6);
}


Pretty basic so far. There's much that is missing though and I'll continue adding as I go forward. All this code does is replace an internal invoker class based upon the assignment or construction argument passed in. If we get a function pointer then we create an invoker able to handle function pointers. Anything else we assume is a functor object. Granted, this could be in error but then the user of the class is attempting to use it inappropriately.

Now, keep in mind that this does not resemble boost::function at all. This is, however, the most straight forward method of implementing this behavior. If you read the boost::function website you'll find them explain why they do it differently:

The use of virtual functions tends to cause 'code bloat' on many compilers. When a class contains a virtual function, it is necessary to emit an additional function that classifies the type of the object. It has been our experience that these auxiliary functions increase the size of the executable significantly when many boost::function objects are used.

The next article will show how to make the above implementation take any argument and return any type. A new impl will need to be created to account for member function pointers. The article following that will explain how boost::function implements this functionality without using virtual functions.

No comments:

Post a Comment