【发布时间】: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::function在constexpr上下文中不起作用,因为它具有非平凡的析构函数,并且捕获 lambda 不能转换为函数指针。 - 函数对象。我不知道如何将它们以这种方式存储在
constexpr上下文中。
【问题讨论】:
标签: c++ templates c++17 template-meta-programming constexpr