2017-03-27 52 views
1

TL; DR:寻找一个预处理器宏来为所有预定义集合的组合生成所有if/else-if/else-error语句/模板参数的枚举。C++在运行时从编译时间已知设置/枚举中选择模板非类型参数


我有一个抽象类(Base)3-亚类(SubA<int a>SubB<int a>SubC<int a, int b>),所以无法初始化抽象类,但可以初始化子类。这些子类也有一个或两个非类型的模板参数。我有一个基准测试工具,它从数据库(子类运行,模板参数和参数/工作负载)拉出不同的基准测试配置。模板参数是确定性集合(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 */); 
+2

你可以用”在运行时不做与模板相关的任何事情,这是一个仅编译时的功能。 –

+1

你可以但它需要一个非常非常大的开关。编辑:取决于可能的模板实例化的数量,它可以用黑魔法(宏)来完成,但你可能想要找到另一个解决方案 – Nyashes

+1

但是我知道编译时所有可能的值,所以编译器必须有一种方法从一个集合中提取这些可能的值并为这些排列创建类?或者在预处理器宏中做一个体面的方法? –

回答

1

首先让我告诉你,如果你有另一种方式来解决你的问题,做到这一点。然后,如果您的目标模板实例化集有限且不太大,则可以将切换因子分解。这里是:

首先,我们需要能够创建对宏的引用并扩展它们,以便我们可以在我们的工厂创建后尽快写入一些通用的函数。这些宏是:

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

现在我们所要做的就是创建和借鉴es到我们的宏,并用它们来建立我们的工厂:

#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) { 
      ... 
     } 
} 
+0

嗯,绝对黑暗的魔法,我喜欢它。这正是我要找的,谢谢! –