将模板函数boolean参数转换为带有模板元编程的运行时函数参数 [英] transpose template function boolean arguments to runtime function arguments with template metaprogramming

查看:204
本文介绍了将模板函数boolean参数转换为带有模板元编程的运行时函数参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个函数,它需要几个布尔模板参数:

 模板< bool par1,bool par2,bool par2& 
void function(int arg1,int arg2,int arg3);

我想在编译时自动生成(用任何模板魔术,用C ++ 11 if需要)将函数指针的模板参数 par * 的值的所有组合的表(或在C ++元编程的有趣结构中的等同物)我可以构造一个函数,它将这些模板参数作为运行时参数,并转发到正确的模板实例:

  void runtime_function(bool par1 ,bool par2,bool par3,int arg1,int arg2,int arg3); 

我认为这可以做,如果而不是一个模板函数想要做类似的,感谢模板模板参数:

 模板< template< bool>类T>类CombinationsOfTemplateParameters; 
template< template< bool,bool>类T> class CombinationsOfTemplateParameters;
template< template< bool,bool,bool>类T> class CombinationsOfTemplateParameters;
//等等,最多一些实现定义的硬限制。

但是据我所知,没有办法指向一个通用的模板函数,未指定。因此,我不知道如何将它传递给它的模板参数列表中的一些帮助类。



有没有办法解决这个问题? / p>

解决方案

第一步,为了理解问题,我将构建一个函数指针数组, b
$ b

 模板< 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> ;,
&函数< false,true,false>,
& function< false,true,true> ;,
& function< true,false,false> ;,
& function& false,true>,
& function< true,true,false> ;,
& function< true,true,true>
};

注意这看起来像一个3位二进制数表:

  0 0 0 == 0 
0 0 1 == 1
0 1 0 == 2
0 1 1 == 3
// etc ...

由位运算形成的整数:

  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);
};

第二步,现在我已经了解了如何构造数组并使用它,



首先使用创建一个整数参数包的类型(使用 Johannes Schaub seq 模板):

  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 ...>类型;
};

然后在包展开式中使用它来生成每个可能的实例化:

 模板< 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)

现在你可以这样使用:

  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);

}

魔法数字 code>是三的幂的二(bool参数的数量)。



第三步,测试它。我相信它甚至不会编译如果我得到核心逻辑错误,因为所有的类型和包扩展将由编译器检查,但我可以得到位操作错误。

  #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);
}

打印:

  f  f< true,false,false>(4,5,6)






完全通用版本



要为具有四个bool模板参数的函数模板执行此操作,您需要使用 gens <16> 并更改包扩展

  template< int ... N> 
const func_type make_table< seq< N ...>> :: funcs [] = {
& function< bool(N& 8),bool(N& 4),bool(N& 2),bool

这不是很方便,因此应该可以将其概括为处理任意数量的参数,通过引入另一个int seq <3,2,1,0> 的参数包并使用它:

  template< int ... N,int ... Bits> 
const func_type make_table< seq< N ...>,seq< Bits ...>> :: funcs [] = {
& function< / *一些按位操作使用N& (1 };

但这不会工作,因为我们想要使用 ,但我们不希望它同时展开 N (这些包有不同的大小,所以它无论如何不工作)因此我们需要使用间接级别来允许单独扩展包。



下面的最终版本使用函数 gen_func< N> 以获取索引N处的函数指针:

 模板< unsigned N,int ... Mask> ; 
static constexpr func_type gen_func(seq
{return& function<(N&(1<< Mask))...& }

并添加 genrevs 整数序列, seq <2,1,0> ,它被传递给该函数以用作 Mask parameter pack:

  gen_func< I>(typename genrevs< NParams> :: type())... 

有了这个变化, make_table 类模板可以处理函数任何arity,所以最后一步是通过函数类型参数化它(并且它推导出参数的数量,并从中可能的函数特化的数量),并添加一个访问器到 make_table 得到正确的函数:

  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);
}

这是完整的最终版本。在昨晚写这个代码后,我意识到它假定函数参数的数量(int,int,int)与模板参数的数量相同< bool,bool,bool> ,如果这不是真的那么你需要添加一个额外的非类型模板参数到 make_table ,指定模板参数的数量(在下面的代码 NParams 并被推导出):

  #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 ...>类型;
};

template< int N,int ... S>
struct genrevs:genrevs< N-1,S ...,N-1> {};

