- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我正在寻找一种(干净的)方法来编写函数定义和函数原型(prototype),而无需代码重复。由于 DRY 已被确立为一个好主意,并且在头文件中手动编码原型(prototype)是一种明显的违规行为,因此这似乎是一个合理的要求。
下面的示例代码指出了使用预处理器解决问题的(粗略的)方法。它似乎不太可能是最佳的,但似乎确实可以正常工作。
使用单独的文件和复制:
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 文件! include 守卫停止递归。当通过独立的预处理器运行时,它编译干净并且看起来合理。否则,在整个源代码中嵌入预处理器条件看起来不太好。
我可以想到几种替代方法。
代码生成器可以工作,但作为一个小烦恼的解决方案似乎有点过分了。由于此时 C 已经存在超过 25 年,因此希望社区就最佳路径达成共识。
感谢阅读。
编辑:gcc 4.8.2 和 clang 5.1 的编译器警告弄乱宏语句会产生相当连贯的编译器错误消息。缺少#endif(如果函数定义很长,很容易做到)会产生“错误:未终止的#else”或“错误:未终止的条件指令”,两者都指的是#ifdef 行。
缺少#else 意味着代码不再有效 C.gcc“错误:预期标识符或'('在'{'标记之前”并且clang添加“函数声明符之后的预期函数体”。两者都指向正确的行号,但都没有暗示缺少#else。
如果结果是致命的,拼写错误的 PROTOTYPE 会产生连贯的消息,如果结果无关紧要,则不会发出警告。当定义和声明不同时,编译器警告并不像它们可能的那样具体,但它们可能足够具体。
最佳答案
普遍接受的路径是您的选项 1),不用担心,只需将声明写两次即可。
与函数实现相比,来自原型(prototype)的重复只是一小部分。像您问题中的宏黑客很快变得笨拙并且几乎没有 yield 。宏机制最终与原始原型(prototype)一样多的代码,只是现在更难理解正在发生的事情,而且您会收到更多神秘的错误消息。理解重复的微不足道被大约相同数量的难以理解的诡计所取代。
对于普通的原型(prototype),编译器会在事情不匹配时发出警告,对于这样的宏基础解决方案,如果您忘记了 #endif
或其他不匹配的东西,您将很难理解错误配对。例如,错误中提及的任何 foo.c
可能有或没有定义 PROTOTYPE
。
关于c - 编写函数原型(prototype)的明智方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24142902/
我想了解 Ruby 方法 methods() 是如何工作的。 我尝试使用“ruby 方法”在 Google 上搜索,但这不是我需要的。 我也看过 ruby-doc.org,但我没有找到这种方法。
Test 方法 对指定的字符串执行一个正则表达式搜索,并返回一个 Boolean 值指示是否找到匹配的模式。 object.Test(string) 参数 object 必选项。总是一个
Replace 方法 替换在正则表达式查找中找到的文本。 object.Replace(string1, string2) 参数 object 必选项。总是一个 RegExp 对象的名称。
Raise 方法 生成运行时错误 object.Raise(number, source, description, helpfile, helpcontext) 参数 object 应为
Execute 方法 对指定的字符串执行正则表达式搜索。 object.Execute(string) 参数 object 必选项。总是一个 RegExp 对象的名称。 string
Clear 方法 清除 Err 对象的所有属性设置。 object.Clear object 应为 Err 对象的名称。 说明 在错误处理后,使用 Clear 显式地清除 Err 对象。此
CopyFile 方法 将一个或多个文件从某位置复制到另一位置。 object.CopyFile source, destination[, overwrite] 参数 object 必选
Copy 方法 将指定的文件或文件夹从某位置复制到另一位置。 object.Copy destination[, overwrite] 参数 object 必选项。应为 File 或 F
Close 方法 关闭打开的 TextStream 文件。 object.Close object 应为 TextStream 对象的名称。 说明 下面例子举例说明如何使用 Close 方
BuildPath 方法 向现有路径后添加名称。 object.BuildPath(path, name) 参数 object 必选项。应为 FileSystemObject 对象的名称
GetFolder 方法 返回与指定的路径中某文件夹相应的 Folder 对象。 object.GetFolder(folderspec) 参数 object 必选项。应为 FileSy
GetFileName 方法 返回指定路径(不是指定驱动器路径部分)的最后一个文件或文件夹。 object.GetFileName(pathspec) 参数 object 必选项。应为
GetFile 方法 返回与指定路径中某文件相应的 File 对象。 object.GetFile(filespec) 参数 object 必选项。应为 FileSystemObject
GetExtensionName 方法 返回字符串,该字符串包含路径最后一个组成部分的扩展名。 object.GetExtensionName(path) 参数 object 必选项。应
GetDriveName 方法 返回包含指定路径中驱动器名的字符串。 object.GetDriveName(path) 参数 object 必选项。应为 FileSystemObjec
GetDrive 方法 返回与指定的路径中驱动器相对应的 Drive 对象。 object.GetDrive drivespec 参数 object 必选项。应为 FileSystemO
GetBaseName 方法 返回字符串,其中包含文件的基本名 (不带扩展名), 或者提供的路径说明中的文件夹。 object.GetBaseName(path) 参数 object 必
GetAbsolutePathName 方法 从提供的指定路径中返回完整且含义明确的路径。 object.GetAbsolutePathName(pathspec) 参数 object
FolderExists 方法 如果指定的文件夹存在,则返回 True;否则返回 False。 object.FolderExists(folderspec) 参数 object 必选项
FileExists 方法 如果指定的文件存在返回 True;否则返回 False。 object.FileExists(filespec) 参数 object 必选项。应为 FileS
我是一名优秀的程序员,十分优秀!