我正在寻找一种(干净的)编写函数定义和函数原型的方法,而不需要代码重复.由于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;
}
Run Code Online (Sandbox Code Playgroud)
使用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
Run Code Online (Sandbox Code Playgroud)
机制有点奇怪 - .h文件通常不包含.c文件!包含守卫会停止递归.它通过独立的预处理器运行时编译干净,看起来合理.否则,在整个源中嵌入预处理器条件看起来不太好.
我能想到几种替代方法.
代码生成器可以工作,但似乎有点过分作为轻微烦恼的解决方案.由于C已经存在超过25年的某个地方,所以希望社区就最佳路径达成共识.
谢谢你的阅读.
编辑:使用gcc 4.8.2和clang 5.1编译警告.弄乱宏语句会产生相当一致的编译器错误消息.缺少#endif(如果函数定义很长,很容易完成)会产生"error:unterminated #else"或"error:unterminated conditional directive",两者都引用#ifdef行.
缺少#else意味着代码不再有效C. gcc"错误:预期标识符或'(''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''建议缺少#else.
如果结果是致命的,拼写PROTOTYPE错误会产生连贯的消息,如果结果无关紧要则不会发出警告.编译器警告不像定义和声明不同时那样具体,但它们可能足够具体.
普遍接受的路径是您的选择1),不用担心,只需写两次声明.
与功能实现相比,来自原型的重复只是一小部分.像你的问题中的宏观黑客很快变得笨拙而且收效甚微.宏机器的代码与原始原型一样多,只是现在更难理解发生了什么,并且你会得到更多神秘的错误信息.理解重复的微不足道被大约相同数量的难以理解的技巧所取代.
使用普通原型时,编译器会在事情不匹配时发出警告,如果您忘记了#endif
或其他不匹配的东西,这样的宏基解决方案很难理解错误.例如foo.c
,错误中的任何提及可能有或没有PROTOTYPE
定义.