-
-
Save ulfurinn/f8110ae86ad32e6cce35 to your computer and use it in GitHub Desktop.
unrolling an STL container into an argument list using variadic templates
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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