使用#ifdefs和#define可选择将函数调用转换为注释

时间:2009-02-13 18:01:24

标签: c++ c c-preprocessor conditional-compilation

是否可以做这样的事情

#ifdef SOMETHING
#define foo //
#else
#define foo MyFunction
#endif

这个想法是,如果定义了SOMETHING,那么对foo(...)的调用将成为注释(或者不会被评估或编译的内容),否则它将成为对MyFunction的调用。

我见过__noop,但我不相信我可以使用它。

EDIT(S):

我认为我不能在这里使用宏,因为MyFunction采用可变数量的参数。

另外,我想这样做,以便不评估参数! (所以做一些事情,比如评论MyFunction的主体并没有真正给我我需要的东西,因为参数仍将被评估)

11 个答案:

答案 0 :(得分:24)

试试这个:

#ifdef SOMETHING
#define foo(x)
#else
#define foo(x) MyFunction(x)
#endif

如果你的函数有多个参数,那么:

#ifdef SOMETHING
#define foo(x,y,z)
#else
#define foo(x,y,z) MyFunction(x,y,z)
#endif

如果你的函数有可变数量的参数,那么你的编译器可能支持所谓的“可变参数宏”,如下所示:

#ifdef SOMETHING
#define foo(...)
#else
#define foo(...) MyFunction(__VA_ARGS__)
#endif

我在实践中看到这种事情的原因是从发布版本中删除了日志记录功能。不过,另请参阅Separate 'debug' and 'release' builds?,其中有人质疑您是否应该


或者,Jonathan对此回答的评论建议不要将函数调用重新定义为无效,而是建议执行以下操作:

#ifdef SOMETHING
#define foo(...) do { if (false) MyFunction(__VA_ARGS__) } while (0)
#else
#define foo(...) do { if (true) MyFunction(__VA_ARGS__) } while (0)
#endif

这样做的原因是函数调用总是被编译(因此它不会留下诸如对已删除变量的引用等无偿错误),但只在需要时调用:参见Kernighan&派克The Practice of Programming以及Goddard Space Flight Center programming standards

来自debug.h文件(源自1990年,因此不使用__VA_ARGS__):

/*
** Usage:  TRACE((level, fmt, ...))
** "level" is the debugging level which must be operational for the output
** to appear. "fmt" is a printf format string. "..." is whatever extra
** arguments fmt requires (possibly nothing).
** The non-debug macro means that the code is validated but never called.
** -- See chapter 8 of 'The Practice of Programming', by Kernighan and Pike.
*/
#ifdef DEBUG
#define TRACE(x)    db_print x
#else
#define TRACE(x)    do { if (0) db_print x; } while (0)
#endif /* DEBUG */

使用C99,不再需要双括号技巧。除非C89兼容性存在问题,否则新代码不应使用它。

答案 1 :(得分:5)

也许更简单的方法是有条件地省略函数的主体?

void MyFunction() {
#ifndef SOMETHING
    <body of function>
#endif
}

除非您特别不希望进行函数调用,否则这似乎是实现目标的一种干净方式。

答案 2 :(得分:3)

不幸的是,当前的C ++版本不支持可变参数宏。

但是,你可以这样做:

#ifdef SOMETHING
#define foo
#else
#define foo(args) MyFunction args
#endif

// you call it with double parens:
foo((a, b, c));

答案 3 :(得分:2)

如果您不希望调用foo,则将其定义为:

void foo() {}

任何对foo()的调用都应该是优化方式。

答案 4 :(得分:2)

这些方面的内容如何:

#ifdef NDEBUG
#define DEBUG(STATEMENT) ((void)0)
#else
#define DEBUG(STATEMENT) (STATEMENT)
#endif

您可以像这样使用它来记录调试消息:

DEBUG(puts("compile with -DNDEBUG and I'm gone"));

格式化输出的非泛型版本以及使用C99可变参数宏和__func__标识符的其他调试信息可能如下所示:

