C 库命名约定

发布于 2024-08-16 22:30:12 字数 1403 浏览 4 评论 0原文

简介

大家好,我最近学习了 C 语言编程! (这对我来说是巨大的一步,因为 C++ 是第一种语言,我接触并害怕了近 10 年。)来自主要面向对象的背景(Java + C#),这是一个非常好的范式转变。

我喜欢 C。这是一门非常美丽的语言。最让我惊讶的是C支持的高级模块化和代码可重用性——当然它没有OO语言那么高,但仍然远远超出了我对命令式语言的期望。

问题

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

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

我非常期待通过您的回答获得见解!

编辑(修改后的问题)

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

答案放松 发布,是一种方式。它不使用通常意义上的前缀,但必须在每个 api 调用前加上“api->”前缀。还有哪些进一步的解决方案(例如使用#define)?

编辑2(状态更新)

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

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

我不会接受任何答案,因为我认为没有正确答案。一个人选择的解决方案取决于具体情况和自己的喜好。我自己会尝试您提到的所有方法,以找出在哪种情况下最适合我的方法。请随意在相应答案的评论中发表支持或反对某些方法的论点。

最后,我要特别感谢:

如果有人认为结束这个问题是合适的(因为没有进一步的见解可期待),他/她应该随意这样做 -我无法决定这一点,因为我不是 C 大师。

Introduction

Hello folks, I recently learned to program in C! (This was a huge step for me, since C++ was the first language, I had contact with and scared me off for nearly 10 years.) Coming from a mostly OO background (Java + C#), this was a very nice paradigm shift.

I love C. It's such a beautiful language. What surprised me the most, is the high grade of modularity and code reusability C supports - of course it's not as high as in a OO-language, but still far beyond my expectations for an imperative language.

Question

How do I prevent naming conflicts between the client code and my C library code? In Java there are packages, in C# there are namespaces. Imagine I write a C library, which offers the operation "add". It is very likely, that the client already uses an operation called like that - what do I do?

I'm especially looking for a client friendly solution. For example, I wouldn't like to prefix all my api operations like "myuniquelibname_add" at all. What are the common solutions to this in the C world? Do you put all api operations in a struct, so the client can choose its own prefix?

I'm very looking forward to the insights I get through your answers!

EDIT (modified question)

Dear Answerers, thank You for Your answers! I now see, that prefixes are the only way to safely avoid naming conflicts. So, I would like to modifiy my question: What possibilities do I have, to let the client choose his own prefix?

The answer Unwind posted, is one way. It doesn't use prefixes in the normal sense, but one has to prefix every api call by "api->". What further solutions are there (like using a #define for example)?

EDIT 2 (status update)

It all boils down to one of two approaches:

  • Using a struct
  • Using #define (note: There are many ways, how one can use #define to achieve, what I desire)

I will not accept any answer, because I think that there is no correct answer. The solution one chooses rather depends on the particular case and one's own preferences. I, by myself, will try out all the approaches You mentioned to find out which suits me best in which situation. Feel free to post arguments for or against certain appraoches in the comments of the corresponding answers.

Finally, I would like to especially thank:

  • Unwind - for his sophisticated answer including a full implementation of the "struct-method"
  • Christoph - for his good answer and pointing me to Namespaces in C
  • All others - for Your great input

If someone finds it appropriate to close this question (as no further insights to expect), he/she should feel free to do so - I can not decide this, as I'm no C guru.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

枫以 2024-08-23 22:30:12

我不是 C 专家,但从我使用过的库来看,使用前缀来分隔函数是很常见的。

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

I'm no C guru, but from the libraries I have used, it is quite common to use a prefix to separate functions.

For example, SDL will use SDL, OpenGL will use gl, etc...

知足的幸福 2024-08-23 22:30:12

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 标记”)需要是唯一的,并且您不能声明本身有用的嵌套结构。不过,它对于收集函数效果很好,并且是一种在 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;
}

The struct way that Ken mentions would look something like this:

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

MyCoolApi * my_cool_api_initialize(void);

Then clients would do:

#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;
}

