C++ 预处理器条件参数
C++ preprocessor conditional parameter
请注意C++03!任何 C++11 解决方案都不适合我,但 post 只是为了知识的缘故。
我知道预处理器可以做这样的事情:
#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
如您所见,您无法按照您尝试过的方式进行操作。宏扩展根本没有内联条件评估,因此您必须创建多个宏。
但是,如果您只是想 "optimise" 正常的代码流,您可以依赖编译器的优化。考虑一下:
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。
如果你可以使用 Boost,你可以用 Boost.Preprocessor:
#define BAR(X) BOOST_PP_EXPR_IF(BOOST_PP_EQUAL(X, 4), cout << "hi" << endl;)
如果条件参数的值域是众所周知的(最好是小的),您可以使用预处理器执行此操作。例如,假设参数可以 only 具有值 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)
注意 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"; )
这里的一些答案比其他答案好。
我接受的那个是 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
请注意C++03!任何 C++11 解决方案都不适合我,但 post 只是为了知识的缘故。
我知道预处理器可以做这样的事情:
#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
如您所见,您无法按照您尝试过的方式进行操作。宏扩展根本没有内联条件评估,因此您必须创建多个宏。
但是,如果您只是想 "optimise" 正常的代码流,您可以依赖编译器的优化。考虑一下:
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。
如果你可以使用 Boost,你可以用 Boost.Preprocessor:
#define BAR(X) BOOST_PP_EXPR_IF(BOOST_PP_EQUAL(X, 4), cout << "hi" << endl;)
如果条件参数的值域是众所周知的(最好是小的),您可以使用预处理器执行此操作。例如,假设参数可以 only 具有值 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)
注意 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"; )
这里的一些答案比其他答案好。 我接受的那个是 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