是否有某些关键字不应由我"#defined"?

Are there certain keywords that should not be "#defined" by me?

本文关键字:#defined 关键字 是否      更新时间:2023-10-16

我正在用C/C++为我的应用程序开发一个平台层。

我想

#define WINDOWS // on Windows machines

#define ANDROID // on Android phones

由于与其他库的冲突,定义"WINDOWS"或"ANDROID"等非常常见的关键字是个坏主意吗?在这些关键字前面加上有意义吗

#define MYLIB_WINDOWS // Not used by any other 3rdparty libraries

#define MYLIB_ANDROID

我想

小心在库的头文件中暴露具有"通用"名称的宏。

还要注意私有代码中的常用宏,特别是如果它#include(直接或间接)引用了其他库的头。

你不能指望所有的库维护人员都像你一样表现良好。

,在这些关键词前面加一些东西有意义吗:

一般来说,是的。

BOOST库套件就是一个很好的例子。BOOST非常小心地确保其头文件导出的所有宏都具有前缀BOOST_。前缀与库的名称空间名称boost::匹配并非巧合。

总之,如果你的库是在命名空间中实现的(它应该是,否则你会污染全局命名空间),请使用与之匹配的宏前缀。

示例:

namespace mylib { namespace innerthing { }}
#define MYLIB_ON 1
#define MYLIB_OFF 0
#define MYLIB_SETTING MYLIB_ON
#define MYLIB_INNERTHING_SETTING MYLIB_OFF

TL;DR版本

具有通用名称的宏迟早会引起问题,所以简短的回答是"永远不要为宏使用通用名称"。

建议

一种非常有用的方法是在非常简单的名称前面加上项目的名称,这在include guard中是非常常见的做法。

正确解释(带示例)

例如,在过去,我正在编写程序,特别是头p(project),并且必须包含一个文件,该文件通过一个不太深的包含链,包含一个头,您可以在其中找到一个名为"Log"的成员函数(为类调用此头C)。

碰巧,通过一个非常不同的include链,我还导入了一个以相同方式命名的宏的定义:"Log"。为宏调用此标头M。

结果是,当我尝试编译时,根据项目中包含的顺序,我要么会很好,要么会出现错误。

编译器执行的第一步是对当前正在编译的源文件调用预处理器。每次如果找到#include,它都会用包含的整个页眉的复制粘贴来替换该行。在预处理结束时,您有一个大文件,里面有来自源代码的所有代码和所有递归包含的头。

如果这个最终文件中的代码按以下顺序(从文件的顶部到底部):

C

M

p

然后一切都很好,因为M中的宏只影响它之后的代码。如果订单是:

M

C

p

预处理器会将函数名与宏的内容进行锯切。

假设M中的代码为:

#define Log printf("Oops")

C中的代码是:

class L {
void Log(const char* message) { printf("%sn", message); }
};

在预处理器阶段之后,每个日志行(在宏声明之后)都将被宏的内容替换。也就是说,来自C的代码现在看起来是这样的:

class L {
void printf("Oops")(const char* message) { printf("%sn", message); }
};

这段代码显然不会编译。

然而,主要的问题是编译错误将与为什么该行没有编译有关,这不是真正的问题:宏替换是。

请注意,根据宏和被替换的代码,您的代码最终可能会编译,但执行与您编码的代码不同的操作(例如,考虑替换常量值,因为您的常量的命名方式与宏相同)。

有用的注释

在调试最糟糕的宏问题时,我使用了gcc版本6。有些东西完全没有用,因为它只关注后预处理器代码。Clang版本3和往常一样,是一个救命稻草:它立即告诉我有一个X11宏(距离我编码的地方有3个库层!),有一个相对合理的名称(大约2个单词的长名称)。不幸的是,一个合理的库名称可能对库用户代码也是合理的,这就是为什么即使是罕见的名称也不够。

每个宏都需要前缀。

您可以#define除之外的任何内容

  1. 任何以前导下划线后跟大写字母开头的内容。

  2. 任何包含双下划线的内容。

  3. 语言关键字。

  4. std

确保其他任何东西都可以免费使用是编译器编写者的工作之一。

请注意,POSIX对此进行了进一步的限制,包括禁止任何带有_t后缀的内容。

但请注意,第三方图书馆也应该遵守这些规则,所以你有责任确保你不会与它们发生冲突。(例如windows.h中定义的东西。)尽可能避免使用宏和使用namespaces&c.可以采取一些措施来防止冲突。

首先,您可能需要考虑使用一些现有的宏,这些宏通常是在这些平台上预定义的,或者使用一个已经提供了一组很好的宏的库,例如boost.predef中的BOOST_OS_ANDROIDBOOST_OS_WINDOWS,而不是定义自己的宏。

如果你要编写自己的条件编译宏,那么你一定要确保你选择的名称是合理唯一的,并且没有在其他地方用于不相关的目的,因为你不想处理名称冲突。