This still has "namespace-issues"; the struct name (called the "struct tag") needs to be unique, and you can't declare nested structs that are useful by themselves. It works well for collecting functions though, and is a technique you see quite often in C.

UPDATE: Here's how the implementation side could look, this was requested in a comment:

#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;
}
满天都是小星星 2024-08-23 22:30:12

很遗憾你被 C++ 吓跑了,因为它有命名空间来精确处理这个问题。在 C 中,您几乎只能使用前缀 - 您当然不能“将 api 操作放入结构中”。

编辑:针对您关于允许用户指定自己的前缀的第二个问题,我会像避免瘟疫一样避免它。 99.9% 的用户会对您提供的任何前缀感到满意(假设它不是太傻),并且会对他们必须跳过才能满足剩余 0.1% 的需求(宏、结构等)感到非常不高兴。

It's a shame you got scared off by C++, as it has namespaces to deal with precisely this problem. In C, you are pretty much limited to using prefixes - you certainly can't "put api operations in a struct".

Edit: In response to your second question regarding allowing users to specify their own prefix, I would avoid it like the plague. 99.9% of users will be happy with whatever prefix you provide (assuming it isn't too silly) and will be very UNHAPPY at the hoops (macros, structs, whatever) they will have to jump through to satisfy the remaining 0.1%.

你没皮卡萌 2024-08-23 22:30:12

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

#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;
}

As a library user, you can easily define your own shortened namespaces via the preprocessor; the result will look a bit strange, but it works:

#define ns(NAME) my_cool_namespace_ ## NAME

makes it possible to write

ns(foo)(42)

instead of

my_cool_namespace_foo(42)

As a library author, you can provide shortened names as desribed here.

If you follow unwinds's advice and create an API structure, you should make the function pointers compile-time constants to make inlinig possible, ie in your .h file, use the follwoing code:

// 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;
}
怼怹恏 2024-08-23 22:30:12

不幸的是,没有确定的方法可以避免 C 中的名称冲突。由于它缺乏名称空间,因此您只能为全局函数和变量的名称添加前缀。大多数库都会选择一些简短且“唯一”的前缀(唯一由于明显的原因而在引号中),并希望不会发生冲突。

需要注意的一件事是,库的大部分代码都可以静态声明 - 这意味着它不会与其他文件中类似命名的函数发生冲突。但导出的函数确实必须小心地添加前缀。

Unfortunately, there's no sure way to avoid name clashes in C. Since it lacks namespaces, you're left with prefixing the names of global functions and variables. Most libraries pick some short and "unique" prefix (unique is in quotes for obvious reasons), and hope that no clashes occur.

One thing to note is that most of the code of a library can be statically declared - meaning that it won't clash with similarly named functions in other files. But exported functions indeed have to be carefully prefixed.

很快妥协 2024-08-23 22:30:12

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

#define add myuniquelibname_add

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

Since you are exposing functions with the same name client cannot include your library header files along with other header files which have name collision. In this case you add the following in the header file before the function prototype and this wouldn't effect client usage as well.

#define add myuniquelibname_add

Please note this is a quick fix solution and should be the last option.

绝不服输 2024-08-23 22:30:12

要获得 struct 方法的真正巨大示例,请查看 Linux 内核;这种风格的 C 代码有 30 多万行。

For a really huge example of the struct method, take a look at the Linux kernel; 30-odd million lines of C in that style.

万人眼中万个我 2024-08-23 22:30:12

前缀只是 C 级别的选择。

在某些平台(支持链接器的单独命名空间,如 Windows、OS X 和一些商业 unice,但不包括 Linux 和 FreeBSD)上,您可以通过将代码填充到库中来解决冲突,并且仅从您真正需要的库中导出符号。 (例如,在导出的符号中存在冲突的情况下,在导入库中使用别名)

Prefixes are only choice on C level.

On some platforms (that support separate namespaces for linkers, like Windows, OS X and some commercial unices, but not Linux and FreeBSD) you can workaround conflicts by stuffing code in a library, and only export the symbols from the library you really need. (and e.g. aliasing in the importlib in case there are conflicts in exported symbols)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文