Skip to content

Instantly share code, notes, and snippets.

@ulfurinn
Created November 21, 2011 15:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ulfurinn/f8110ae86ad32e6cce35 to your computer and use it in GitHub Desktop.
Save ulfurinn/f8110ae86ad32e6cce35 to your computer and use it in GitHub Desktop.
unrolling an STL container into an argument list using variadic templates
/*
* variadic.cpp
*
* Created on: Nov 21, 2011
* Author: ulfurinn
*/
#include <vector>
#include <iostream>
#include <stdexcept>
using namespace std;
// recursion step, argument accumulation
template< int remaining, typename Ret, typename Class, typename F, typename Container >
class invoker {
public:
template< typename ... Args >
static inline Ret invoke( Class * inst, F f, Container args, Args ... unrolled_args ) {
return invoker< remaining - 1, Ret, Class, F, Container >::invoke( inst, f, args,
args[ remaining - 1 ], unrolled_args... );
}
};
// recursion terminator, actual call
template< typename Ret, typename Class, typename F, typename Container >
class invoker< 0, Ret, Class, F, Container > {
public:
template< typename ... Args >
static inline Ret invoke( Class * inst, F f, Container args, Args ... unrolled_args ) {
return ( inst->*f )( unrolled_args... );
}
};
template< typename Ret, typename Class, typename Container >
class fun_wrap_base {
public:
virtual ~fun_wrap_base( ) {
}
// virtual functions cannot be templates, we have to sacrifice Container generality, although overloads can help to some extent
virtual Ret invoke( Class * inst, Container args ) = 0;
};
template< typename Ret, typename Class, typename Container, typename ... P >
class fun_wrap : public fun_wrap_base< Ret, Class, Container > {
public:
static const unsigned arity = sizeof...(P);
fun_wrap( Ret(Class::*func)( P... ) ) :
f( func ) {
}
virtual ~fun_wrap( ) {
}
virtual Ret invoke( Class * inst, Container args ) {
if ( args.size() != arity )
throw std::invalid_argument( "Arity mismatch" );
return invoker< arity, Ret, Class, Ret(Class::*)( P... ), Container >::invoke( inst, f,
args );
}
private:
Ret (Class::*f)( P... );
};
class method {
public:
method( ) :
impl_( nullptr ) {
}
method( fun_wrap_base< int, demo, std::vector< int > > * impl ) :
impl_( impl ) {
}
int operator()( demo * inst, std::vector< int > args ) {
return impl_->invoke( inst, args );
}
operator bool( ) {
return impl_ != nullptr;
}
private:
fun_wrap_base< int, demo, std::vector< int > > * impl_;
};
template< typename Ret, typename Class, typename ... P >
method wrap( Ret(Class::*f)( P... ) ) {
return method( new fun_wrap< Ret, Class, std::vector< int >, P... >( f ) );
}
class demo {
public:
virtual int fun0( ) = 0;
virtual int fun1( int ) = 0;
virtual int fun2( int, int ) = 0;
virtual int fun3( int, int, int ) = 0;
};
class demo1 : public demo {
public:
int fun0( ) {
return 42;
}
int fun1( int a ) {
return a;
}
int fun2( int a, int b ) {
return a + b;
}
int fun3( int a, int b, int c ) {
return a + b * c;
}
};
class demo2 : public demo1 {
public:
int fun0( ) {
return 43;
}
int fun3( int a, int b, int c ) {
return 55;
}
};
int main( ) {
method f0 = wrap( &demo::fun0 );
method f1 = wrap( &demo::fun1 );
method f2 = wrap( &demo::fun2 );
method f3 = wrap( &demo::fun3 );
demo * x = new demo1(), * y = new demo2();
cout << f0( x, vector< int >() ) << endl;
cout << f1( x, vector< int >( { 1 } ) ) << endl;
cout << f2( x, vector< int >( { 1, 2 } ) ) << endl;
cout << f3( x, vector< int >( { 1, 2, 3 } ) ) << endl;
cout << endl;
cout << f0( y, vector< int >() ) << endl;
cout << f1( y, vector< int >( { 1 } ) ) << endl;
cout << f2( y, vector< int >( { 1, 2 } ) ) << endl;
cout << f3( y, vector< int >( { 1, 2, 3 } ) ) << endl;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment