Here is a code for statically building any function with any number of arguments. It Is completely independent from any compiler as gcc and libraries as std or stl (excepting the printf
's used, but you can remove them safely). But requires c++0x because of the variadic templates.
#include <stdio.h>
// SIZEOF Type Package
template<typename ... Tn>
struct SIZEOF
{ static const unsigned int Result = 0; };
template<typename T1, typename ... Tn>
struct SIZEOF<T1, Tn ...>
{ static const unsigned int Result = sizeof(T1) + SIZEOF<Tn ...>::Result ; };
template<int ...>
struct MetaSequenceOfIntegers { };
template<int AccumulatedSize, typename Tn, int... GeneratedSequence>
struct GeneratorOfIntegerSequence;
template<
int AccumulatedSize,
typename Grouper,
typename Head,
typename... Tail,
int... GeneratedSequence
>
struct GeneratorOfIntegerSequence<
AccumulatedSize, Grouper( Head, Tail... ), GeneratedSequence... >
{
typedef typename GeneratorOfIntegerSequence
<
AccumulatedSize + sizeof(Head),
Grouper( Tail... ),
GeneratedSequence...,
AccumulatedSize
>::type type;
};
template<int AccumulatedSize, typename Grouper, int... GeneratedSequence>
struct GeneratorOfIntegerSequence<AccumulatedSize, Grouper(), GeneratedSequence...>
{
typedef MetaSequenceOfIntegers<GeneratedSequence...> type;
};
template<typename Tn>
class Closure;
template<typename ReturnType, typename... Tn>
class Closure<ReturnType( Tn... )>
{
public:
typedef ReturnType(*Function)(Tn ...);
static const unsigned int PARAMETERS_COUNT = sizeof...( Tn );
static const unsigned int PARAMETERS_LENGTH = SIZEOF<Tn ...>::Result;
private:
Function _entry;
char* _parameters;
public:
Closure(Function _entry, Tn ... an): _entry(_entry)
{
printf( "Closure::Closure(_entry=%d, PARAMETERS_COUNT=%d,
PARAMETERS_LENGTH=%d, sizeof=%d) => %d\n",
&_entry, PARAMETERS_COUNT, PARAMETERS_LENGTH, sizeof(*this), this );
if(PARAMETERS_LENGTH) _parameters = new char[PARAMETERS_LENGTH];
pack_helper( _parameters, an ... );
}
~Closure() {
printf( "Closure::~Closure(this=%d, _entry=%d,
PARAMETERS_COUNT=%d, PARAMETERS_LENGTH=%d, sizeof=%d)\n",
this, &_entry, PARAMETERS_COUNT, PARAMETERS_LENGTH, sizeof(*this) );
if(PARAMETERS_LENGTH) delete _parameters;
}
ReturnType operator()() {
return _run( typename GeneratorOfIntegerSequence< 0, int(Tn...) >::type() );
}
private:
template<int ...Sequence>
ReturnType _run(MetaSequenceOfIntegers<Sequence...>)
{
printf( "Closure::_run(this=%d)\n", this );
return _entry( unpack_helper<Sequence, Tn>()... );
}
template<const int position, typename T>
T unpack_helper()
{
printf( "Closure::unpack_helper(Head=%d, address=%d(%d), position=%d)\n",
sizeof( T ), _parameters + position, _parameters, position );
return *reinterpret_cast<T *>( _parameters + position );
}
public:
template<typename Head, typename ... Tail>
static void pack_helper(char* pointer_address, Head head, Tail ... tail)
{
printf( "Closure::pack_helper(
Head=%d, address=%d)\n", sizeof( Head ), pointer_address );
*reinterpret_cast<Head *>(pointer_address) = head;
pack_helper(pointer_address + sizeof( Head ), tail ...);
}
static void pack_helper(char* pointer_address) {}
};
/**
* Create a closure which can have any return type.
*/
template<typename ReturnType, typename ... Tn>
Closure< ReturnType(Tn ...) > create_closure(
ReturnType(*_entry)( Tn ... ), Tn ... an )
{
auto closure = new Closure< ReturnType(Tn ...) >( _entry, an ... );
printf( "create_closure=%d\n", closure );
return *closure;
}
char test_function1(char arg1, int arg2, bool arg3) {
printf(" test_function1: %c, %d, %d\n", arg1, arg2, arg3);
}
char test_function2(const char* arg1, const char* arg2, char arg3) {
printf(" test_function2: %s, %s, %c\n", arg1, arg2, arg3);
}
char test_function3() {
printf(" test_function3\n");
}
void test_function4() {
printf(" test_function4\n");
}
void test_function5(const char* arg1) {
printf(" test_function5=%s\n", arg1);
}
template<typename ... Tn>
void test_closure(Tn ... an) {
auto closure = create_closure(an ...);
closure();
printf( "\n" );
}
// clang++ -Xclang -ast-print -fsyntax-only test.cpp > expanded.cpp
int main()
{
test_closure( &test_function1, 'a', 10, false );
test_closure( &test_function2, "test1", "test2", 'b' );
test_closure( &test_function3 );
test_closure( &test_function4 );
test_closure( &test_function5, "Testa 3" );
test_closure( &test_function5, "Testa 4" );
}
Running it you will see the tests results:
$ g++ -o test test_variadic_critical_section_dynamic.cpp && ./test
Closure::Closure(_entry=-13672,
PARAMETERS_COUNT=3, PARAMETERS_LENGTH=6, sizeof=16) => 164864
Closure::pack_helper(Head=1, address=164976)
Closure::pack_helper(Head=4, address=164977)
Closure::pack_helper(Head=1, address=164981)
create_closure=164864
Closure::_run(this=-13520)
Closure::unpack_helper(Head=1, address=164981(164976), position=5)
Closure::unpack_helper(Head=4, address=164977(164976), position=1)
Closure::unpack_helper(Head=1, address=164976(164976), position=0)
test_function1: a, 10, 0
Closure::~Closure(this=-13520, _entry=-13520,
PARAMETERS_COUNT=3, PARAMETERS_LENGTH=6, sizeof=16)
Closure::Closure(_entry=-13672,
PARAMETERS_COUNT=3, PARAMETERS_LENGTH=17, sizeof=16) => 164976
Closure::pack_helper(Head=8, address=165008)
Closure::pack_helper(Head=8, address=165016)
Closure::pack_helper(Head=1, address=165024)
create_closure=164976
Closure::_run(this=-13520)
Closure::unpack_helper(Head=1, address=165024(165008), position=16)
Closure::unpack_helper(Head=8, address=165016(165008), position=8)
Closure::unpack_helper(Head=8, address=165008(165008), position=0)
test_function2: test1, test2, b
Closure::~Closure(this=-13520, _entry=-13520,
PARAMETERS_COUNT=3, PARAMETERS_LENGTH=17, sizeof=16)
Closure::Closure(_entry=-13624,
PARAMETERS_COUNT=0, PARAMETERS_LENGTH=0, sizeof=16) => 165008
create_closure=165008
Closure::_run(this=-13520)
test_function3
Closure::~Closure(this=-13520, _entry=-13520,
PARAMETERS_COUNT=0, PARAMETERS_LENGTH=0, sizeof=16)
Closure::Closure(_entry=-13624,
PARAMETERS_COUNT=0, PARAMETERS_LENGTH=0, sizeof=16) => 165040
create_closure=165040
Closure::_run(this=-13520)
test_function4
Closure::~Closure(this=-13520, _entry=-13520,
PARAMETERS_COUNT=0, PARAMETERS_LENGTH=0, sizeof=16)
Closure::Closure(_entry=-13624,
PARAMETERS_COUNT=1, PARAMETERS_LENGTH=8, sizeof=16) => 165072
Closure::pack_helper(Head=8, address=609568)
create_closure=165072
Closure::_run(this=-13520)
Closure::unpack_helper(Head=8, address=609568(609568), position=0)
test_function5=Testa 3
Closure::~Closure(this=-13520, _entry=-13520,
PARAMETERS_COUNT=1, PARAMETERS_LENGTH=8, sizeof=16)
Closure::Closure(_entry=-13624,
PARAMETERS_COUNT=1, PARAMETERS_LENGTH=8, sizeof=16) => 609568
Closure::pack_helper(Head=8, address=609600)
create_closure=609568
Closure::_run(this=-13520)
Closure::unpack_helper(Head=8, address=609600(609600), position=0)
test_function5=Testa 4
Closure::~Closure(this=-13520, _entry=-13520,
PARAMETERS_COUNT=1, PARAMETERS_LENGTH=8, sizeof=16)
You can run it with clang++
to see the generated template code:
$ clang++ -Xclang -ast-print -fsyntax-only test.cpp > expanded.cpp
// ...
private:
template<> char _run<<0, 8, 16>>(MetaSequenceOfIntegers<0, 8, 16>)
{
return this->_entry(
this->unpack_helper<0, const char *>(),
this->unpack_helper<8, const char *>(),
this->unpack_helper<16, char>()
);
}
template<> const char *unpack_helper<0, const char *>()
{
return *reinterpret_cast<const char **>(this->_parameters + 0);
}
template<> const char *unpack_helper<8, const char *>() {
return *reinterpret_cast<const char **>(this->_parameters + 8);
}
template<> char unpack_helper<16, char>() {
return *reinterpret_cast<char *>(this->_parameters + 16);
}
// ...
References
- How to reverse an integer parameter pack?
- Can we see the template instantiated code by C++ compiler
- Variadic templates, parameter pack and its discussed ambiguity in a parameter list
- "unpacking" a tuple to call a matching function pointer