【问题标题】:Instanciating many templates in constexpr function在 constexpr 函数中实例化许多模板
【发布时间】:2018-07-15 10:23:49
【问题描述】:

我正在开发 GameBoy 模拟器。出于好奇,我想生成一个 constexpr Opcode 对象数组,其中包含 run() 函数指针以及其他一些有用的字段。

这是一个大致如何工作的示例:

#include <array>

struct CPU
{
    int some_state = 0;
};

void f(CPU& cpu)
{
    cpu.some_state = 42;
}

void g(CPU& cpu)
{
    cpu.some_state = 12;
}

struct Opcode
{
    using Runner = void(*)(CPU&);
    Runner run = [](CPU&) {};

    /* more fields here */
};

constexpr auto gen_opcodes()
{
    std::array<Opcode, 2> ret {};

    ret[0] = { f };
    ret[1] = { g };

    return ret;
}

constexpr auto opcodes = gen_opcodes();

int main()
{
    CPU cpu;
    opcodes[1].run(cpu);
}

数组为constexpr 的原因是我希望它能够被编译器很好地优化。据我了解,如果数组只有const,编译器将更难优化run() 调用,因为这些调用是单独调用的,编译器应该内联它们,即if (something == 0x00) { opcodes[0x00].run(blahblah); }

但是,这种方法的好处是一次生成一堆opcodes。想到了使用模板函数,当一些opcodes进入一个模式,我应该很容易一次生成几十个opcodes

但是虽然以下工作:

template<int i>
void f(CPU& cpu)
{
    cpu.some_state = i;
}

/* ... */

constexpr auto gen_opcodes()
{
    std::array<Opcode, 2> ret {};

    ret[0] = { f<3> };
    ret[1] = { f<2> };

    return ret;
}

这失败了:

constexpr auto gen_opcodes()
{
    std::array<Opcode, 100> ret {};

    for (int i = 30; i < 50; ++i)
    {
        ret[i] = { f<i> };
    }

    return ret;
}

注意:这显然是为了示例,在实践中略有不同。

原因是这个上下文中的i 不是一个常量表达式。

如果不手动编写这些函数模板,如何生成它们?否则是否有另一种“足够短”并且具有我之前所说的优势的解决方案?

我有一些想法,但似乎都不够:

  • 有状态的 lambda。但是,std::functionconstexpr 上下文中不起作用,因为它具有非平凡的析构函数,并且捕获 lambda 不能转换为函数指针。
  • 函数对象。我不知道如何将它们以这种方式存储在 constexpr 上下文中。

【问题讨论】:

    标签: c++ templates c++17 template-meta-programming constexpr


    【解决方案1】:

    原因是这个上下文中的 i 不是常量表达式

    所以诀窍是将i 转换为常量表达式。

    您在constexpr 函数中使用std::arrayoperator[],因此您使用的是C++17。

    在 C++17 中,您可以使用 std::index_sequence/std::make_index_sequence(从 C++14 开始提供)和模板折叠(从 C++17 开始)。

    所以你可以分两步写gen_opcodes()

    生成具有循环长度的std::index_sequence 的第一步

    constexpr auto gen_opcodes_1()
    { return gen_opcodes_2(std::make_index_sequence<20U>{}); }
    

    第二步,使用模板折叠来模拟for循环

    template <std::size_t ... Is>
    constexpr auto gen_opcodes_2 (std::index_sequence<Is...> const &)
     {
       std::array<Opcode, 100> ret {};
    
       ( (ret[30+Is] = { f<30+Is> }), ... );
    
       return ret;
     }
    

    以下是完整的编译示例

    #include <array>
    
    struct CPU
     { int some_state = 0; };
    
    template <int I>
    void f (CPU & cpu)
     { cpu.some_state = I; }
    
    struct Opcode
     { 
       using Runner = void(*)(CPU&);
       Runner run = [](CPU&) {};
     };
    
    template <std::size_t ... Is>
    constexpr auto gen_opcodes_2 (std::index_sequence<Is...> const &)
     {
       std::array<Opcode, 100> ret {};
    
       ( (ret[30+Is] = { f<30+Is> }), ... );
    
       return ret;
     }
    
    constexpr auto gen_opcodes_1()
     { return gen_opcodes_2(std::make_index_sequence<20U>{}); }
    
    constexpr auto opcodes = gen_opcodes_1();
    
    int main()
     {
       CPU cpu;
       opcodes[1].run(cpu);
     }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-02-09
      • 2017-03-28
      • 1970-01-01
      • 2013-12-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多