Okay, well here's a very basic example (believe it or not!). It should be bug-free, but you might need to do some testing, to be sure.
Code:
#include <memory>
/*
Placeholder
*/
struct DataType
{ };
/*
This is what all classes derive from.
A function named 'process' must be defined, and can be inline.
Also known as 'The Curiously Recurring Template Idiom'.
*/
template < typename Derived >
struct function_base
{
inline Derived& derived( void )
{
return *static_cast< Derived* >( this );
}
inline Derived const& derived( void ) const
{
return *static_cast< Derived const* >( this );
}
inline void operator ( )( DataType& data )
{
derived( ).process( data );
}
};
/*
This class provides the 'chaining' mechanism. Used internally only.
*/
template < typename Lhs, typename Rhs >
struct function_base_list : function_base< function_base_list< Lhs, Rhs > >
{
function_base_list( function_base< Lhs > const& lhs, function_base< Rhs > const& rhs )
: lhs( lhs.derived( ) ), rhs( rhs.derived( ) )
{ }
inline void operator ( )( DataType& data )
{
lhs( data );
rhs( data );
}
Lhs
lhs;
Rhs
rhs;
};
/*
The 'chaining' generator.
*/
template < typename Lhs, typename Rhs >
function_base_list< Lhs, Rhs > operator + ( function_base< Lhs > const& lhs, function_base< Rhs > const& rhs )
{
return function_base_list< Lhs, Rhs >( lhs, rhs );
}
/*
This class basically just simplifies things so that you:
1) don't have to write out a complex template declarations.
2) can assign a value to a function_base_list in the future, rather than immediately.
Since it's also derived from function_base_list, you can chain it to other lists, as well.
Ideally, it should use a smart pointer, but since this is just a simple example,
I just went with an std::auto_ptr. The one nice thing about using that, though,
is that it allows you to concatenate a greedy_function_base_holder to itself
without creating a recursive situation.
*/
struct greedy_function_base_holder : function_base< greedy_function_base_holder >
{
struct base_dispatcher : function_base< base_dispatcher >
{
virtual void operator ( )( DataType& )
{ }
};
template < typename Derived >
struct derived_dispatcher : base_dispatcher
{
derived_dispatcher( function_base< Derived > const& self )
: self( self.derived( ) )
{ }
virtual void operator ( )( DataType& data )
{
self( data );
}
Derived
self;
};
greedy_function_base_holder( void )
{
*this = base_dispatcher( );
}
greedy_function_base_holder( greedy_function_base_holder const& rhs )
{
*this = rhs;
}
template < typename Derived >
greedy_function_base_holder( function_base< Derived > const& rhs )
{
*this = rhs;
}
greedy_function_base_holder& operator = ( greedy_function_base_holder const& rhs )
{
greedy_function_base_holder&
sap = const_cast< greedy_function_base_holder& >( rhs );
ptr = sap.ptr;
sap = base_dispatcher( );
return *this;
}
template < typename Derived >
greedy_function_base_holder& operator += ( function_base< Derived > const& rhs )
{
return *this = *this + rhs;
}
template < typename Derived >
greedy_function_base_holder& operator = ( function_base< Derived > const& rhs )
{
ptr = std::auto_ptr< base_dispatcher >( new derived_dispatcher< Derived >( rhs ) );
return *this;
}
inline void operator ( )( DataType& data )
{
( *ptr.get( ) )( data );
}
std::auto_ptr< base_dispatcher >
ptr;
};
// Example:
#include <iostream>
using namespace
std;
struct test : function_base< test >
{
test( int value = 0 )
: value( value )
{ }
/*
Parameter unused for test
*/
inline void process( DataType& )
{
cout << value << endl;
}
int
value;
};
int main( void )
{
DataType
unused;
greedy_function_base_holder
gfbh1 = test( 1 ) + test( 2 ) + test( 3 ),
gfbh2 = gfbh1 + test( 4 ) + test( 5 ) + test( 6 );
gfbh2 += test( 7 ) + test( 8 ) + test( 9 );
gfbh2( unused );
return 0;
}
EDIT: Oh and maybe I should make this clear: the greedy_function_base_holder is called 'greedy' because it will 'take' the underlying data from any greedy_function_base_holder's chained to it. So for instance, 'gfbh1' in the example above loses it's data after it is chained to 'gfbh2'. Like I said, a smart pointer would be a better approach for that class (just be careful with cyclic references!).