C库命名约定

时间:2010-01-01 14:27:28

标签: c naming-conventions

简介

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

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

问题

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

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

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

编辑(修改后的问题)

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

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

编辑2(状态更新)

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

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

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

最后,我要特别感谢:

  • Unwind - 他的复杂答案包括“struct-method”的全面实施
  • Christoph - 感谢他的回答并指出Namespaces in C
  • 所有其他人 - 为您提供出色的意见

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

8 个答案:

答案 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中出现别名)