16

有没有可能做这样的事情

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

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

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

编辑:

我不认为我可以在这里真正使用宏,因为 MyFunction 接受可变数量的参数。

另外,我想让它不评估参数!(因此,像注释掉 MyFunction 的主体之类的事情并不能真正给我我需要的东西,因为仍然会评估参数)

4

11 回答 11

25

试试这个:

#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

我在实践中看到这种事情的原因是为了摆脱发布版本中的日志记录功能。但是,另请参阅单独的“调试”和“发布”版本?人们质疑你是否应该有不同的构建。


或者,乔纳森对此答案的评论建议不要将函数调用重新定义为空,而是建议执行以下操作:

#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 & Pike The Practice of ProgrammingGoddard航天飞行中心编程标准

从 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 兼容性有问题,否则新代码不应使用它。

于 2009-02-13T18:04:29.300 回答
5

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

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

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

于 2009-02-13T18:04:57.287 回答
3

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

但是,您可以这样做:

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

// you call it with double parens:
foo((a, b, c));
于 2009-02-13T19:08:34.483 回答
2

如果在您不想调用 foo 的情况下,您将其定义为:

void foo() {}

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

于 2009-02-13T19:01:47.120 回答
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");
于 2009-02-13T19:08:18.150 回答
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 

您甚至可以在 MyFunction() 的源代码和标头中包含 NoOp_MyFunction() 的实现。您还可以灵活地在 NoOp_MyFunction() 中添加额外的日志记录或调试信息。

于 2009-02-13T22:41:17.730 回答
1

不,C 和 C++ 标准说你不能 #define 某事作为评论,所以

#define foo //

不会工作。

于 2009-02-13T19:09:14.950 回答
1
#ifdef SOMETHING
#define foo sizeof
#else
#define foo MyFunction
#endif

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

于 2009-02-13T19:12:08.717 回答
1

我有点不愿意发布这个答案,因为它使用宏黑客可能成为问题的根源。但是 -如果对您想要消失的函数的调用始终在语句中单独使用(即,它们永远不是更大表达式的一部分),那么类似以下的东西可以工作(并且它处理可变参数):

#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要递增的值,则其中一个构建存在错误。

于 2009-02-14T08:29:33.900 回答
0

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

#define foo()

foo();    // this will be ignored
于 2009-02-13T18:04:55.900 回答
0

如何围绕每个调用 myFunction

#ifdef SOMETHING
myFunction(...);
#endif

?

于 2009-02-13T20:42:30.217 回答