C++预处理器条件参数

6
请注意C++03!任何C++11的解决方案对我都不好,但请仅供了解而发布。
我知道预处理器可以完成以下操作:
#define FOO 4
#if FOO == 4
    cout<<"hi"<<endl;
#endif

我需要的是:

#define BAR(X)\
    #if X == 4\
       cout<<"hi"<<endl;\
    #endif

main.cpp

BAR(4)

我不明白为什么所有必需的信息在预处理器时间不可用。
所以,请告诉我如何实现这种行为。
编辑1: 普通的if条件在我的情况下行不通,因为我还会做像这样的事情:
#define BAR(X)\
    #if X == 4\
       int poop;
    #elif
       double poop;
    #endif

1
你可能需要展示更多的代码来说明你想做什么。根据你给出的代码示例,我建议在这一部分使用模板而不是提供纯宏解决方案。取决于它会让其他事情变得多么复杂。 - Chris Beck
1
这不会起作用,你实际上是在尝试在运行时评估预处理器指令。 - Christian Kiewiet
1
@ChristianKiewiet 不正确。 在主函数中,调用的是“BAR(4)”,这显然是完全静态、类型自由的,在编译时就已知。 - Gulzar
2
从你第一次编辑的问题到现在,这样做不就可以了吗?template<int> struct BAR { typedef double T; }; template<> struct BAR<4> { typedef int T; }; BAR<4>::T intPoop; BAR<1>::T doublePoop; - Christian Kiewiet
2
@πάνταῥεῖ:什么?完全胡说八道。你又在错误地使用dupehammer了。我重新打开了这个问题,因为你是错的。BAR(i)BAR(4)是完全不同的用例。 - Lightness Races in Orbit
显示剩余8条评论
4个回答

4

正如您发现的那样,您无法以您尝试的方式完成此操作。宏展开根本没有内联条件评估,因此您需要创建多个宏。

然而,如果您只是想“优化”普通代码流程,可以依赖于编译器的优化。请考虑以下内容:

if (true) {
   std::cout << "Hi\n";
}

由此产生的程序将不会有任何条件检查,因为true总是真值。

同样地:

if (false) {
   std::cout << "Hi\n";
}

生成的程序不包含任何用于生成输出的代码,因为 false 永远不是真值。
同样地:
if (4 != 4) {
   std::cout << "Hi\n";
}

该程序仍不包含std::cout代码。
在许多情况下,您可以利用这一事实保持代码简洁并实现所需效果:
#define BAR(X) \
   if ((X) == 4) {
      std::cout << "hi" << std::endl;\
   }

当你写BAR(5)BAR(42)或者BAR(999)时,约束条件是if语句必须在这个位置是有效的。这种方法也很灵活,因为现在你可以使用运行时的值(例如BAR(i)),虽然在这种情况下条件无法在编译时折叠,但你也没有理由指望它能够做到。
在我的日志宏中,我采用了这种方法:当以LOG_LEVEL_DEBUG调用宏时,在发布版本中,该宏扩展为一个静态已知永远不匹配的条件。 这个想法是让编译器进行优化 你还需要考虑使用一些宏扩展技巧来避免后续else子句的问题

谢谢。这确实很好用于优化目的,但这不是这里的情况。我添加了适合我的需求的答案。 - Gulzar

4

如果条件参数的取值范围已知(最好是较小的),您可以使用预处理器来实现此操作。例如,假设参数只能有值0和1:

#define DOIT_0(X)
#define DOIT_1(X) X
#define CONCAT_(X, Y) X ## Y
#define MAYBE(X) CONCAT_(DOIT_, X)

#define BAR(X) MAYBE(X)(  cout<<"hi"<<endl;  )

#define YESNO 0
BAR(YESNO)

在coliru上实时演示

请注意传递给BAR的参数中未受保护的逗号。

对于相等性检查,再次针对小范围:

#define CONCAT3_(X,Y,Z) X ## Y ## Z
#define EQUAL_0_0(X) X
#define EQUAL_1_1(X) X
#define EQUAL_1_1(X) X
#define EQUAL_0_1(X)
#define EQUAL_0_2(X)
#define EQUAL_1_0(X)
#define EQUAL_1_2(X)
#define EQUAL_2_0(X)
#define EQUAL_2_1(X)
#define DO_IF_EQUAL(X, Y) CONCAT3_(EQUAL_, X, Y)

#define BAR(X) DO_IF_EQUAL(X, 2) ( std::cout << "hi\n"; )

这是一个非常好的解决方案,虽然不太美观也不够可扩展,但因为它回答了问题,所以点个赞。谢谢。 - Gulzar
@Gulzar,就 Boost.Preprocessor 内部实现而言,这基本上就是它的实现方式。只不过值的范围通常是 0..256,并且有一定的可配置性。 - Angew is no longer proud of SO

2
如果您使用Boost,您可以使用 Boost.Preprocessor来完成这个任务:
#define BAR(X) BOOST_PP_EXPR_IF(BOOST_PP_EQUAL(X, 4), cout << "hi" << endl;)

它是如何实现的? - Gulzar
@Gulzar 你不想知道。你真的不想知道;-)但如果你还是想知道,你可以自己去找——毕竟它是开源的。恐怕没有简单的方法来概括它——基本上它是预处理器黑魔法。 - Angew is no longer proud of SO

1
一些答案比其他答案更好。我接受的那个是由Christian Kiewiet在评论中发布的,但对于我的目的来说它是最准确的。以下是扩展版本:

useCases.h

enum UseCases{
    useCase1=0,
    useCase2,
    useCaseNumber//always last for iterations
} 

specializer.h

#include "useCases.h"
<template UseCases theCase>
struct StaticCase{
    //empty, thus accidents calling from this can't happen
}

//specialization
template<>
class StaticCase<UseCases::useCase1>{
     typedef int T;
     static foo(T arg){cout<<"case1";};
}


template<>
class StaticCase<UseCases::useCase2>{
     typedef double T;
     static foo(){cout<<"case2";};
}

现在,我能做到。
#define BAR1(useCase) StaticCase<useCase>::foo(); 

或者

#define BAR2(useCase) StaticCase<useCase>::T var;

和调用:

BAR1(UseCases::useCase1)//output - case1
BAR1(UseCases::useCase2)//output - case2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接