C library naming conventions

别来无恙 提交于 2019-12-02 18:11:48
phtrivier

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...

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

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%.

Christoph

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

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.

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.

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.

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)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!