C库命名约定

介绍

大家好,我最近学会用C编程! (这对我来说是一个巨大的进步,因为C ++是第一种语言,我接触并吓唬了我近10年。)来自大多数OO背景(Java + C#),这是一个非常好的范式转换。

我爱C.这是一种如此美丽的语言。 令我感到惊讶的是,高级的模块化和代码可重用性C支持 – 当然它不像OO语言那么高,但仍远远超出我对命令式语言的期望。

如何防止客户端代码与C库代码之间的命名冲突? 在Java中有包,在C#中有命名空间。 想象一下,我写了一个C库,它提供了“添加”操作。 客户端很可能已经使用了类似的操作 – 我该怎么办?

我特别想找一个客户友好的解决方案。 例如,我不想为我的所有api操作添加前缀,例如“myuniquelibname_add”。 C世界的常见解决方案是什么? 你把所有api操作放在一个结构中,所以客户端可以选择自己的前缀吗?

我非常期待通过你的答案得到的见解!

编辑(修改后的问题)

亲爱的Answerers,谢谢你的答案! 我现在看到,前缀是安全避免命名冲突的唯一方法。 所以,我想修改我的问题: 我有什么可能性,让客户选择他自己的前缀?

Unwind发布的答案是单向的。 它不使用正常意义上的前缀,但必须为每个api调用添加前缀“api->”。 还有哪些解决方案(比如使用#define)?

编辑2(状态更新)

这一切归结为两种方法之一:

  • 使用结构
  • 使用#define(注意:有很多方法,如何使用#define来实现,我想要的是什么)

我不接受任何答案,因为我认为没有正确的答案。 选择的解决方案取决于具体情况和自己的偏好。 我,我自己会尝试你提到的所有方法,找出最适合我的情况。 随意在相应答案的评论中发布支持或反对某些appraoches的论据。

最后,我要特别感谢:

  • 放松 – 因为他的复杂答案包括“结构方法”的完整实现
  • 克里斯托弗 – 他的好答案,并指向C中的命名空间
  • 所有其他 – 为您的出色投入

如果有人发现关闭这个问题是合适的(因为没有进一步的见解),他/她应该随意这样做 – 我无法决定这一点,因为我不是C大师。

我不是C大师,但是从我使用的库中,使用前缀来分隔函数是很常见的。

例如,SDL将使用SDL,OpenGL将使用gl等…

Ken提到的结构方式看起来像这样:

struct MyCoolApi { int (*add)(int x, int y); }; MyCoolApi * my_cool_api_initialize(void); 

然后客户会这样做:

 #include  #include  #include "mycoolapi.h" int main(void) { struct MyCoolApi *api; if((api = my_cool_api_initialize()) != NULL) { int sum = api->add(3, 39); printf("The cool API considers 3 + 39 to be %d\n", sum); } return EXIT_SUCCESS; } 

这仍然有“命名空间问题”; struct name(称为“struct tag”)必须是唯一的,并且您不能声明自己有用的嵌套结构。 它适用于收集function,并且是您在C中经常看到的一种技术。

更新:以下是实施方面的外观,这是在评论中要求的:

 #include "mycoolapi.h" /* Note: This does **not** pollute the global namespace, * since the function is static. */ static int add(int x, int y) { return x + y; } struct MyCoolApi * my_cool_api_initialize(void) { /* Since we don't need to do anything at initialize, * just keep a const struct ready and return it. */ static const struct MyCoolApi the_api = { add }; return &the_api; } 

你被C ++吓到了,这是一种耻辱,因为它有命名空间来处理这个问题。 在C中,你几乎只限于使用前缀 – 你当然不能“在一个结构中放置api操作”。

编辑:在回答关于允许用户指定自己的前缀的第二个问题时,我会像瘟疫一样避免它。 99.9%的用户会对你提供的任何前缀感到满意(假设它不是太愚蠢),并且在他们必须跳过以满足剩余的0.1%的篮球(宏,结构,等等)时会非常不安。

作为库用户,您可以通过预处理器轻松定义自己的缩短命名空间; 结果看起来有点奇怪,但它有效:

 #define ns(NAME) my_cool_namespace_ ## NAME 

可以写

 ns(foo)(42) 

代替

 my_cool_namespace_foo(42) 

作为图书馆作者,您可以提供此处描述的缩写名称。

如果你遵循unwinds的建议并创建一个API结构,你应该使函数指针编译时常量使inlinig成为可能,即在你的.h文件中,使用下面的代码:

 // canonical name extern int my_cool_api_add(int x, int y); // API structure struct my_cool_api { int (*add)(int x, int y); }; typedef const struct my_cool_api *MyCoolApi; // define in header to make inlining possible static MyCoolApi my_cool_api_initialize(void) { static const struct my_cool_api the_api = { my_cool_api_add }; return &the_api; } 

不幸的是,没有确定的方法可以避免C语言中的名称冲突。由于缺少名称空间,因此您可以使用前缀全局函数和变量的名称。 大多数图书馆选择一些简短且“独特”的前缀(由于显而易见的原因, 唯一的引号是引号),并希望不会发生冲突。

需要注意的一点是,库的大部分代码都可以静态声明 – 这意味着它不会与其他文件中类似命名的函数冲突。 但出口function确实必须仔细加上前缀。

由于您使用相同名称公开函数,因此客户端不能包含库头文件以及具有名称冲突的其他头文件。 在这种情况下,您在函数原型之前在头文件中添加以下内容,这也不会影响客户端使用。

 #define add myuniquelibname_add 

请注意,这是一个快速解决方案,应该是最后一个选项。

有关struct方法的一个非常大的例子,请看一下Linux内核; 这种风格有30多万行C.

前缀只是C级别的选择。

在某些平台上(支持链接器的单独命名空间,如Windows,OS X和一些商业unices,但不包括Linux和FreeBSD),您可以通过在库中填充代码来解决冲突,并且只从您真正需要的库中导出符号。 (例如,如果导出符号中存在冲突,则在importlib中出现别名)