Подтвердить что ты не робот

Соглашения об именах библиотек C

Введение

Привет, ребята, я недавно научился программировать на C! (Это был огромный шаг для меня, поскольку С++ был первым языком, с которым я связался и напугал меня почти 10 лет.) Исходя из основного фона OO (Java + С#), это был очень приятный сдвиг парадигмы.

Я люблю C. Это такой красивый язык. Что меня больше всего удивило, это высокий уровень модульности и поддержки повторного использования кода C - конечно, это не так высоко, как на языке OO, но все же далеко за пределами моих ожиданий относительно императивного языка.

Вопрос

Как предотвратить конфликты имен между клиентским кодом и кодом библиотеки C? В Java есть пакеты, на С# есть пространства имен. Представьте себе, что я пишу библиотеку C, которая предлагает операцию "добавить". Весьма вероятно, что клиент уже использует операцию, называемую так: что мне делать?

Я особенно ищу дружелюбное решение. Например, я не хотел бы префикс всех моих операций api, таких как myuniquelibname_add. Каковы общие решения этого в мире C? Вы помещаете все операции api в структуру, поэтому клиент может выбрать собственный префикс?

Я очень с нетерпением жду того, как я получаю ответы на ваши вопросы!

EDIT (измененный вопрос)

Уважаемые участники, спасибо за ваши ответы! Теперь я вижу, что префиксы - единственный способ безопасно избежать конфликтов имен. Итак, я хотел бы модифицировать мой вопрос: Какие у меня есть возможности, чтобы клиент мог выбрать свой префикс?

Ответ Unwind, это один из способов. Он не использует префиксы в обычном смысле, но нужно приписать каждый api-вызов "api- > ". Какие существуют другие решения (например, с использованием #define)?

EDIT 2 (обновление статуса)

Все это сводится к одному из двух подходов:

  • Использование структуры
  • Использование #define (примечание: существует много способов, как можно использовать #define для достижения, чего я хочу)

Я не буду принимать никакого ответа, потому что я думаю, что нет правильного ответа. Решение, которое выбирается, зависит от конкретного случая и от собственных предпочтений. Я, сам по себе, опробую все подходы, которые вы упомянули, чтобы узнать, что мне подходит лучше всего в этой ситуации. Не стесняйтесь публиковать аргументы за или против определенных оценок в комментариях соответствующих ответов.

Наконец, я хотел бы особо поблагодарить:

  • Unwind - за его сложный ответ, включая полную реализацию "структурного метода"
  • Christoph - за его хороший ответ и указав на Пространства имен в C
  • Все остальные - для вашего отличного ввода

Если кто-то сочтет нужным закрыть этот вопрос (как не ожидая дальнейших ожиданий), он/она должен быть свободен делать это - я не могу это решить, поскольку я не являюсь гуру.

4b9b3361

Ответ 1

Я не C гуру, но из библиотек, которые я использовал, довольно часто используется префикс для разделения функций.

Например, SDL будет использовать SDL, OpenGL будет использовать gl и т.д.

Ответ 2

Структурный способ, который упоминает Кен, будет выглядеть примерно так:

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

Ответ 3

Это позор, который вы испугали на С++, поскольку у него есть пространства имен, чтобы справиться именно с этой проблемой. В C вы в значительной степени ограничены использованием префиксов - вы, безусловно, не можете "поместить операции api в структуру".

Изменить: В ответ на ваш второй вопрос относительно того, как пользователи могут указать свой собственный префикс, я бы избегал его, как чума. 99,9% пользователей будут довольны любым префиксом, который вы предоставляете (при условии, что он не слишком глупый), и будет очень НЕОПРЕДЕЛЕННО на обручах (макросы, структуры и т.д.), Которые им придется преодолевать, чтобы удовлетворить оставшиеся 0,1%.

Ответ 4

Как пользователь библиотеки, вы можете легко определить свои сокращенные пространства имен через препроцессор; результат будет немного странным, но он работает:

#define ns(NAME) my_cool_namespace_ ## NAME

позволяет писать

ns(foo)(42)

вместо

my_cool_namespace_foo(42)

Как автор библиотеки, вы можете предоставить сокращенные имена как описано здесь.

Если вы следуете удалять советы и создаете структуру 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;
}

Ответ 5

К сожалению, нет уверенного способа избежать столкновений имен в C. Поскольку в нем не хватает пространств имен, вы остаетесь с префиксом имен глобальных функций и переменных. Большинство библиотек выбирают короткий и "уникальный" префикс (уникальные в кавычках по понятным причинам), и надеемся, что никаких столкновений не произойдет.

Следует отметить, что большая часть кода библиотеки может быть статически объявлена ​​- это означает, что она не будет сталкиваться с аналогичными именованными функциями в других файлах. Но экспортируемые функции действительно должны быть тщательно префиксны.

Ответ 6

Поскольку вы открываете функции с тем же именем, клиент не может включать файлы заголовков библиотеки вместе с другими файлами заголовков, которые имеют коллизию имен. В этом случае перед прототипом функции вы добавите следующее в файл заголовка, и это не повлияет на использование клиента.

#define add myuniquelibname_add

Обратите внимание: это быстрое решение и должно быть последним вариантом.

Ответ 7

Для действительно огромного примера метода struct взгляните на ядро ​​Linux; 30-нечетный миллион строк C в этом стиле.

Ответ 8

Префиксы - это только выбор на уровне C.

На некоторых платформах (поддерживающих отдельные пространства имен для компоновщиков, таких как Windows, OS X и некоторые коммерческие структуры, но не Linux и FreeBSD), вы можете обходить конфликты, набирая код в библиотеке и экспортировать только символы из библиотеки, которую вы очень нужно. (и, например, сглаживание в importlib в случае возникновения конфликтов в экспортируемых символах)