条件宏文本替换

时间:2011-12-16 00:31:18

标签: c++ macros

我的直觉是,这是不可能的,但我不是专家。 这就是我想做的事情:

#define KEY(i) #if (i == 0) KeyClassA(arg.fieldA)
               #elif (i == 1) KeyClassB(arg.fieldB)
//...
#endif

//inside a function with given arg
for(int i = 0; i < N; i++) {
    Data* data = array[i]->find(KEY(i));
    //do things with data
}

该代码显然比C ++代码更伪代码,我个人认为这样的东西不会编译,但我的意图应该是明确的:根据相应的数据结构为find函数提供一个临时的类对象数组。也就是说,数组中的每个数据结构都需要不同的密钥匹配类。

宏观文本替换似乎是尝试实现这一目标的“最聪明”的方式,但我显然欢迎任何其他想法让这样的事情发挥作用。

4 个答案:

答案 0 :(得分:4)

宏文本替换是您的问题的非解决方案,因为索引i仅在运行时才知道。在编译之前甚至开始处理宏。

如果在编译时不知道N,那么您将需要使用某些条件结构和可能的循环组合。如果KeyClass* es的数量是固定的(似乎是这种情况),您可能会做这样的事情:

void Foo(int N, Array& array, const Bar& arg)
{
    if(N > 3 || N <= 0) return;
    Data* data = array[0]->find(KeyClassA(arg.fieldA));
    // DoSomething(data);
    if(N == 1) return;
    data = array[1]->find(KeyClassB(arg.fieldB));
    // DoSomething(data);
    if(N == 2) return;
    data = array[2]->find(KeyClassC(arg.fieldC));
    // DoSomething(data);
}

将所有常用代码放在DoSomething()函数中(最好使用更好的函数名),这样就不会重复N的所有可能有效值。

如果在编译时知道N,则可以简单地展开循环。

void Foo(Array& array, const Bar& arg)
{
    Data* data = array[0]->find(KeyClassA(arg.fieldA));
    // DoSomething(data);
    data = array[1]->find(KeyClassB(arg.fieldB));
    // DoSomething(data);
    data = array[2]->find(KeyClassC(arg.fieldC));
    // DoSomething(data);
}

如果你不想自己展开循环,你甚至可以使用模板元编程,虽然这对你正在做的事情可能有点过分:

// The basic idea using template specializations
template<int i> 
struct GetKey; 

template<> 
struct GetKey<0> 
{ 
    KeyClassA From(const Bar& arg) { return KeyClassA(arg.fieldA); } 
}; 

template<> 
struct GetKey<1> 
{ 
    KeyClassB From(const Bar& arg) { return KeyClassB(arg.fieldB); } 
}; 

template<> 
struct GetKey<2> 
{ 
    KeyClassC From(const Bar& arg) { return KeyClassC(arg.fieldC); } 
}; 

template<int i, int N>
struct Iterate
{
    static void Body(Array& array, const Bar& arg)
    {
        Data* data = array[i]->find(GetKey<i>().From(arg));
        // DoSomething(data);
        Iterate<i+1, N>::Body(array, arg);
    }
};

template<int N>
struct Iterate<N, N>
{
    static void Body(Array& array, const Bar&) {}
};

void Foo(Array& array, const Bar& arg)
{
    Iterate<0, 3>::Body(array, arg);
}

答案 1 :(得分:2)

在这种情况下,无论如何都不可能,因为i不是编译时常量。 (不只是编译时常量,而是在预处理器阶段保持不变)

所以你必须使用普通的C ++ if语句来完成它。 (或开关)

基于我认为你想要做的事情,使用循环会使它变得比它需要的更复杂。只需将其全部写出来,就不需要任何循环或if语句。

array[0]->find(arg.fieldA);
array[1]->find(arg.fieldB);
...

(您似乎也没有对Data* data

做任何事情

编辑:有新信息。

在这种情况下,您可以将循环体放入函数调用中。像这样:

void loop_body(KeyClass &key, /* other parameters */ ){
    Data* data = array[0]->find(key);

    //  Rest of the body
}

只需为每个字段调用它。

loop_body(arg.fieldA);
loop_body(arg.fieldB);
...

答案 2 :(得分:0)

你试过#define KEY(i) i?KeyClassB(arg.fieldB):KeyClassA(arg.fieldA)

吗?

答案 3 :(得分:0)

#define KEY(i) ((i) == 0 ? KeyClassA(arg.fieldA) : \
                (i) == 1 ? KeyClassB(arg.fieldB) :\
                ...)

这是一个宏的事实真的不会给你带来什么;计算仍然必须在运行时完成,因为它取决于i的值。

这将作为内联函数更有意义。