使用模板元编程将模板函数布尔参数转换为运行时函数参数

Lor*_*one 6 c++ templates metaprogramming template-meta-programming c++11

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

template<bool par1, bool par2, bool par2>
void function(int arg1, int arg2, int arg3);
Run Code Online (Sandbox Code Playgroud)

我想在编译时自动生成(使用任何模板魔法,如果需要,使用C++ 11)一个表(或类似于C++元编程的有趣结构中的某些东西)的函数指针到所有值的组合模板参数par*,这样我就可以构造一个函数,它将这些模板参数作为运行时参数并转发到正确的模板实例化:

void runtime_function(bool par1, bool par2, bool par3, int arg1, int arg2, int arg3);
Run Code Online (Sandbox Code Playgroud)

我认为,如果不是模板函数,可以通过模板模板参数来实现这一点:

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.
Run Code Online (Sandbox Code Playgroud)

但据我所知,没有办法指向通用模板函数,不保留其模板参数.因此,我首先不知道如何将其传递给模板参数列表中的某个辅助类.

有没有办法解决这个问题?

Jon*_*ely 20

第一步,只是为了理解问题,我将为每个实例化构造一个函数指针数组:

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 >
};
Run Code Online (Sandbox Code Playgroud)

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

0 0 0  == 0
0 0 1  == 1
0 1 0  == 2
0 1 1  == 3
// etc...
Run Code Online (Sandbox Code Playgroud)

因此,您可以使用按位运算形成的整数索引数组:

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);
};
Run Code Online (Sandbox Code Playgroud)

第二步,既然我已经了解了如何构造数组并使用它,我会使用可变参数模板自动生成数组,而不是手工编写.

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

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;
};
Run Code Online (Sandbox Code Playgroud)

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

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) >...
  };
Run Code Online (Sandbox Code Playgroud)

现在你可以像这样使用它:

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

}
Run Code Online (Sandbox Code Playgroud)

神奇的数字8是两个三的幂(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);
}
Run Code Online (Sandbox Code Playgroud)

它打印:

f<false, true, true>(1, 2, 3)
f<true, false, false>(4, 5, 6)
Run Code Online (Sandbox Code Playgroud)

完全通用的版本

要为具有四个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(N&1) >...
  };
Run Code Online (Sandbox Code Playgroud)

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

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

但这不起作用,因为我们希望使用包扩展,Bits但我们不希望它同时扩展N(并且包装有不同的尺寸,因此它无论如何都不起作用)所以我们需要使用一个级别间接允许包单独扩展.

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

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

并添加genrevs以创建一个反向的整数序列seq<2,1,0>,将其传递给该函数以用作Mask参数包:

gen_func<I>(typename genrevs<NParams>::type()) ...
Run Code Online (Sandbox Code Playgroud)

与该改变make_table类模板可以处理与任何元数的功能,所以最终的步骤是通过将函数类型对其进行参数化(和具有它推导的参数的数量,并且从所述的可能的功能特数)并添加一个存取要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);
}
Run Code Online (Sandbox Code Playgroud)

这是完整的最终版本.在昨晚写完这段代码后,我意识到它假定函数参数(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...> 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...)>
  { };
Run Code Online (Sandbox Code Playgroud)

  • 这只是一个进行中的工作,以证明它是一个二进制数表.示例的其余部分不使用它.仔细看:) (3认同)