Boost logo

Boost :

From: Marco (mrcekets_at_[hidden])
Date: 2007-10-07 18:33:15


On Tue, 02 Oct 2007 01:52:51 +0200, Joel de Guzman
<joel_at_[hidden]> wrote:
>
> As I said in my other post, it's better not to rely on mpl or
> fusion for low level libs like this. I gave a proof of
> concept. The challenge is to make it complete. The ideal is
> to:
>
> 1) Not rely on any libraries at all except boost.function
> whereby making it trim and clean (it's ok to use Boost PP**).
> Yep, you can get rid of the need for tuples, but if that
> makes it too painful, I guess it's ok to use it. It's a fairly
> simple class anyway.
> 2) Another challenge is to make it usable on as many compilers
> as possible. The first ideal is more important that this one.
> Perhaps it's ok to forget antiquated compilers without partial
> specialization, etc.
>
> (**You'll still have to expand the overload_function(s) for
> N arguments, where N is the maximum the library can handle)
>

As Marco Costalba, I worked a little on a possible extension
of boost::function in order to support overloading.

Just a simple example:

int foo1(char )
{
     return 123;
}

double foo2(int, char)
{
     return 123.456;
}

int main()
{
   overload<int(char ), double(int, char )> f;

   f.set( &foo1 );
   f.set( &foo2 );

   int i = f('x');
   double d = f(123, 'x');

   BOOST_ASSERT(i == 123);
   BOOST_ASSERT(d > 123.455 && d < 123.457);

   return 0;
}

My implementation relies on recursive inheritance of
structs that inherit directly from boost::function.
In this way I get function arguments automatically managed.
However it is not based on tuples but it uses BOOST_PP_* macros
and some utility from boost traits library.
The nicest feature, IMO, is that in the most of the cases
there isn't the need to specify indeces in order to set
an overload: the signature is deduced from the type of
the passed functor and used for setting the correct
boost::function enclosed object.
The max number of supported overloads is defined through
a configurable macro and the max number of function arguments
is the same available in the boost function library.

At present the implementation of the overload class provides:

(1)
Several constructors and methods to set overload functions
which can be used with any number (i.e. <= BOOST_OVERLOAD_LIMIT)
of parameters and that relies on signature deduction.
The kinds of handled functors are:
-function pointers
-pointers to member function
-function objects
-function object wrapped with boost::ref.
The functors can be passed in any order.

overload<sig1_t, sig2_t> f;
f.set(&foo2, &foo1)

However overloaded and polymorfic function objects are
*not* handled, by these interface functions.

(2)
It's for this reason that I implemented also two template
methods for setting an overload: one based on indices,
and another based on signatures:

overload<sig1_t, sig2_t> f;
f.set<0>(&foo1);
f.set<sig2_t>(&foo2);

(3)
As in the boost function library swap, empty and clear methods,
are provided, moreover there is a get method in order
to obtain a (const) reference to a specific boost function
which belongs to the overload. See the example:

void foo(std::string s1, std::string s2, std::string s3)
{
     std::cout << s1 << s2 << s3 << std::endl;
}

[snip]

// .... inside main:

typedef void sig2_t (std::string , std::string , std::string );
sig2_t* f2 = &foo;

boost::overload<sig1_t, sig2_t, sig3_t, sig4_t> f;

[snip]

f.set(f2);
f( "Hello", ", ", "world !" );

f.clear<sig2_t>(); // same as f.clear<1>()
BOOST_ASSERT( f.empty<sig2_t>() ); // same as f.empty<1>()

boost::function<sig2_t> g(f2);
f.swap_function(g);
f("I'm ", "back ", "again !");
BOOST_ASSERT( g.empty() );

g = f.get<sig2_t>(); // same as f.get<1>()
g("That's ", "all ", "folks !");

The empty, clear and get methods are available in two flavours:
signature based and index based (index start from 0).
The swap_function method works only with boost::function objects.

(4)
Some utility for type information :

typedef boost::overload<sig1_t, sig2_t, sig3_t> overload_type;

BOOST_STATIC_ASSERT((
     overload_type::has_signature<sig_t1>::value ));
BOOST_STATIC_ASSERT((
      is_same<overload_type::signature<2>::type, sig3_t>::value ));

How do you find the interface: confortable or confusing ?
At present stage I've kept methods name as simple as possible
and coherent with the ones used in boost::function.
If you find them too generic I could append a _overload
or _function suffix but better ideas are welcome.

In the attached file there is an implementation of
the overload class, you should consider it as a start point
for discussion. Indeed the implementations are two:

in the first I deduce function object signature by exploiting
template function argument deduction, if the signature is not
discovered no error is reported, and error handling is demanded
to boost::function at call time; signature deduction of
overloaded and polymorfic function objects cause an "unresolved
overloaded function type" compile error;

in the second implementation, with more template metaprogramming
I succeed in deducing function object signature using only
template classes; if the signature is not deduced a compile time
error is produced; if this behavior is not desirable I can remove
it, or turn it in a user controllable feature;
signature deduction of overloaded and polymorfic function objects
is successfull if it exists one and only one signature registered
in the overload that matches with the function object, else
an ad-hoc compile time error, pointing out the ambiguity,
is produced.

I tested both implementations with gcc 4.1.2 under Linux.
Your opinions on pro and cons of the two implementations
would be most appreciated. I'd like to know if the boost
community finds such an extension of the boost function
library worthwhile and useful, and if you consider the proposed
implementation a sensible approach.

Regards,
Marco Cecchetti

-- 
Using Opera's revolutionary e-mail client: http://www.opera.com/mail/



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