有某些我不应该“ #defined”的关键字吗?

时间:2018-07-07 09:56:20

标签: c++ c-preprocessor

我正在为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

4 个答案:

答案 0 :(得分:6)

  

我想

当心在库的头文件中公开带有“通用”名称的宏。

还请注意私有代码中的通用宏-尤其是#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

答案 1 :(得分:2)

TL; DR版本

具有通用名称的宏迟早会引起问题,因此简短的答案是“永远不要为宏使用通用名称”。

建议

一种非常有用的方法是在您的简单名称前添加项目名称,这在包含保护程序中很常见。

正确的解释(带有示例)

作为一个例子,过去我在程序上工作,特别是在头文件P(roject)上,并且必须包含一个文件,该文件通过不太深的include链包含了一个可以在其中找到成员的头文件函数名为“ Log”(将此标题C称为类)。

碰巧的是,通过一个截然不同的包含链,我还导入了以相同方式命名的宏的定义:“ Log”。将此标头M称为宏。

结果是,当我尝试编译时,根据项目中包含对象的顺序,我会很好,或者最终遇到错误。

编译器执行的第一步是在当前正在编译的源文件上调用预处理器。每次if找到#include时,它都会用您要包括的整个标头的复制粘贴字面替换该行。在预处理结束时,您将拥有一个大文件,其中包含源中的所有代码以及所有递归包含的标头。

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

C

M

P

然后一切都很好,因为M中的宏仅影响其后的代码。 如果订单相反:

M

C

P

预处理器会将函数名称与宏的内容一起渗透。

假设M中的代码为:

#define Log printf("Oops")

C中的代码是:

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

在预处理阶段之后,每个日志行(在宏声明之后)将被宏的内容替换。也就是说,来自C的代码现在如下所示:

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

此代码显然不会编译。

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

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

有用的提示

在调试最糟糕的宏问题时,我使用的是gcc版本6。有些东西完全没用,因为它只关注后预处理程序代码。 Clang版本3.something像往常一样是一个省事的地方:它立即告诉我,有一个X11宏(从我编码的地方开始到3层库!),它的名字相对敏感(长2个单词的名字)。不幸的是,对于您的库来说,一个明智的名称对于库用户代码也可能是明智的,这就是为什么即使是稀有名称也不够的原因。

每个宏确实都需要_前缀。

答案 2 :(得分:1)

除了{p>

  1. 以下划线开头,大写字母开头的任何内容。

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

  3. 语言关键字。

  4. #define

这是编译器编写器的一项工作,以确保您可以免费使用其他任何东西。

请注意,POSIX对此进一步施加了限制,包括禁止使用带有后缀std的任何内容。

但是请注意,尽管第三方图书馆也应遵守这些规则,以确保您不会与它们冲突。 (例如,_t中定义的内容。)避免使用宏,而使用windows.h和&c。可以采取一些预防冲突的方法。

答案 3 :(得分:1)

首先,您可能要考虑使用通常在那些平台上预定义的一些现有宏,或者已经提供一组不错的宏的库,例如BOOST_OS_ANDROID BOOST_OS_WINDOWS from boost.predef,而不是定义自己的宏。

如果要编写自己的条件编译宏,则必须确保所选择的名称不是唯一合理的名称,并且在其他地方尚未用于无关目的,因为名称冲突不是您要处理的。