c++ - transpose template function boolean arguments to runtime function arguments with template metaprogramming -
c++ - transpose template function boolean arguments to runtime function arguments with template metaprogramming -
i have function takes several boolean template arguments:
template<bool par1, bool par2, bool par2> void function(int arg1, int arg2, int arg3);
i want generate automatically @ compile-time (with whatever template magic, c++11 if needed) table (or equivalent in funny structures of c++ metaprogramming) of function pointers combination of values of template parameters par*
, can build function takes these template parameters runtime arguments , forwards right template instantiation:
void runtime_function(bool par1, bool par2, bool par3, int arg1, int arg2, int arg3);
i think can done if instead of template function 1 wanted same classes, template template arguments:
template<template<bool> class t> class combinationsoftemplateparameters; template<template<bool, bool> class t> class combinationsoftemplateparameters; template<template<bool, bool, bool> class t> class combinationsoftemplateparameters; //and on, implementation defined hard limit.
but far know there's no way point generic template function, leaving template parameters unspecified. hence don't know how pass helper class in template parameter list, in first place.
is there way solve problem?
step one, understand problem, build array of function pointers each instantiation:
template<bool, bool, bool> void function(int, int, int); typedef void (*func_type)(int, int, int); func_type funcs[] = { &function<false, false, false>, &function<false, false, true>, &function<false, true, false>, &function<false, true, true >, &function<true, false, false>, &function<true, false, true >, &function<true, true, false>, &function<true, true, true > };
notice how looks table of 3-bit binary numbers:
0 0 0 == 0 0 0 1 == 1 0 1 0 == 2 0 1 1 == 3 // etc...
so can index array integer formed bitwise operations:
void runtime_function(bool par1, bool par2, bool par3, int arg1, int arg2, int arg3) { func_type f = funcs[ int(par1)<<2 | int(par2)<<1 | int(par3) ]; f(arg1, arg2, arg3); };
step two, i've understood how build array , utilize it, generate array automatically variadic template, instead of writing out hand.
first utilize type creates parameter pack of integers (using johannes schaub's seq
template):
template<int ...> struct seq { }; template<int n, int ...s> struct gens : gens<n-1, n-1, s...> { }; template<int ...s> struct gens<0, s...> { typedef seq<s...> type; };
then utilize in pack expansion generate each possible instantiation:
template<bool, bool, bool> void function(int, int, int); typedef void (*func_type)(int, int, int); template<typename> struct make_table; template<int... n> struct make_table<seq<n...>> { static const func_type funcs[sizeof...(n)]; }; template<int... n> const func_type make_table<seq<n...>>::funcs[sizeof...(n)] = { &function< bool(n&4), bool(n&2), bool(n&1) >... };
now can utilize so:
void runtime_function(bool par1, bool par2, bool par3, int arg1, int arg2, int arg3) { typedef gens<8>::type seq8; func_type f = make_table<seq8>::funcs[ (par1<<2) | (par2<<1) | par3 ]; f(arg1, arg2, arg3); }
the magic number 8
2 powerfulness of 3 (the number of bool parameters.)
step three, test it. i'm confident wouldn't have compiled if got core logic wrong, types , pack expansions checked compiler, have got bitwise operations wrong.
#include <iostream> template<bool b1, bool b2, bool b3> void function(int i1, int i2, int i3){ std::cout << std::boolalpha << "f<" << b1 << ", " << b2 << ", " << b2 << ">(" << i1 << ", " << i2 << ", " << i3 << ")\n"; } int main() { runtime_function(false, true, true, 1, 2, 3); runtime_function(true, false, false, 4, 5, 6); }
it prints:
f<false, true, true>(1, 2, 3) f<true, false, false>(4, 5, 6)
fully generic version to function template 4 bool template parameters you'd need utilize gens<16>
, alter pack expansion
template<int... n> const func_type make_table<seq<n...>>::funcs[] = { &function< bool(n&8), bool(n&4), bool(n&2), bool(n&1) >... };
this isn't convenient, should possible generalise handle number of parameters, introducing parameter pack of ints seq<3,2,1,0>
, using like:
template<int... n, int... bits> const func_type make_table<seq<n...>, seq<bits...>>::funcs[] = { &function< /* bitwise op using n & (1<<bits) ... */ > ... };
but won't work, because want pack expansion using bits
don't want expand n
@ same time (and packs have different sizes woulodn't work anyway,) need utilize level of indirection allow packs expanded separately.
the final version below uses function gen_func<n>
function pointer @ index n:
template<unsigned n, int... mask> static constexpr func_type gen_func(seq<mask...>) { homecoming &function<(n&(1<<mask))...>; }
and adds genrevs
create reverse sequence of integers, seq<2,1,0>
, passed function used mask
parameter pack:
gen_func<i>(typename genrevs<nparams>::type()) ...
with alter make_table
class template can handle functions arity, final step parameterise function type (and have deduce number of parameters, , number of possible function specializations) , add together accessor make_table
right function:
void runtime_function(bool par1, bool par2, bool par3, int arg1, int arg2, int arg3) { auto f = make_table<void(int, int, int)>::get(par1, par2, par3); f(arg1, arg2, arg3); }
here's total final version. after writing code lastly night realised assumes number of function parameters (int, int, int)
same number of template parameters <bool, bool, bool>
, if that's not true you'd need add together non-type template parameter make_table
, specifying number of template parameters (in code below that's nparams
, deduced):
#include <type_traits> template<int ...> struct seq { }; template<int n, int ...s> struct gens : gens<n-1, n-1, s...> { }; template<int ...s> struct gens<0, s...> { typedef seq<s...> type; }; template<int n, int ...s> struct genrevs : genrevs<n-1, s..., n-1> { }; template<int ...s> struct genrevs<0, s...> { typedef seq<s...> type; }; template<bool, bool, bool> void function(int, int, int); template<unsigned n> struct pow2 { static constexpr unsigned value = 2*pow2<n-1>::value; }; template<> struct pow2<0> { static constexpr unsigned value = 1; }; template<typename signature> struct make_table_seq; template<typename res, typename... params> struct make_table_seq<res(params...)> : gens<pow2<sizeof...(params)>::value> { }; template<typename signature, typename = typename make_table_seq<signature>::type> struct make_table; template<typename res, typename... params, int... i> class make_table<res(params...), seq<i...>> { static const unsigned nparams = sizeof...(params); public: typedef res (*func_type)(params...); template<typename... bool> static typename std::enable_if<sizeof...(bool)==nparams, func_type>::type get(bool... b) { homecoming funcs[ shift_or(0, b...) ]; } private: template<unsigned n, int... mask> static constexpr func_type gen_func(seq<mask...>) { homecoming &function<(bool(n&(1<<mask)))...>; } template<typename... bool> static int shift_or(int i, bool b0, bool... b) { homecoming shift_or((i<<1) | int(b0), b...); } static int shift_or(int i) { homecoming i; } static const func_type funcs[sizeof...(i)]; }; template<typename res, typename... params, int... i> const typename make_table<res(params...), seq<i...>>::func_type make_table<res(params...), seq<i...>>::funcs[] = { gen_func<i>(typename genrevs<nparams>::type()) ... }; // specialise function pointer types function types template<typename res, typename... params> struct make_table_seq<res(*)(params...)> : make_table_seq<res(params...)> { }; template<typename res, typename... params, typename t> class make_table<res(*)(params...), t> : make_table<res(params...)> { };
c++ templates c++11 metaprogramming template-meta-programming
Comments
Post a Comment