是否有某些关键字不应该由我“#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
除了
,您可以#define
任何事情
任何以前导下划线和大写字母开头的内容。
任何包含双下划线的内容。
语言关键字。
std
.
编译器编写者的工作之一是确保您可以免费使用其他任何东西。
请注意,POSIX 对此有进一步的限制,包括不允许任何带有 _t
后缀的内容。
但请注意,尽管第 3 方图书馆也应遵循这些规则,因此您有责任确保不与它们发生冲突。 (例如 windows.h
中定义的东西。)尽可能避免使用宏并使用 namespace
s &c。可以采取一些措施来防止冲突。
首先,您可能要考虑使用一些通常在这些平台上预定义的现有宏,或者已经提供一组不错的宏的库,例如 BOOST_OS_ANDROID
BOOST_OS_WINDOWS
from boost.predef,而不是定义您自己的宏。
如果您打算编写自己的条件编译宏,那么您绝对应该确保您 select 的名称相当独特,并且没有在其他地方用于不相关的目的,因为名称冲突不是什么你要处理。
I would like to
谨防在您的库的 header 文件中公开带有 'common' 名称的宏。
还要注意私有代码中通常命名的宏 - 特别是如果它 #include
s(直接或间接)其他库的 headers.
您不能指望所有库维护者都像您一样well-behaved。
and would it make sense to prefix these keywords with something:
总的来说,是的。
BOOST 库套件就是一个很好的例子。 BOOST 非常注意确保其 header 文件导出的所有宏都具有前缀 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 版本
具有通用名称的宏迟早会导致问题,因此简短的回答是 "NEVER use a common name for your macro"。
建议
一种非常有用的方法是在您非常简单的名称前加上您的项目名称,这在 include guards 中是很常见的做法。
正确解释(举例)
举个例子,过去我正在研究程序,特别是 header P(roject),并且必须包含一个文件,该文件通过一个不太深的包含链包含一个 header 在那里你可以找到一个名为 "Log" 的成员函数(将此 header C 称为 class)。
碰巧的是,通过一个非常不同的包含链,我还导入了一个以相同方式命名的宏的定义:"Log"。将此 header M 称为宏。
结果是,当我尝试编译时,根据项目中包含的顺序,我要么没问题,要么最终出现错误。
编译器执行的第一步是对当前正在编译的源文件调用预处理器。每次如果找到#include,它都会用您要包含的整个 header 的复制粘贴替换该行。在预处理结束时,您有一个大文件,其中包含源代码中的所有代码以及所有递归包含的 headers.
如果此最终文件中的代码按以下顺序(文件从上到下):
C
男
P
那么一切都很好,因为 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.something 完全没用,因为它只关注 post-preprocessor 代码。像往常一样,Clang 版本 3.something 是一个可取之处:它立即告诉我有一个 X11 宏(从我编码的地方开始有 3 个库层!)具有一个相对合理的名称 (long-ish 2字名)。不幸的是,你的库的合理名称可能对库用户代码也是合理的,这就是为什么即使是罕见的名称也不够。
每个宏都需要 _ 前缀。
我正在 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
除了
,您可以#define
任何事情
任何以前导下划线和大写字母开头的内容。
任何包含双下划线的内容。
语言关键字。
std
.
编译器编写者的工作之一是确保您可以免费使用其他任何东西。
请注意,POSIX 对此有进一步的限制,包括不允许任何带有 _t
后缀的内容。
但请注意,尽管第 3 方图书馆也应遵循这些规则,因此您有责任确保不与它们发生冲突。 (例如 windows.h
中定义的东西。)尽可能避免使用宏并使用 namespace
s &c。可以采取一些措施来防止冲突。
首先,您可能要考虑使用一些通常在这些平台上预定义的现有宏,或者已经提供一组不错的宏的库,例如 BOOST_OS_ANDROID
BOOST_OS_WINDOWS
from boost.predef,而不是定义您自己的宏。
如果您打算编写自己的条件编译宏,那么您绝对应该确保您 select 的名称相当独特,并且没有在其他地方用于不相关的目的,因为名称冲突不是什么你要处理。
I would like to
谨防在您的库的 header 文件中公开带有 'common' 名称的宏。
还要注意私有代码中通常命名的宏 - 特别是如果它 #include
s(直接或间接)其他库的 headers.
您不能指望所有库维护者都像您一样well-behaved。
and would it make sense to prefix these keywords with something:
总的来说,是的。
BOOST 库套件就是一个很好的例子。 BOOST 非常注意确保其 header 文件导出的所有宏都具有前缀 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 版本
具有通用名称的宏迟早会导致问题,因此简短的回答是 "NEVER use a common name for your macro"。
建议
一种非常有用的方法是在您非常简单的名称前加上您的项目名称,这在 include guards 中是很常见的做法。
正确解释(举例)
举个例子,过去我正在研究程序,特别是 header P(roject),并且必须包含一个文件,该文件通过一个不太深的包含链包含一个 header 在那里你可以找到一个名为 "Log" 的成员函数(将此 header C 称为 class)。
碰巧的是,通过一个非常不同的包含链,我还导入了一个以相同方式命名的宏的定义:"Log"。将此 header M 称为宏。
结果是,当我尝试编译时,根据项目中包含的顺序,我要么没问题,要么最终出现错误。
编译器执行的第一步是对当前正在编译的源文件调用预处理器。每次如果找到#include,它都会用您要包含的整个 header 的复制粘贴替换该行。在预处理结束时,您有一个大文件,其中包含源代码中的所有代码以及所有递归包含的 headers.
如果此最终文件中的代码按以下顺序(文件从上到下):
C
男
P
那么一切都很好,因为 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.something 完全没用,因为它只关注 post-preprocessor 代码。像往常一样,Clang 版本 3.something 是一个可取之处:它立即告诉我有一个 X11 宏(从我编码的地方开始有 3 个库层!)具有一个相对合理的名称 (long-ish 2字名)。不幸的是,你的库的合理名称可能对库用户代码也是合理的,这就是为什么即使是罕见的名称也不够。
每个宏都需要 _ 前缀。