#define TRACE( allargs) do { printf allargs; } while ( 0)
...
TRACE(( "%s %s\n", "Help", "me"));
One of my favorite tricks is a way to pass variable number of arguments to macros, to be later used in calling printf-like functions for example. To do this, I specify that the macro has only one parameter and use it in the body of the macro without (), but pass all the parameters to the macro in (( and )), so the list looks like a single argument. For example,
#define TRACE( allargs) do { printf allargs; } while ( 0)
...
TRACE(( "%s %s\n", "Help", "me"));
Struct literals with default values (that are not zero), using C99 variadic macros
struct Example {
int from;
int to;
const char *name;
}
#define EXAMPLE(...) ((struct Example){.from=0, .to=INT_MAX, .name="", __VA_ARGS__})
using EXAMPLE(.name="test") uses the default values, except for the explicit override of name. This shadowing with later mentions of the same member is well-defined in the standard.
/*! \brief Compile-time assertion.
*
* Note that the cassert() macro generates no code, and hence need not
* be restricted to debug builds. It does have the side-effect of
* declaring a type name with typedef. For this reason, a unique
* number or string of legal identifier characters must be included
* with each invocation to avoid the attempt to redeclare a type.
*
* A failed assertion will attempt to define a type that is an array
* of -1 integers, which will throw an error in any standards
* compliant compiler. The exact error is implementation defined, but
* since the defined type name includes the string "ASSERTION" it
* should trigger curiosity enough to lead the user to the assertion
* itself.
*
* Because a typedef is used, cassert() may be used inside a function,
* class or struct definition as well as at file scope.
*/
#define cassert(x,i) typedef int ASSERTION_##i[(x)?1:-1]
The do{}while(0) idiom is there to avoid issues that might result from accidentally making a usage of D(...) the only content of a conditional or loop. You don't want code like this to mean the wrong thing, after all:
If I could make that case throw an error, I would, but the preprocessor would have to be a full compiler itself to tell that the D() macro was the sole content of a loop body.
I'm also a big fan of compile-time assertions. My formulation is slightly different, but has no real advantages over others I've seen. The key is to form a uniquely named typedef that throws an error if the asserted condition is false, and not otherwise. In cassert.h we have:
/*! \brief Compile-time assertion.
*
* Note that the cassert() macro generates no code, and hence need not
* be restricted to debug builds. It does have the side-effect of
* declaring a type name with typedef. For this reason, a unique
* number or string of legal identifier characters must be included
* with each invocation to avoid the attempt to redeclare a type.
*
* A failed assertion will attempt to define a type that is an array
* of -1 integers, which will throw an error in any standards
* compliant compiler. The exact error is implementation defined, but
* since the defined type name includes the string "ASSERTION" it
* should trigger curiosity enough to lead the user to the assertion
* itself.
*
* Because a typedef is used, cassert() may be used inside a function,
* class or struct definition as well as at file scope.
*/
#define cassert(x,i) typedef int ASSERTION_##i[(x)?1:-1]
And in some source file, anywhere a typedef would be legal:
The resulting error message is often obscure, but will contain the fragment of identifier enabling the offending line to be discovered by brute force.
I've been guilty of using the preprocessor in places where writing a code generation utility might have been the preferred answer, much like the code in another answer that generated lots of boiler-plate based on the unique parts of an enum member's name. That is especially handy when writing a lot of message-dispatch glue to be compiled in C.
int value = -1;
char *str = getstr();
#define TEST( _v ) \
if (!strcmp(# _v, str)) \
value = k ## _v ## Enum
TEST( One );
TEST( Two );
TEST( Three );
TEST( Four );
One can simplify repetitive things for ie. enum lists
...and later do a switch case over a structured way
#define TEST( _v ) \
case k ## _v ## Enum: \
CallFunction ## _v(); \
break;
switch (c) {
TEST( One );
TEST( Two );
TEST( Three );
TEST( Four );
}
Note: Sure this could be done with a function pointer array but this opens for a little more flexibilities to add parameters and also use the string expansions with the single hash.
...or to test on strings to get the right enum value
int value = -1;
char *str = getstr();
#define TEST( _v ) \
if (!strcmp(# _v, str)) \
value = k ## _v ## Enum
TEST( One );
TEST( Two );
TEST( Three );
TEST( Four );
In this example defines three functions (bits_str_uchar(), bits_str_uint(), bits_str_int()) that handle three different data types (unsigned char, unsigned int, int). However, all return a string that contains the bits of the value passed.
When you implement a COM server, you have to take care of all exceptions your code could possibly throw - letting an exception through COM method boundary will often crash the calling application.
Methods brackets are useful for this. There's an opening bracket which is a macro containing "try" and a closing bracket that contains a set of "catch"es, wrapping of exceptions into ErrorInfo and producing HRESULTs.
BOOST_BINARY 宏执行一些 clevel pre-处理器技巧使 C++ 能够以二进制表示数字常量。 但其范围仅限于 0-255。
The BOOST_BINARY macro performs some clevel pre-processor trickery to give C++ the ability to express numeric constants in binary. It is limited to 0-255 however.
if you want to get "message from debug!" message, compile with:
gcc -D DEBUG foo.c
Otherwise, nothing happens. Gcc is a very smart compiler. If DEBUG isn't defined, the generated if(0) (dead code) will be removed from your code with some optimizations on.
You still can do more:
debug
{
pritnf("I'm in debug mode!\n");
}
else
{
printf("I'm not in debug mode\n");
}
// could evaluate at compile time if __builtin_sin gets
// special treatment by the compiler
#define sin(x) __builtin_sin(x)
// parentheses avoid substitution by the macro
double (sin)(double arg) {
return sin(arg); // uses the macro
}
int main() {
// uses the macro
printf("%f\n", sin(3.14));
// uses the function
double (*x)(double) = &sin;
// uses the function
printf("%f\n", (sin)(3.14));
}
In C, it's common to define macros that do some stuff getting the verbatim argument, and at the same time define functions to be able to get the address of it transparently.
// could evaluate at compile time if __builtin_sin gets
// special treatment by the compiler
#define sin(x) __builtin_sin(x)
// parentheses avoid substitution by the macro
double (sin)(double arg) {
return sin(arg); // uses the macro
}
int main() {
// uses the macro
printf("%f\n", sin(3.14));
// uses the function
double (*x)(double) = &sin;
// uses the function
printf("%f\n", (sin)(3.14));
}
/** 1st arg is type , 2nd is field name , 3rd is initial value , 4th is help */
GENX( int , "y" , 1 , "number of ..." );
GENX( float , "z" , 6.3 , "this value sets ..." );
GENX( std::string , "name" , "myname" , "name of ..." );
然后他可以在不同的环境中使用它为每个 #include 定义它的地方通常具有不同的定义:
class X
{
public :
void setDefaults()
{
#define GENX( type , member , value , help )\
member = value ;
#include "gen.x"
#undef GENX
}
void help( std::ostream & o )
{
#define GENX( type , member , value , help )\
o << #member << " : " << help << '\n' ;
#include "gen.x"
#undef GENX
}
private :
#define GENX( type , member , value , help )\
type member ;
#include "gen.x"
#undef GENX
}
There is also the X Macro idiom which can be useful for DRY and simple code generation :
One defines in a header gen.x a kind of table using a not yet defined macro :
/** 1st arg is type , 2nd is field name , 3rd is initial value , 4th is help */
GENX( int , "y" , 1 , "number of ..." );
GENX( float , "z" , 6.3 , "this value sets ..." );
GENX( std::string , "name" , "myname" , "name of ..." );
Then he can use it in different places defining it for each #include with a usually different definition :
class X
{
public :
void setDefaults()
{
#define GENX( type , member , value , help )\
member = value ;
#include "gen.x"
#undef GENX
}
void help( std::ostream & o )
{
#define GENX( type , member , value , help )\
o << #member << " : " << help << '\n' ;
#include "gen.x"
#undef GENX
}
private :
#define GENX( type , member , value , help )\
type member ;
#include "gen.x"
#undef GENX
}
/* Use CONCATENATE_AGAIN to expand the arguments to CONCATENATE */
#define CONCATENATE( x,y) CONCATENATE_AGAIN(x,y)
#define CONCATENATE_AGAIN(x,y) x ## y
静态编译时断言。
例如:
#define CONCATENATE_4( a,b,c,d) CONCATENATE_4_AGAIN(a,b,c,d)
#define CONCATENATE_4_AGAIN(a,b,c,d) a ## b ## c ## d
/* Creates a typedef that's legal/illegal depending on EXPRESSION. *
* Note that IDENTIFIER_TEXT is limited to "[a-zA-Z0-9_]*". *
* (This may be replaced by static_assert() in future revisions of C++.) */
#define STATIC_ASSERT( EXPRESSION, IDENTIFIER_TEXT) \
typedef char CONCATENATE_4( static_assert____, IDENTIFIER_TEXT, \
____failed_at_line____, __LINE__ ) \
[ (EXPRESSION) ? 1 : -1 ]
/* Note: Windows may have __FUNCTION__. C99 defines __func__. */
#define CURRENT_CODE_LOCATION() \
CodeLocation( __PRETTY_FUNCTION__, __FILE__, __LINE__ )
随后被 MESSAGE/WARN/FAIL 宏用作方便的源位置打印机制。 例如:
#define WARN_IF_NAN(X) \
do \
{ \
if ( isnan(X) != 0 ) \
WARN( # X " is NaN (Floating Point NOT-A-NUMBER)" ); \
if ( isinf(X) != 0 ) \
WARN( # X " is INF (Floating Point INFINITY)" ); \
} while ( false )
The double-evaluation to expand the arguments trick: (E.g. Use the actual line number and not "__LINE__".)
/* Use CONCATENATE_AGAIN to expand the arguments to CONCATENATE */
#define CONCATENATE( x,y) CONCATENATE_AGAIN(x,y)
#define CONCATENATE_AGAIN(x,y) x ## y
Static compile-time assertions.
E.g.:
#define CONCATENATE_4( a,b,c,d) CONCATENATE_4_AGAIN(a,b,c,d)
#define CONCATENATE_4_AGAIN(a,b,c,d) a ## b ## c ## d
/* Creates a typedef that's legal/illegal depending on EXPRESSION. *
* Note that IDENTIFIER_TEXT is limited to "[a-zA-Z0-9_]*". *
* (This may be replaced by static_assert() in future revisions of C++.) */
#define STATIC_ASSERT( EXPRESSION, IDENTIFIER_TEXT) \
typedef char CONCATENATE_4( static_assert____, IDENTIFIER_TEXT, \
____failed_at_line____, __LINE__ ) \
[ (EXPRESSION) ? 1 : -1 ]
Initializing an instance of class CodeLocation: (Storing File/Line/Function from the point of invocation -- this can *ONLY* be done with a macro or by directly accessing the __FILE__/__LINE__/etc macros at the source point.)
/* Note: Windows may have __FUNCTION__. C99 defines __func__. */
#define CURRENT_CODE_LOCATION() \
CodeLocation( __PRETTY_FUNCTION__, __FILE__, __LINE__ )
Subsequently used by MESSAGE/WARN/FAIL macros as a convenient source-location printing mechanism. For example:
#define WARN_IF_NAN(X) \
do \
{ \
if ( isnan(X) != 0 ) \
WARN( # X " is NaN (Floating Point NOT-A-NUMBER)" ); \
if ( isinf(X) != 0 ) \
WARN( # X " is INF (Floating Point INFINITY)" ); \
} while ( false )
Assert/Unless macros. You can pass any token, including operators like '==', through a macro. So constructs like:
ASSERT( foo, ==, bar )
Or
UNLESS( foo, >=, 0, value=0; return false; );
Are legal. Assert/Unless macros can automatically add all sorts the nice useful info like CodeLocation, stack traces, or throwing exceptions / coredumping / exiting gracefully.
发布评论
评论(25)
你可以看看 Boost.Preprocessor发现预处理器的许多有趣用途......
You can have a look at Boost.Preprocessor to find lot's of interesting uses of the preprocessor...
我最喜欢的技巧之一是将可变数量的参数传递给宏,稍后用于调用类似 printf 的函数。 为此,我指定宏只有一个参数,并在不带 () 的宏主体中使用它,但在 (( 和 )) 中将所有参数传递给宏,因此列表看起来像单个参数。 例如,
One of my favorite tricks is a way to pass variable number of arguments to macros, to be later used in calling printf-like functions for example. To do this, I specify that the macro has only one parameter and use it in the body of the macro without (), but pass all the parameters to the macro in (( and )), so the list looks like a single argument. For example,
我将这个有趣的归功于 Sean Barrett:
一种让预处理器根据可以通过宏表达的更高级别结构为您生成代码的黑客方法。
I credit Sean Barrett for this fun one:
A hacky way to get the preprocessor to generate code for you based on some higher level structure that you can express through macros.
具有默认值(不为零)的结构体文字,使用 C99 可变参数宏
使用
EXAMPLE(.name="test")
使用默认值,除了显式覆盖name. 标准中明确定义了稍后提及同一成员的这种阴影。
Struct literals with default values (that are not zero), using C99 variadic macros
using
EXAMPLE(.name="test")
uses the default values, except for the explicit override ofname
. This shadowing with later mentions of the same member is well-defined in the standard.对于嵌入式代码,embeddedgurus.com
使您能够处理二进制值:
这实现了与 @Ferruccio 之前关于 BOOST_BINARY 的响应类似的目标,尽管有所扩展。
这是代码(复制粘贴,未经测试,请参阅链接了解更多详细信息)
我喜欢宏。 调试时非常有趣!
For embedded code, a nice trick from embeddedgurus.com
enables you to handle binary values:
This achieve similar goals as the previous response from @Ferruccio about BOOST_BINARY, although a bit expanded.
Here's the code (copy'n pasted, not tested, see link for more details)
I like macros. So much fun when debugging !
日志记录是特别经常使用宏的地方之一:
Logging is one place where macros are particulary often used:
我经常将调试声纳之类的东西包装在一个简单的宏中,允许它从发布版本中编译出来:
稍后使用通常类似于:
do{}while(0)
习惯用法是为了避免出现问题这可能是由于意外地将D(...)
用作条件或循环的唯一内容而导致的。 毕竟,您不希望这样的代码意味着错误的事情:如果我能让这种情况抛出错误,我会的,但预处理器本身必须是一个完整的编译器才能告诉
D( )
宏是循环体的唯一内容。我也是编译时断言的忠实粉丝。 我的配方略有不同,但与我见过的其他配方相比没有真正的优势。 关键是形成一个唯一命名的 typedef,如果断言条件为 false,则该 typedef 会抛出错误,否则不会抛出错误。 在 cassert.h 中,我们有:
在某些源文件中,typedef 在任何地方都是合法的:
生成的错误消息通常是模糊的,但会包含标识符片段,使有问题的行能够通过暴力发现。
我一直对在编写代码生成实用程序可能是首选答案的地方使用预处理器感到内疚,就像另一个答案中的代码基于枚举成员名称的独特部分生成大量样板文件一样。 当编写大量要在 C 中编译的消息分发胶水时,这尤其方便。
I often wrap things like debug sonar in a simple macro that allows it to be compiled out of release builds:
Usage later is typically something like:
The
do{}while(0)
idiom is there to avoid issues that might result from accidentally making a usage ofD(...)
the only content of a conditional or loop. You don't want code like this to mean the wrong thing, after all:If I could make that case throw an error, I would, but the preprocessor would have to be a full compiler itself to tell that the
D()
macro was the sole content of a loop body.I'm also a big fan of compile-time assertions. My formulation is slightly different, but has no real advantages over others I've seen. The key is to form a uniquely named typedef that throws an error if the asserted condition is false, and not otherwise. In cassert.h we have:
And in some source file, anywhere a typedef would be legal:
The resulting error message is often obscure, but will contain the fragment of identifier enabling the offending line to be discovered by brute force.
I've been guilty of using the preprocessor in places where writing a code generation utility might have been the preferred answer, much like the code in another answer that generated lots of boiler-plate based on the unique parts of an enum member's name. That is especially handy when writing a lot of message-dispatch glue to be compiled in C.
我使用宏的主要地方是在我自己的测试框架中。 例如,当我想断言某些代码必须抛出时,我使用这个宏:
并像这样使用它:
我使用它们的唯一其他地方是在类声明中。 我有一个宏:
我用它来指定一个类不能被复制(或分配):
这没有做任何特别的事情,但引起人们的注意并且可以很容易地搜索到。
The main place I use macros is in my own testing framework. For example, when I want to assert that some code must throw, I use this macro:
And use it like this:
The only other place I use them is in class declarations. I have a macro:
which I use to specify that a class cannot be copied (or assigned):
this doesn't do anything special but draws peoples attention and can easily be searched for.
人们可以简化重复的事情,即。 枚举列表
...然后通过结构化方式执行 switch case
注意: 当然这可以使用函数指针数组来完成,但这为添加参数和使用字符串提供了更多的灵活性使用单个哈希进行扩展。
...或者测试字符串以获得正确的枚举值
One can simplify repetitive things for ie. enum lists
...and later do a switch case over a structured way
Note: Sure this could be done with a function pointer array but this opens for a little more flexibilities to add parameters and also use the string expansions with the single hash.
...or to test on strings to get the right enum value
从 CrashRpt 项目中,需要技巧来扩展宏和定义:
From the CrashRpt project , need trick to widen macros and defines:
您可以使用宏来定义具有不同数据类型的相同功能。 例如:
在此示例中定义了三个处理三种不同数据类型(
无符号字符
、无符号整数
、int
)。 但是,所有这些都返回一个包含所传递值的位的字符串。You can use macros to define the same functionality with different data types. For example:
In this example defines three functions (
bits_str_uchar()
,bits_str_uint()
,bits_str_int()
) that handle three different data types (unsigned char
,unsigned int
,int
). However, all return a string that contains the bits of the value passed.当您实现 COM 服务器时,您必须处理代码可能抛出的所有异常 - 让异常通过 COM 方法边界通常会使调用应用程序崩溃。
方法括号对此很有用。 有一个左括号是一个包含“try”的宏,一个右括号包含一组“catch”,将异常包装到 ErrorInfo 中并生成 HRESULT。
When you implement a COM server, you have to take care of all exceptions your code could possibly throw - letting an exception through COM method boundary will often crash the calling application.
Methods brackets are useful for this. There's an opening bracket which is a macro containing "try" and a closing bracket that contains a set of "catch"es, wrapping of exceptions into ErrorInfo and producing HRESULTs.
大多数(全部?)C++ 单元测试框架都是基于宏构建的。 我们使用 UnitTest++。 检查一下,看看各种奇特的宏。
Most (all?) C++ Unit Testing frameworks are built upon macros. We use UnitTest++. Check it out to see all sorts of fancy macros.
BOOST_BINARY 宏执行一些 clevel pre-处理器技巧使 C++ 能够以二进制表示数字常量。 但其范围仅限于 0-255。
The BOOST_BINARY macro performs some clevel pre-processor trickery to give C++ the ability to express numeric constants in binary. It is limited to 0-255 however.
在微控制器上,通常使用 UART 来调试代码,因为硬件断点有许多缺点。
这是一个简单的宏,已被证明非常有用:
使用示例:
收到的流:
是的,它很粗糙且不安全。 它仅在错误被隔离之前应用,因此该宏不会造成任何损害。
On micro controllers it is common to debug code using UART, as hardware breakpoints have many drawbacks.
This is a simple macro that has proven very useful:
Usage example:
Recieved stream:
Yes, it's crude and unsafe. It's only applied until the bug is isolated, so this macro does no harm.
恕我直言,pthreads 实用宏特别令人印象深刻。
The pthreads utility macros are particularily impressive IMHO.
当我处理巨大的 c/c++ 嵌套结构(例如用于 3GPP RRC/NBAP/RNSAP 的结构)时,我遵循这一技巧来使代码看起来干净。
这将使维护期间的生活变得更轻松。在设计期间也是如此,并且代码将是可读的。
When i work on huge c/c++ nested structures like the one used for 3GPP RRC/NBAP/RNSAP, i follow this trick to make the code look clean.
This will make life easier during maintenance time..also in design time and code will be readable.
将它们转换为语言的构造以提高类型安全性和调试能力。
Converting them to a construct of the language to improve type safety and debugging ability.
我经常用这个。 我有一个
debug.h
标头定义如下:然后:
如果您想获得
“message from debug!”
消息,请编译:否则,什么也不会发生。 Gcc 是一个非常智能的编译器。 如果未定义
DEBUG
,则生成的if(0)
(死代码)将从您的代码中删除,并进行一些优化。您还可以做更多事情:
几天前,我看到 D 编程语言 也提供了非常相似的功能。
如果您在没有上下文的情况下思考上述内容,您可以将认为定义为
然后
Often I use this. I have a
debug.h
header define as the following:and then:
if you want to get
"message from debug!"
message, compile with:Otherwise, nothing happens. Gcc is a very smart compiler. If
DEBUG
isn't defined, the generatedif(0)
(dead code) will be removed from your code with some optimizations on.You still can do more:
Some days ago I seen the D programming language provide a feature very similar too.
If you think the above without context, you can define thinks as
And then
在宏中,控制流非常容易,因为它只是文本替换。 这是一个 for 循环的示例:
In macros, it's very easy to do control flow because it's just text substitution. Here's an example with a for loop:
在 C 中,通常定义宏来执行一些获取逐字参数的操作,同时定义函数以便能够透明地获取它的地址。
In C, it's common to define macros that do some stuff getting the verbatim argument, and at the same time define functions to be able to get the address of it transparently.
还有 X 宏习惯用法,对于 DRY 和简单的代码生成很有用:
一个人在标头 gen.x 中使用尚未定义宏定义一种表:
然后他可以在不同的环境中使用它为每个 #include 定义它的地方通常具有不同的定义:
There is also the X Macro idiom which can be useful for DRY and simple code generation :
One defines in a header gen.x a kind of table using a not yet defined macro :
Then he can use it in different places defining it for each #include with a usually different definition :
最酷的宏是:assert、include Guards、__FILE__、__LINE__。
避免在代码中使用其他宏。
编辑:
仅当您没有合法的解决方案时才使用宏。
Coolest macro is: assert, include guards, __FILE__, __LINE__.
Avoid using other macro in your code.
EDIT:
Use macros only when you don't have legal solution w/o them.
用于调试的 SHOW():
双重评估以扩展参数技巧:(例如,使用实际行号而不是“__LINE__”。)
静态编译时断言。
例如:
使用方式:
初始化类 CodeLocation 的实例:(从调用点存储文件/行/函数——这*只能*通过宏或直接访问 __FILE__/__LINE__/etc 宏来完成在源点。)
随后被 MESSAGE/WARN/FAIL 宏用作方便的源位置打印机制。 例如:
Assert/Unless 宏。 您可以通过宏传递任何标记,包括“==”等运算符。 因此构造如下:
或者
是合法的。 Assert/Unless 宏可以自动添加各种有用的信息,例如 CodeLocation、堆栈跟踪或抛出异常/核心转储/优雅退出。
使 errno 更简单:
例如 printf( "Open failed. " ERRNO_FORMAT, ERRNO_ARGS );
SHOW() for debugging:
The double-evaluation to expand the arguments trick: (E.g. Use the actual line number and not "__LINE__".)
Static compile-time assertions.
E.g.:
Used via:
Initializing an instance of class CodeLocation: (Storing File/Line/Function from the point of invocation -- this can *ONLY* be done with a macro or by directly accessing the __FILE__/__LINE__/etc macros at the source point.)
Subsequently used by MESSAGE/WARN/FAIL macros as a convenient source-location printing mechanism. For example:
Assert/Unless macros. You can pass any token, including operators like '==', through a macro. So constructs like:
Or
Are legal. Assert/Unless macros can automatically add all sorts the nice useful info like CodeLocation, stack traces, or throwing exceptions / coredumping / exiting gracefully.
Making errno simplier:
E.g. printf( "Open failed. " ERRNO_FORMAT, ERRNO_ARGS );