制作库时是否应避免使用宏
Should MACROs be avoided when making a library
库提供了一个派生类,并将派生类作为模板参数。
例:
class userclass : public lib::superclass<userclass>
{}
如您所见,要键入的内容很多。并且"用户类"应始终派生为公共类,以便其正常工作。所以我想出了两个看起来像这样的宏:
#define SUPER(x) public lib::superclass<x>
#define SUPERCLASS(x) class x : public lib::superclass<x>
用户现在可以键入其中之一。
class userclass : SUPER(userclass)
{}
SUPERCLASS(userclass)
{}
但主要问题是宏 SUPER 和 SUPERCLASS 存在于用户全局命名空间中的速度与包含标头一样快。
我可以/应该:
- 有办法保留命名空间要求但仍默认为公共派生?
- 按原样使用宏。
- 只需要求用户写出完整的"public lib::superclass"。
我使用的是 vs 11,该库针对的是 Windows 开发人员。
使用宏的第一条规则是"不要,如果有任何其他解决方案"。在这种情况下,还有另一种解决方案,因此请摆脱它们。
其次,你的宏弊大于利,因为人们不知道他们通过阅读它扩展到什么,而完整的定义却知道。说真的,您正在节省真正微小的字符数,而可读性却付出了可怕的代价。简单地写出继承要好得多。
这真的不是很多输入。我见过很多不应该缩短的更长的行。用宏隐藏它只会混淆你的代码。如果我快速浏览一下您的SUPERCLASS(userclass) {}
,我几乎可以猜到它是一个类(我不喜欢使用基于猜测的库),但我不知道它或其父级是否被称为userclass
(或两者都不是)或它正在使用哪种继承。这意味着你必须记录它,并强迫人们在需要时查找它。
所以正确答案是选项 3 - 不要使用宏。
如果您确实确实需要在库中使用宏,请为其提供特定于库的前缀。这与您将到达命名空间宏一样接近。
我投票给3。只需要求用户写出完整的"public lib::superclass"。
在以下情况下,库中的宏可能很有用:
- 真的有很多东西要写,
- 有些东西必须写好几遍
- 或者你想隐藏一个丑陋的实现细节,而语言不允许你这样做。
但在您的情况下:
- 没有那么多可写,
- 是的,您必须将类名输入两次,
- 你不想隐瞒你正在子类的事实,甚至你正在写一个类!
我不认为类名的重复 - 一个积极的点 - 是值得的。特别是因为您将隐藏class
关键字并给读者带来相当大的混乱。
无论如何,如果库使用宏,则习惯上将库名称放在所有宏的前面:
#define MY_FANCY_LIBRARY_NAME_SUPER(x) public lib::superclass<x>
但是现在你没有节省这么多打字...
PS:记住编程的黄金法则:
代码只写一次,但永远读,因此它应该很容易阅读,而不是容易编写。
- 在提升multi_index容器中,是否定义了"default index"?
- 在C++STL中是否有Polyval(Matlab函数)等价物?
- 检查输入是否不是整数或数字
- 是否可以初始化不可复制类型的成员变量(或基类)
- 在C++中,是否可以基于给定的标识符创建基类的新实例,反之亦然
- 是否可以通过C++扩展强制多个python进程共享同一内存
- 此代码是否违反一个定义规则
- 是否需要删除包含对象的"pair"?
- 是否可以从int转换为enum类类型
- 无论条件是否为true,if总是在c++中执行
- 如何找到大小'x'数组是否完全填充,在C++?
- 检查值是否在集合p1和p2中,但不在p3中
- 是否可以在编译时初始化数组,以便在运行时不会花费时间?
- 检查 std::shared_ptr<> 的当前底层类型是否为 T
- 在c++中检查长方体是否尽可能快地重叠(无迭代)
- GL_SHADERSTORAGE_BUFFER位置是否与其他着色器位置冲突
- 子目录是否继承属性,例如add_definitions,include_directories和父Cmakelist.t
- 标准是否使用多余的大括号(例如 T{{{10}}})定义列表初始化?
- C/C++预处理器是否可以检测一些编译器选项
- 是否可以用"iostream"包装现有的TCP/OOpenSSL会话