【发布时间】:2014-06-10 13:55:52
【问题描述】:
我正在寻找一种(干净的)方法来编写函数定义和函数原型,而无需重复代码。由于 DRY 是一个很好的想法,并且在头文件中手工编码原型显然是违反了这似乎是一个合理的要求。
下面的示例代码表明了一种使用预处理器解决问题的(粗略的)方法。它似乎不太可能是最佳的,但似乎可以正常工作。
使用单独的文件和复制:
foo.h:
#ifndef FOO_H
#define FOO_H
// Normal header file stuff
int dofoo(int a);
#endif /* FOO_H */
foo.c:
#include "foo.h"
int dofoo(int a) {
return a * 2;
}
使用 C 预处理器:
foo.h:
#ifndef FOO_H
#define FOO_H
// Normal header file stuff
#ifdef PROTOTYPE // if incorrect:
// No consequences for this test case, but we lose a sanity check
#error "PROTOTYPE set elsewhere, include mechanism will fall over"
#endif
#define PROTOTYPE // if incorrect:
// "error: redefinition of 'dofoo'" in clang & gcc,
// referring to int dofoo() line in foo.c
#include "foo.c"
#undef PROTOTYPE //if incorrect:
// No warnings, but should trigger the earlier #error statement if
// this method is used in more than one file
#endif /* FOO_H */
foo.c:
#include "foo.h"
int dofoo (int a)
#ifdef PROTOTYPE // if incorrect:
// "error: redefinition of 'dofoo'" in clang & gcc,
// referring to int dofoo() line in foo.c
;
#else
{
return a * 2;
}
#endif
机制有点奇怪 - .h 文件通常不包含 .c 文件!包含守卫停止递归。当通过独立的预处理器运行时,它编译干净并且看起来很合理。否则,在整个源代码中嵌入预处理器条件看起来并不好。
我可以想到几种替代方法。
- 不用担心代码重复
- 更改为自动生成界面的语言
- 使用代码生成器(例如 sqlite 的 makeheaders)
代码生成器可以工作,但作为解决小麻烦的解决方案似乎有点矫枉过正。由于 C 在这一点上已经存在超过 25 年,因此希望社区能够就最佳路径达成共识。
感谢您的阅读。
edit:gcc 4.8.2 和 clang 5.1 的编译器警告 弄乱宏语句会产生相当一致的编译器错误消息。缺少#endif(如果函数定义很长,很容易做到)会产生“错误:未终止的#else”或“错误:未终止的条件指令”,两者都指的是#ifdef行。
缺少#else 意味着代码不再有效 C. gcc "error: expected identifier or '(' before '{' token" and clang add "expected function body after function declarator". 两者都指向正确的行号,但都没有暗示缺少#else。
如果结果是致命的,则 PROTOTYPE 拼写错误会产生连贯的消息,如果结果无关紧要,则不会发出警告。编译器警告并不像定义和声明不同时那样具体,但它们可能已经足够具体了。
【问题讨论】:
-
这看起来像零增益的大量工作。你把DRY 带到了一个不合理的极端。
-
奇怪但聪明。不过,我不确定我是否会在实践中这样做。
-
这是否违反 DRY 是有争议的,作为它的主要动机之一(如果你有两次相同的代码,你有一个错误,忘记在重复的代码中修复它,所以该错误未修复)不要保留:编译器将捕获仅部分代码的更改。
-
从 DRY 的角度来看,第二种方法更糟糕:有 4 次你可以得到
PROTOTYPE错误。 -
我很惊讶主要担心宏命令中的错误。 gcc 4.8.2 和 clang 5.1 在没有启用警告标志的情况下捕获由此产生的致命错误,就像它们会捕获过时的原型一样。因此,安全性似乎并没有降低。我大多不喜欢这种方法,因为它在美学上并不令人满意。