我正在为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
答案 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>
以下划线开头,大写字母开头的任何内容。
包含双下划线的任何内容。
语言关键字。
#define
。
这是编译器编写器的一项工作,以确保您可以免费使用其他任何东西。
请注意,POSIX对此进一步施加了限制,包括禁止使用带有后缀std
的任何内容。
但是请注意,尽管第三方图书馆也应遵守这些规则,以确保您不会与它们冲突。 (例如,_t
中定义的内容。)避免使用宏,而使用windows.h
和&c。可以采取一些预防冲突的方法。
答案 3 :(得分:1)
首先,您可能要考虑使用通常在那些平台上预定义的一些现有宏,或者已经提供一组不错的宏的库,例如BOOST_OS_ANDROID
BOOST_OS_WINDOWS
from boost.predef,而不是定义自己的宏。
如果要编写自己的条件编译宏,则必须确保所选择的名称不是唯一合理的名称,并且在其他地方尚未用于无关目的,因为名称冲突不是您要处理的。