Соглашения о присвоении имен библиотеки C

Введение

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

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

Вопрос

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

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

Я очень с нетерпением жду понимания, я прохожу через Ваши ответы!

РЕДАКТИРОВАНИЕ (измененный вопрос)

Дорогие Отвечающие стороны, спасибо за Ваши ответы! Я теперь вижу, что префиксы являются единственным способом безопасно избежать конфликтов имен. Так, я хотел бы к modifiy свой вопрос: Какие возможности я имею, чтобы позволить клиенту выбрать свой собственный префикс?

Ответ Раскручивается отправленный, один путь. Это не использует префиксы в нормальном смысле, но нужно снабдить префиксом каждый вызов API "API->". Что дальнейшие решения там (как использование #define, например)?

ОТРЕДАКТИРУЙТЕ 2 (обновление статуса)

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

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

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

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

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

16
задан Community 23 May 2017 в 10:29
поделиться

8 ответов

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

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

.
16
ответ дан 30 November 2019 в 16:24
поделиться

Структурный способ, который упоминает 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 (называемое "тег структуры") должно быть уникальным, и вы не можете объявлять вложенные структуры, которые полезны сами по себе. Тем не менее, это хорошо работает для сбора функций, и это методика, которую вы довольно часто видите в C.

UPDATE: Вот как может выглядеть сторона реализации, это было запрошено в комментарии:

#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;
}
10
ответ дан 30 November 2019 в 16:24
поделиться

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

Edit: В ответ на ваш второй вопрос о том, чтобы разрешить пользователям указывать свой префикс, я бы избежал этого, как чума. 99.9% пользователей будут довольны любым префиксом, который вы предоставите (предполагая, что это не слишком глупо) и будут очень UNHAPPY в обручах (макросы, структуры, что угодно), через которые им придется прыгать, чтобы удовлетворить оставшимся 0.1%

.
6
ответ дан 30 November 2019 в 16:24
поделиться

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

#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;
}
4
ответ дан 30 November 2019 в 16:24
поделиться

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

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

2
ответ дан 30 November 2019 в 16:24
поделиться

Для действительно огромного примера метода struct посмотрите на ядро Linux; 30 с лишним миллионов строк Си в этом стиле.

1
ответ дан 30 November 2019 в 16:24
поделиться

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

#define add myuniquelibname_add

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

.
1
ответ дан 30 November 2019 в 16:24
поделиться

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

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

.
0
ответ дан 30 November 2019 в 16:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: