重复使用相同模板函数的紧凑型switch语句

时间:2018-10-27 05:45:55

标签: c++ c++11 templates

我有以下带有相当大的switch语句的C ++代码(我正在使用C ++ 11编译器):

void function(std::size_t i, other_args)
{
   switch(i)
   {
      case 0:
         templatedfunction<0>(other_args);
         break;
      case 1:
         templatedfunction<1>(other_args);
         break;
      case 2:
         templatedfunction<2>(other_args);
         break;
      ...............
      //lots of other cases here
      ...............
      case 100:
         templatedfunction<100>(other_args);
         break;

   }  

}

其中templatedfunction被定义为

template<std::size_t T>
void templatedfunction(other_args){
   //some code here
}

这是用于描述一个简单概念的很多代码行(即,调用templatedfunction的值与其在其模板化参数中传递的变量i的值相同)。 C ++中有没有一种方法可以更紧凑地编写此代码?应该有一种方法可以更紧凑地实现此长switch语句。...使用templatedfunction<i>(other_args)不会编译,因为i是变量而不是编译时间常数。谢谢。

4 个答案:

答案 0 :(得分:5)

非递归方法是创建函数指针数组,然后根据索引选择一个。

template<std::size_t... Is>
void function_impl(std::index_sequence<Is...>, std::size_t i)
{
    using func_t = void(*)();
    static constexpr std::array<func_t, sizeof...(Is)> fnc_arr = {
        (&templatedfunction<Is>)...
    };

    fnc_arr[i]();
}

void function(std::size_t i)
{
    function_impl(std::make_index_sequence<100>(), i);
}

看到它运作here。 请注意,std::index_sequence是C ++ 14,但可以在C ++ 11中轻松实现。

编辑:

这是index_sequence的简单实现。请注意,这是非常差劲的实现,因为它是递归的,并且深度为O(N),所以它不会让您make_index_sequence<5000>进行谷歌更好的实现。而且,它只是index而不是integer序列。

template<std::size_t... Is>
struct index_sequence
{
    using type = std::size_t;

    static constexpr std::size_t size() noexcept
    {
        return sizeof...(Is);
    }
};

namespace detail
{
    template<std::size_t N, typename Seq>
    struct append;

    template<std::size_t N, std::size_t... Is>
    struct append<N, index_sequence<Is...>>
    {
        using type = index_sequence<Is..., N>;
    };


    template<std::size_t N>
    struct make_integer_seq
    {
        using type = typename append<N, typename make_integer_seq<N - 1>::type>::type;
    };

    template<>
    struct make_integer_seq<0>
    {
        using type = index_sequence<0>;
    };
}

template<std::size_t N>
using make_integer_sequence = typename detail::make_integer_seq<N - 1>::type;

答案 1 :(得分:3)

您可以执行以下操作:

$results = Category::selectRaw('categories.category_name, categories.slug,  COUNT(post_categories.post_id) AS `post_count`')
            ->join('post_categories', 'categories.id', '=', 'post_categories.category_id')
            ->join('posts', 'posts.id', '=', 'post_categories.post_id')
            ->whereRaw('categories.publish = 1 AND posts.publish = 1')
            ->groupBy('category_name')
            ->orderBy('post_count', 'DESC')
            ->limit(5)
            ->get();

优化器很可能会将所有这些递归调用转换为一堆比较和跳转汇编指令。一个简单的例子是here

(在上面的代码中,我假设您的template<std::size_t> struct Index_t { }; template<std::size_t i, class... Ts> void function_impl(std::size_t index, Index_t<i>, const Ts&... ts) { if (index == i) templatedfunction<i>(ts...); else function_impl(index, Index_t<i + 1>{}, ts...); } template<class... Ts> void function_impl(std::size_t, Index_t<101>, const Ts&...) { } template<class... Ts> void function(std::size_t index, const Ts&... ts) { function_impl(index, Index_t<0>{}, ts...); } 案例没有落空。)

答案 2 :(得分:3)

我喜欢Evg的解决方案(+1),但它是线性的,请执行很多模板递归操作,这些递归操作可能会超出递归限制(也许优化程序可以避免这种情况,但我不喜欢依赖优化程序)。

因此,我提出了Evg解决方案的对数变体:我们不能遍历单个索引,而只能遍历代表索引间隔的几个索引。然后,我们可以根据i的值将这个间隔二等分。

我的意思是:给出以下function()

void function (std::size_t i)
 {
   if ( i > 100 )
      std::cout << i << " is out of range" << std::endl;
   else
      function_impl<0, 101>(i); // as a switch from 0 to 100 (included)
 }

我们可以如下编写function_impl()函数

template <std::size_t L1, std::size_t L2>
void function_impl (std::size_t i)
 {
   if ( L1 == i )
      templatedFunction<L1>();
   else
    {
      constexpr auto LM { (L1 + L2) >> 1 }; 

      if ( i < LM )
         function_impl<L1, LM>(i);
      else
         function_impl<LM, L2>(i);
    }
 }

这大大减少了递归调用的数量。

以下是完整的编译示例

#include <iostream>

template <std::size_t I>
void templatedFunction ()
 { std::cout << I << std::endl; }

template <std::size_t L1, std::size_t L2>
void function_impl (std::size_t i)
 {
   if ( L1 == i )
      templatedFunction<L1>();
   else
    {
      constexpr auto LM { (L1 + L2) >> 1 }; 

      if ( i < LM )
         function_impl<L1, LM>(i);
      else
         function_impl<LM, L2>(i);
    }

 }

void function (std::size_t i)
 {
   if ( i > 100 )
      std::cout << i << " is out of range" << std::endl;
   else
      function_impl<0, 101>(i);
 }

int main ()
 {
   for ( auto ui = 0u ; ui < 102u ; ++ui )
      function(ui);
 }

答案 3 :(得分:1)

除非可以更改templatedFunction,以使i是一个参数而不是参数,否则应该做一些棘手的事情。

例如,预处理。关于使用C ++预处理器生成大型代码序列(例如,this one),已经有很多说法。在这里,为了节省输入内容,如果您在其他地方不需要复杂的宏代数,则可以使用简单的二进制序列:

#define CASE_2(n, CASE) CASE(n); CASE(n + 1)
#define CASE_4(n, CASE) CASE_2(n, CASE); CASE_2(n + 2, CASE)
#define CASE_8(n, CASE) CASE_4(n, CASE); CASE_4(n + 4, CASE)
#define CASE_16(n, CASE) CASE_8(n, CASE); CASE_8(n + 8, CASE)
#define CASE_32(n, CASE) CASE_16(n, CASE); CASE_16(n + 16, CASE)
#define CASE_64(n, CASE) CASE_32(n, CASE); CASE_32(n + 32, CASE)

#define SINGLE_CASE(n) case n: templatedFunction<n>(other_args)

switch(i) {
    CASE_64(0, SINGLE_CASE);
    CASE_32(64, SINGLE_CASE);
    CASE_4(96, SINGLE_CASE);
    SINGLE_CASE(100);
}