当前位置:  开发笔记 > 编程语言 > 正文

##预处理器运算符和陷阱的应用有哪些应用?

如何解决《##预处理器运算符和陷阱的应用有哪些应用?》经验,为你挑选了4个好方法。

正如我之前的许多问题所述,我正在通过K&R工作,目前正在进入预处理器.其中一个更有意思的事情 - 我之前从未尝试过的任何学习C的尝试 - 是##预处理器操作员.根据K&R的说法:

预处理器运算符## 提供了一种在宏扩展期间连接实际参数的方法.如果替换文本中##的参数与a相邻,则参数将替换为实际参数, ##并删除周围的空白区域,并重新扫描结果.例如,宏paste 连接其两个参数:

#define paste(front, back) front ## back

所以paste(name, 1)创建令牌 name1.

如何以及为什么有人会在现实世界中使用它?它的使用的实际例子是什么,有什么需要考虑的?



1> Michael Burr..:

当您使用令牌粘贴(' ##')或字符串化(' #')预处理运算符时,需要注意的一点是,必须使用额外的间接级别才能在所有情况下正常工作.

如果你不这样做,并且传递给令牌粘贴操作符的项目本身就是宏,你将获得可能不是你想要的结果:

#include 

#define STRINGIFY2( x) #x
#define STRINGIFY(x) STRINGIFY2(x)
#define PASTE2( a, b) a##b
#define PASTE( a, b) PASTE2( a, b)

#define BAD_PASTE(x,y) x##y
#define BAD_STRINGIFY(x) #x

#define SOME_MACRO function_name

int main() 
{
    printf( "buggy results:\n");
    printf( "%s\n", STRINGIFY( BAD_PASTE( SOME_MACRO, __LINE__)));
    printf( "%s\n", BAD_STRINGIFY( BAD_PASTE( SOME_MACRO, __LINE__)));
    printf( "%s\n", BAD_STRINGIFY( PASTE( SOME_MACRO, __LINE__)));

    printf( "\n" "desired result:\n");
    printf( "%s\n", STRINGIFY( PASTE( SOME_MACRO, __LINE__)));
}

输出:

buggy results:
SOME_MACRO__LINE__
BAD_PASTE( SOME_MACRO, __LINE__)
PASTE( SOME_MACRO, __LINE__)

desired result:
function_name21


@AbhimanyuAryan:我不确定这是不是你问的问题,但是`__LINE__`是一个特殊的宏名称,它被预处理器替换为源文件的当前行号.

2> Brian R. Bon..:

CrashRpt:使用##将宏多字节字符串转换为Unicode

CrashRpt(崩溃报告库)中的一个有趣用法如下:

#define WIDEN2(x) L ## x
#define WIDEN(x) WIDEN2(x)
//Note you need a WIDEN2 so that __DATE__ will evaluate first.

在这里,他们希望使用双字节字符串而不是每字节一个字节的字符串.这可能看起来毫无意义,但他们这样做是有充分理由的.

 std::wstring BuildDate = std::wstring(WIDEN(__DATE__)) + L" " + WIDEN(__TIME__);

他们将它与另一个宏一起使用,该宏返回带有日期和时间的字符串.

放在L旁边__ DATE __会给你一个编译错误.


Windows:将##用于通用Unicode或多字节字符串

Windows使用类似以下内容:

#ifdef  _UNICODE
    #define _T(x)      L ## x
#else
    #define _T(x) x
#endif

并且_T在代码中无处不在


各种库,用于清洁访问器和修饰符名称:

我也看到它在代码中用于定义访问器和修饰符:

#define MYLIB_ACCESSOR(name) (Get##name)
#define MYLIB_MODIFIER(name) (Set##name)

同样,您可以将此相同方法用于任何其他类型的聪明名称创建.


各种库,使用它一次制作多个变量声明:

#define CREATE_3_VARS(name) name##1, name##2, name##3
int CREATE_3_VARS(myInts);
myInts1 = 13;
myInts2 = 19;
myInts3 = 77;


由于您可以在编译时连接字符串文字,因此可以将BuildDate表达式减少为`std :: wstring BuildDate = WIDEN(__ DATE__)L""WIDEN(__ TIME __);`并隐式构建整个字符串.

3> bk1e..:

这是我在升级到新版本的编译器时遇到的问题:

不必要地使用令牌粘贴operator(##)是不可移植的,可能会产生不需要的空格,警告或错误.

当令牌粘贴操作符的结果不是有效的预处理器令牌时,令牌粘贴操作符是不必要的并且可能是有害的.

例如,有人可能会尝试使用token-pasting运算符在编译时构建字符串文字:

#define STRINGIFY(x) #x
#define PLUS(a, b) STRINGIFY(a##+##b)
#define NS(a, b) STRINGIFY(a##::##b)
printf("%s %s\n", PLUS(1,2), NS(std,vector));

在某些编译器上,这将输出预期的结果:

1+2 std::vector

在其他编译器上,这将包括不需要的空格:

1 + 2 std :: vector

相当现代的GCC版本(> = 3.3左右)将无法编译此代码:

foo.cpp:16:1: pasting "1" and "+" does not give a valid preprocessing token
foo.cpp:16:1: pasting "+" and "2" does not give a valid preprocessing token
foo.cpp:16:1: pasting "std" and "::" does not give a valid preprocessing token
foo.cpp:16:1: pasting "::" and "vector" does not give a valid preprocessing token

解决方案是在将预处理程序令牌连接到C/C++运算符时省略令牌粘贴运算符:

#define STRINGIFY(x) #x
#define PLUS(a, b) STRINGIFY(a+b)
#define NS(a, b) STRINGIFY(a::b)
printf("%s %s\n", PLUS(1,2), NS(std,vector));

关于连接的GCC CPP文档章节有关于令牌粘贴操作符的更多有用信息.


它被称为"令牌粘贴"操作符有一个原因 - 目的是在完成后以一个令牌结束.好的写作.

4> Vebjorn Ljos..:

这在各种情况下都很有用,以免不必要地重复自己.以下是Emacs源代码中的示例.我们想从库中加载许多函数.应将函数"foo"赋值给fn_foo,等等.我们定义以下宏:

#define LOAD_IMGLIB_FN(lib,func) {                                      \
    fn_##func = (void *) GetProcAddress (lib, #func);                   \
    if (!fn_##func) return 0;                                           \
  }

然后我们可以使用它:

LOAD_IMGLIB_FN (library, XpmFreeAttributes);
LOAD_IMGLIB_FN (library, XpmCreateImageFromBuffer);
LOAD_IMGLIB_FN (library, XpmReadFileToImage);
LOAD_IMGLIB_FN (library, XImageFree);

好处是不必写都fn_XpmFreeAttributes"XpmFreeAttributes"(和风险拼错其中之一).

推荐阅读
地之南_816
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有