#ifdef NDEBUG
#define Dprintf(FORMAT, ...) ((void)0)
#define Dputs(MSG) ((void)0)
#else
#define Dprintf(FORMAT, ...) \
    fprintf(stderr, "%s() in %s, line %i: " FORMAT "\n", \
        __func__, __FILE__, __LINE__, __VA_ARGS__)
#define Dputs(MSG) Dprintf("%s", MSG)
#endif

以下是您使用这些宏的方法:

Dprintf("count = %i", count);
Dputs("checkpoint passed");

答案 5 :(得分:2)

可能,您不希望按照建议执行简单的“代码删除”, 因为你的来电者会期待副作用 要发生的争论。这是一些麻烦的来电片段 应该让你思考:

// pre/post increment inside method call:
MyFunction(i++); 

// Function call (with side effects) used as method argument: 
MyFunction( StoreNewUsernameIntoDatabase(username) ); 

如果您要通过简单地说:

来禁用MyFunction
#define MyFunction(x) 
然后,呼叫者所期待的副作用会消失, 并且他们的代码会破坏,并且很难调试。我喜欢 上面的“sizeof”建议,我也喜欢这个建议 通过#ifdef来禁用MyFunction()的主体,尽管这意味着 所有调用者都获得相同版本的MyFunction()。从你的 问题陈述,我认为实际上并不是你想要的。

如果你真的需要通过预处理器定义来禁用MyFunction() 每个源文件的基础,然后我会这样做:

#ifdef SOMETHING 
#define MyFunction(x) NoOp_MyFunction(x) 

int NoOp_MyFunction(x) { } 
#endif 

你甚至可以在里面包含NoOp_MyFunction()的实现 来源&amp; MyFunction()的标题。你也有灵活性 在NoOp_MyFunction()中添加额外的日志记录或调试信息 好。

答案 6 :(得分:1)

不,C和C ++标准规定你不能#define某些东西作为评论,所以

#define foo //

无效。

答案 7 :(得分:1)

#ifdef SOMETHING
#define foo sizeof
#else
#define foo MyFunction
#endif

我假设foo是printf样式函数?无论如何,这不适用于零参数功能,但如果是这种情况,您就已经知道该怎么做了。如果你真的想成为肛门,你可以使用(void)sizeof,但这可能是不必要的。

答案 8 :(得分:1)

我有点不愿意发布这个答案,因为它使用宏hackery可能成为问题的根源。但是 - if 对你想要消失的函数的调用总是在一个语句中单独使用(即,它们永远不会是一个更大的表达式的一部分),那么类似下面的东西可以工作(和它处理varargs):

#ifdef SOMETHING
#define foo (1) ? ((void) 0) : (void)
#else
#define foo MyFunction
#endif

所以如果你有代码行:

foo( "this is a %s - a++ is %d\n", "test", a++);

它将在预处理步骤之后结束:

MyFunction( "this is a %s - a++ is %d\n", "test", a++);

(1) ? ((void) 0) : (void)( "this is a %s - a++ is %d\n", "test", a++);

将伪函数的参数列表转换为由逗号运算符分隔的一组表达式,这些表达式永远不会被计算,因为条件总是返回((void) 0)结果。

这与ChriSW和Jonathan Leffler建议的内容很接近:

#ifdef SOMETHING
#define foo if (0) MyFunction
#else
#define foo if (1) MyFunction
#endif

这略有不同,因为它不需要编译器支持可变参数宏(__VA_ARGS__)。

我认为这对于消除调试跟踪函数调用很有用,调试跟踪函数调用通常永远不会组合成更大的表达式,但除此之外,我认为这是一种危险的技术。

注意问题的可能性 - 特别是如果调用中的参数产生副作用(这是宏的一般问题 - 不仅仅是这个hack)。在示例中,只有在构建中定义a++时才会评估SOMETHING,否则不会。因此,如果调用后的代码取决于要增加的a的值,则其中一个构建会出现错误。

答案 9 :(得分:0)

如果我没记错的话,你应该能够#define你的宏“没有”,这将导致编译器忽略该调用

#define foo()

foo();    // this will be ignored

答案 10 :(得分:0)

使用

围绕每个myFunction调用怎么样?
#ifdef SOMETHING
myFunction(...);
#endif