gpt4 book ai didi

C++ 在运行时从编译时已知集/枚举中选择模板非类型参数

转载 作者:行者123 更新时间:2023-11-30 05:15:32 25 4
gpt4 key购买 nike

TL;DR: 寻找预处理器宏来生成所有 if/else-if/else-error模板参数的预定义集/枚举的所有组合的语句。


我有一个抽象类 ( SubA<int a> ) 的 3 个子类 ( SubB<int a>SubC<int a, int b>Base ),所以我无法初始化抽象类,但可以初始化子类。这些子类也有一个或两个非类型模板参数。

class Base {...};  // abstract
class SubA<int a> : public Base {...};
class SubB<int a> : public Base {...};
class SubC<int a, int b> : public Base {...};

我有一个基准测试工具,它从数据库中提取不同的基准配置(要运行的子类、模板参数和参数/工作负载)。模板参数是最终集(a in {256, 512, 1024, 2048, 4096, 8192, 16384}b in {1, 2, 3})。

我希望能够拥有一个 Base使用正确的模板参数将对象实例化为子类,而无需 if/else if所有的可能性/组合。在 C++ 中是否有一种干净的方法可以使用枚举、数组甚至预处理器宏来执行此操作?

一个冗长的方法是有很多 if-else 语句,但我想要一个更简洁的解决方案,以便尽可能地从枚举、集合或数组中提取值。肯定有生成组合的预处理器方法,或者从枚举中选择的方法(因此编译器创建所有枚举组合的类)?

Base *base = nullptr;

if (sub == "SubA") {
if (a == 512) {
if (b == 1) {
base = new SubA<512, 1>();
} else if (b == 2) {
base = new SubA<512, 2>();
} else if (b == 3) {
base = new SubA<512, 3>();
}
} else if (a == 1024) {
// ...
}
} else if (sub == "SubB") {
// ...
} else if (sub == "SubC") {
// ...
}

if (base == nullptr) {
throw std::exception();
}

作为进一步的解释,这里有一个用 JS 编写的等效解决方案:

class Base = {...};
function SubAFactory(a, b) = {return class SubA {... definition ...}};
function SubBFactory(a, b) = {return class SubB {... definition ...}};
function SubCFactory(a, b) = {return class SubC {... definition ...}};

const SubFactories = {
SubA: SubAFactory,
SubB: SubBFactory,
SubC: SubCFactory
};

function BaseFactory(sub, a, b) {
// NOTE: an if-else between subclasses would also be fine
// as long as the template args are "dynamic".
return SubFactories[sub](a, b);
}

// retrieved from db at runtime, a and b values will
// always be part of a finite set known at compile time
const dbResult = {sub: 'SubA', a: 2048, b: 2};
const sub = dbResult.sub;
const a = dbResult.a;
const b = dbResult.b;

const base = new BaseFactory(sub, a, b)(/* class constructor args */);

最佳答案

首先让我告诉你,如果你有另一种方法来解决你的问题,那就去做吧。然后,如果您的模板实例化目标集是有限的且不太大,则有一种方法可以分解您的切换。方法如下:

首先,我们需要能够创建对宏的引用并扩展它们,这样我们就可以编写一些通用的东西,我们可以在创建工厂后立即取消定义。这些宏是:

//force another dereferencing cycle
# define EXPAND(...) __VA_ARGS__
//black magic
# define EMPTY(...)

//dereference a macro reference on expansion
# define DEFER(...) __VA_ARGS__ EMPTY()

然后是真正的部分:构造一个开关,在每个运行时情况下使用代码转发编译时类型:

#define makeCase(_value, appendTo, ...) case _value: \
DEFER(appendTo)()(__VA_ARGS__, _value) \
break;

#define makeRuntimeSwitch(_runtimeVal, appendTo, ...) switch( _runtimeVal) \
{ \
makeCase(1, appendTo, __VA_ARGS__) \
makeCase(2, appendTo, __VA_ARGS__) \
makeCase(3, appendTo, __VA_ARGS__) \
makeCase(4, appendTo, __VA_ARGS__) \
makeCase(5, appendTo, __VA_ARGS__) \
}

这会将我们的模板参数附加到 VA_ARGS,直到我们拥有所有这些参数并且能够使用另一个宏来使用它们:

#define makeRuntimeConsume(_p1, _p2) return new templatedStuff<_p1, _p2>();

现在我们所要做的就是创建对宏的引用并使用它们来构建我们的工厂:

#define makeRuntimeConsumeId() makeRuntimeConsume
#define makeRuntimeSwitchId() makeRuntimeSwitch
baseStuff* makeStuff(int a, int b)
{
EXPAND(EXPAND(makeRuntimeSwitch( a, makeRuntimeSwitchId, b, makeRuntimeConsumeId)));
}
//undef all macro, you don't need them anymore

并恶搞由宏为您生成的丑陋开关。这可以打开任何运行时并返回任何编译时间(我的示例打开 n 枚举以作为类型转发到(可变)模板化方法)

生成的代码如下所示:

switch (a) {
case 1:
switch (b) {
case 1:
return new templatedStuff<1, 1>();
case 2:
...
}
case 2:
switch (b) {
...
}
}

关于C++ 在运行时从编译时已知集/枚举中选择模板非类型参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43046021/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com