template< int ... S>
struct genrevs< 0,S ...> {
typedef seq< S ...>类型;
};

template< bool,bool,bool> void function(int,int,int);

template< unsigned N>
struct pow2
{
static constexpr unsigned value = 2 * pow2< N-1> :: value;
};

模板<> 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)
{return funcs [shift_or ,b ...)]; }

private:
template< unsigned N,int ... Mask>
static constexpr func_type gen_func(seq< Mask ...>)
{return& function<(bool(N&(1 << Mask)))...) }

template< typename ... Bool>
static int shift_or(int i,bool b0,Bool ... b)
{
return shift_or((i << 1)| int(b0),b ...) ;
}

static int shift_or(int i){return 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())...
}

//专门用于函数指针类型以及函数类型
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 ...)>
{};


I have a function which takes several boolean template arguments:

template<bool par1, bool par2, bool par2>
void function(int arg1, int arg2, int arg3);

I want to generate automatically at compile-time (with whatever template magic, with C++11 if needed) a table (or something equivalent as in the funny structures of C++ metaprogramming) of the function pointers to all the combination of the values of the template parameters par*, so that I can construct a function which takes these template parameters as runtime arguments and forward to the right template instantiation:

void runtime_function(bool par1, bool par2, bool par3, int arg1, int arg2, int arg3);

I think that this can be done if instead of a template function one wanted to do the same with classes, thanks to 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 so on, up to some implementation defined hard limit.

But as far as I know there's no way to point to a generic template function, leaving its template parameters unspecified. Hence I don't know how to pass it to some helper class in its template parameter list, in the first place.

Is there a way to solve this problem?

解决方案

Step one, just to understand the problem, I would construct an array of function pointers with 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 that looks like a table of 3-bit binary numbers:

0 0 0  == 0
0 0 1  == 1
0 1 0  == 2
0 1 1  == 3
// etc...

So you can index into the array with an integer formed by 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, now that I've understood how to construct the array and use it, I would generate the array automatically with a variadic template, instead of writing it out by hand.

First use a type that creates a 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 use that in a pack expansion to 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 you can use that like 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 is two to the power of three (the number of bool parameters.)

Step three, test it. I'm fairly confident it wouldn't even have compiled if I got the core logic wrong, as all the types and pack expansions will be checked by the compiler, but I could have got the 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 do it for a function template with four bool template parameters you'd need to use gens<16> and change the 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 very convenient, so it should be possible to generalise it to handle any number of parameters, by introducing another parameter pack of ints seq<3,2,1,0> and using it like:

template<int... N, int... Bits>
  const func_type make_table<seq<N...>, seq<Bits...>>::funcs[] = {
    &function< /* some bitwise op using N & (1<<Bits) ... */ > ...
  };

But this won't work, because we want a pack expansion using Bits but we don't want it to expand N at the same time (and the packs have different sizes so it woulodn't work anyway,) so we need to use a level of indirection to allow the packs to be expanded separately.

The final version below uses a function gen_func<N> to get the function pointer at index N:

template<unsigned N, int... Mask>
  static constexpr func_type gen_func(seq<Mask...>)
  { return &function<(N&(1<<Mask))...>; }

And adds genrevs to create a reverse sequence of integers, seq<2,1,0>, which is passed to that function to be used as the Mask parameter pack:

gen_func<I>(typename genrevs<NParams>::type()) ...

With that change the make_table class template can handle functions with any arity, so the final step is to parameterise it by the function type (and have it deduce the number of parameters, and from that the number of possible function specializations) and to add an accessor to make_table to get the 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 the full final version. After writing this code last night I realised it assumes that the number of function parameters (int, int, int) is the same as the number of template parameters <bool, bool, bool>, if that's not true then you'd need to add an extra non-type template parameter to make_table, specifying the number of template parameters (in the code below that's NParams and is 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)
      { return funcs[ shift_or(0, b...) ]; }

  private:
    template<unsigned N, int... Mask>
      static constexpr func_type gen_func(seq<Mask...>)
      { return &function<(bool(N&(1<<Mask)))...>; }

    template<typename... Bool>
      static int shift_or(int i, bool b0, Bool... b)
      {
        return shift_or((i<<1) | int(b0), b...);
      }

    static int shift_or(int i) { return 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 for function pointer types as well as 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...)>
  { };

这篇关于将模板函数boolean参数转换为带有模板元编程的运行时函数参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