条件宏文本替换

Conditional macro text replacement

本文关键字:文本替换 条件      更新时间:2023-10-16

我的直觉是这是不可能的,但我不是专家。以下是我想做的:

#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 函数提供一个临时类对象。也就是说,数组中的每个数据结构都需要不同的键匹配类。

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

文本替换不能解决您的问题,因为索引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);
}
在这种情况下,

无论如何都是不可能的,因为i不是编译时常量。(不仅仅是编译时常量,而是预处理器阶段的常量(

因此,您必须使用正常的 if 语句C++来执行此操作。(或开关(

根据我认为您要做的事情,使用循环会使它比需要的更复杂。只需全部写出来,就不需要任何循环或 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);
...

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

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

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

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