大家好,我最近学会了用C编程! (这对我来说是一个巨大的进步,因为C ++是第一种语言,我接触并吓唬了我近10年。)来自大多数OO背景(Java + C#),这是一个非常好的范式转换。
我爱C.这是一种如此美丽的语言。最让我感到惊讶的是C级支持的高级模块化和代码可重用性 - 当然它不像OO语言那么高,但仍然远远超出了我对命令式语言的期望。
如何防止客户端代码和我的C库代码之间的命名冲突?在Java中有包,在C#中有命名空间。想象一下,我写了一个C库,它提供了“添加”操作。客户端很可能已经使用了类似的操作 - 我该怎么办?
我特别想找到一个客户友好的解决方案。例如,我不想为我的所有api操作添加前缀,例如“myuniquelibname_add”。 C世界中有哪些常见的解决方案?您是否将所有api操作放在结构中,以便客户端可以选择自己的前缀?
我非常期待通过你的答案得到的见解!
亲爱的答案者,谢谢你的回答!我现在看到,前缀是安全避免命名冲突的唯一方法。所以,我想修改我的问题:我有什么可能,让客户选择他自己的前缀?
发布的答案Unwind是一种方式。它不使用正常意义上的前缀,但必须为每个api调用添加前缀“api>”。还有哪些解决方案(比如使用#define)?
这一切归结为两种方法之一:
我不接受任何答案,因为我认为没有正确答案。选择的解决方案取决于具体情况和自己的偏好。我自己会尝试你提到的所有方法,以找出最适合我的情况。您可以在相应答案的评论中发布支持或反对某些appraoches的论据。
最后,我要特别感谢:
如果有人发现关闭这个问题是合适的(因为没有进一步的见解),他/她应该随意这样做 - 我无法做出决定,因为我不是C大师。
答案 0 :(得分:15)
我不是C大师,但是从我使用的库中,使用前缀来分隔函数是很常见的。
例如,SDL将使用SDL,OpenGL将使用gl等...
答案 1 :(得分:9)
Ken提到的结构方式看起来像这样:
struct MyCoolApi
{
int (*add)(int x, int y);
};
MyCoolApi * my_cool_api_initialize(void);
然后客户会这样做:
#include <stdio.h>
#include <stdlib.h>
#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
名称(称为“struct tag”)必须是唯一的,并且您不能声明自己有用的嵌套结构。它适用于收集函数,并且是您在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;
}
答案 2 :(得分:6)
你被C ++吓到了,这是一种耻辱,因为它有命名空间来处理这个问题。在C中,你几乎只限于使用前缀 - 你当然不能“将api操作放在结构中”。
编辑:在回答您关于允许用户指定自己的前缀的第二个问题时,我会像瘟疫一样避免它。 99.9%的用户会对你提供的任何前缀感到满意(假设它不是太愚蠢),并且在他们必须跳过以满足剩余的0.1%的篮球(宏,结构,等等)时会非常不安。 / p>
答案 3 :(得分:4)
作为库用户,您可以通过预处理器轻松定义自己的缩短命名空间;结果看起来有点奇怪,但它确实有效:
#define ns(NAME) my_cool_namespace_ ## NAME
可以写
ns(foo)(42)
而不是
my_cool_namespace_foo(42)
作为图书馆作者,您可以提供缩写名称as desribed here。
如果你按照unwinds's advice并创建一个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;
}
答案 4 :(得分:2)
不幸的是,没有确定的方法可以避免C语言中的名称冲突。由于缺少命名空间,因此您需要为全局函数和变量的名称添加前缀。大多数图书馆都会选择一些简短的“唯一”前缀( unique 在引号中有明显的原因),并希望不会发生冲突。
需要注意的一点是,库的大多数代码都可以静态声明 - 这意味着它不会与其他文件中类似命名的函数冲突。但出口功能确实必须仔细加上前缀。
答案 5 :(得分:1)
由于您使用相同名称公开函数,因此客户端不能包含库头文件以及具有名称冲突的其他头文件。在这种情况下,您可以在函数原型之前在头文件中添加以下内容,这也不会影响客户端使用。
#define add myuniquelibname_add
请注意,这是一个快速解决方案,应该是最后一个选项。
答案 6 :(得分:1)
对于struct方法的一个非常大的例子,看看Linux内核;这种风格的30多万行C。
答案 7 :(得分:0)
前缀只能在C级别上选择。
在某些平台上(支持链接器的单独命名空间,如Windows,OS X和一些商业统一,但不支持Linux和FreeBSD),您可以通过在库中填充代码来解决冲突,并且只从库中导出符号真的需要。 (例如,如果导出符号中存在冲突,则在importlib中出现别名)