是否有某些关键字不应由我"#defined"?
Are there certain keywords that should not be "#defined" by me?
我正在用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
除之外的任何内容
-
任何以前导下划线后跟大写字母开头的内容。
-
任何包含双下划线的内容。
-
语言关键字。
-
std
。
确保其他任何东西都可以免费使用是编译器编写者的工作之一。
请注意,POSIX对此进行了进一步的限制,包括禁止任何带有_t
后缀的内容。
但请注意,第三方图书馆也应该遵守这些规则,所以你有责任确保你不会与它们发生冲突。(例如windows.h
中定义的东西。)尽可能避免使用宏和使用namespace
s&c.可以采取一些措施来防止冲突。
首先,您可能需要考虑使用一些现有的宏,这些宏通常是在这些平台上预定义的,或者使用一个已经提供了一组很好的宏的库,例如boost.predef中的BOOST_OS_ANDROID
BOOST_OS_WINDOWS
,而不是定义自己的宏。
如果你要编写自己的条件编译宏,那么你一定要确保你选择的名称是合理唯一的,并且没有在其他地方用于不相关的目的,因为你不想处理名称冲突。
- Visual Studio 2015:Extern "C" 和 "export" 关键字
- C++中的"inline"关键字
- 如何确保C++函数在定义之前声明(如override关键字)
- 谷歌模拟和覆盖关键字
- 应用程序崩溃并显示"symbol _ZdlPvm, version Qt_5 not defined in file libQt5Core.so.5 with link time reference"
- 结构体 S { int align; } 之间的区别;(struct 关键字后的名称)和 struct { int al
- 如果全局变量默认是外部变量,为什么要添加"extern"关键字?
- 当我从下面的代码中删除关键字 virtual 时,它可以正常工作,否则会出现错误。在这里"virtual"字的意义是什么?
- 为什么"delete"关键字不删除节点?
- 在 c++ 中正确定义"this"关键字?
- "Ill-defined for-loop - loop executes infinitely" (MSVC C6295)
- 这个额外的关键字在这个 c++ 类声明中是什么意思?
- 在 typedef 内部使用 const 关键字和在 typedef 外部使用 const 关键字之间有区别吗?
- C++ - 为什么这里需要'template'关键字?
- C++函数的关键字?
- 使用 'typename' 关键字将非类型视为依赖上下文中的类型
- 使用"std::enable_if_t" "function template has already been defined"
- "friend"关键字在C++中是什么意思?
- 声明C++数组(带或不带 "new" 关键字)
- 是否有某些关键字不应由我"#defined"?