写函数原型的明智方法

Jon*_*eld 4 c dry

我正在寻找一种(干净的)编写函数定义和函数原型的方法,而不需要代码重复.由于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文件!包含守卫会停止递归.它通过独立的预处理器运行时编译干净,看起来合理.否则,在整个源中嵌入预处理器条件看起来不太好.

我能想到几种替代方法.

  1. 不要担心代码重复
  2. 更改为自动生成界面的语言
  3. 使用代码生成器(例如sqlite的makeheaders)

代码生成器可以工作,但似乎有点过分作为轻微烦恼的解决方案.由于C已经存在超过25年的某个地方,所以希望社区就最佳路径达成共识.

谢谢你的阅读.

编辑:使用gcc 4.8.2和clang 5.1编译警告.弄乱宏语句会产生相当一致的编译器错误消息.缺少#endif(如果函数定义很长,很容易完成)会产生"error:unterminated #else"或"error:unterminated conditional directive",两者都引用#ifdef行.

缺少#else意味着代码不再有效C. gcc"错误:预期标识符或'(''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''建议缺少#else.

如果结果是致命的,拼写PROTOTYPE错误会产生连贯的消息,如果结果无关紧要则不会发出警告.编译器警告不像定义和声明不同时那样具体,但它们可能足够具体.

sth*_*sth 5

普遍接受的路径是您的选择1),不用担心,只需写两次声明.

与功能实现相比,来自原型的重复只是一小部分.像你的问题中的宏观黑客很快变得笨拙而且收效甚微.宏机器的代码与原始原型一样多,只是现在更难理解发生了什么,并且你会得到更多神秘的错误信息.理解重复的微不足道被大约相同数量的难以理解的技巧所取代.

使用普通原型时,编译器会在事情不匹配时发出警告,如果您忘记了#endif或其他不匹配的东西,这样的宏基解决方案很难理解错误.例如foo.c,错误中的任何提及可能有或没有PROTOTYPE定义.