【问题标题】:C++ preprocessor conditional parameterC++ 预处理器条件参数
【发布时间】:2015-08-24 10:11:07
【问题描述】:

请注意 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

【问题讨论】:

  • 您可能想要显示更多代码/您正在尝试做的事情。根据您提供的代码示例,我想我可能会建议为此部分使用模板而不是提供纯宏解决方案?取决于它会使其他事情变得多么复杂。
  • 这不起作用,您实际上是在尝试在运行时评估预处理器指令。
  • @ChristianKiewiet 不正确。在 main 中,调用是“BAR(4)”,它在编译时间之前非常明显地完全是静态的 - 无类型的。
  • 从您的第一次编辑到您的问题,这不是诀窍吗? template&lt;int&gt; struct BAR { typedef double T; }; template&lt;&gt; struct BAR&lt;4&gt; { typedef int T; }; BAR&lt;4&gt;::T intPoop; BAR&lt;1&gt;::T doublePoop;
  • @πάνταῥεῖ:什么?完全废话。在这里,你又在欺骗自己再次。我重新提出了这个问题,因为你完全错了。 BAR(i)BAR(4) 是完全不同的用例。

标签: c++ if-statement c-preprocessor c++03


【解决方案1】:

正如您所发现的,您无法以您尝试过的方式执行此操作。宏扩展根本没有内联条件评估,因此您必须创建多个宏。

但是,如果您只是想“优化”正常的代码流,则可以依赖编译器的优化。考虑一下:

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;\
   }

当然,这里的限制是if 语句必须在您编写BAR(5)BAR(42)BAR(999) 的地方有效。

这也是灵活的,因为现在您可以使用运行时值(如BAR(i)),虽然条件不能再在编译时折叠,但在这种情况下,您没有理由期望无论如何。

我在我的日志记录宏中采用了这种方法:当调用 LOG_LEVEL_DEBUG 时,宏会扩展为在发布版本中静态已知永远不会匹配的条件。

这个想法是让编译器进行优化

您还可以考虑使用a little macro expansion trick to avoid problems with subsequent else clauses

【讨论】:

  • 谢谢。这对于优化目的确实很好,但这里不是这种情况。我添加了适合我需要的答案。
【解决方案2】:

如果条件参数的值域是众所周知的(并且最好是小的),您可以使用预处理器执行此操作。例如,假设参数只能有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)

Live on 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"; )

【讨论】:

  • 这是一个非常好的但不是很漂亮也不是可扩展的解决方案,但是 +1 因为它回答了这个问题。谢谢
  • @Gulzar FWIW,这就是 Boost.Preprocessor 内部实现的方式。除了值的范围通常是 0..256,并且有些可配置。
【解决方案3】:

如果您可以使用 Boost,则可以使用 Boost.Preprocessor

#define BAR(X) BOOST_PP_EXPR_IF(BOOST_PP_EQUAL(X, 4), cout << "hi" << endl;)

【讨论】:

  • 那里是如何实现的?
  • @Gulzar 你不想知道。你真的不想知道 ;-) 但如果你仍然想知道,你可以自己寻找——毕竟它是开源的。恐怕没有简单的方法来总结它 - 它几乎是预处理器黑魔法。
【解决方案4】:

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

用例.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

【讨论】:

    猜你喜欢
    • 2016-03-30
    • 2014-04-02
    • 1970-01-01
    • 2015-09-13
    • 1970-01-01
    • 1970-01-01
    • 2011-01-08
    • 1970-01-01
    相关资源
    最近更新 更多