Ответ 1
Я не C гуру, но из библиотек, которые я использовал, довольно часто используется префикс для разделения функций.
Например, SDL будет использовать SDL, OpenGL будет использовать gl и т.д.
Привет, ребята, я недавно научился программировать на C! (Это был огромный шаг для меня, поскольку С++ был первым языком, с которым я связался и напугал меня почти 10 лет.) Исходя из основного фона OO (Java + С#), это был очень приятный сдвиг парадигмы.
Я люблю C. Это такой красивый язык. Что меня больше всего удивило, это высокий уровень модульности и поддержки повторного использования кода C - конечно, это не так высоко, как на языке OO, но все же далеко за пределами моих ожиданий относительно императивного языка.
Как предотвратить конфликты имен между клиентским кодом и кодом библиотеки C? В Java есть пакеты, на С# есть пространства имен. Представьте себе, что я пишу библиотеку C, которая предлагает операцию "добавить". Весьма вероятно, что клиент уже использует операцию, называемую так: что мне делать?
Я особенно ищу дружелюбное решение. Например, я не хотел бы префикс всех моих операций api, таких как myuniquelibname_add. Каковы общие решения этого в мире C? Вы помещаете все операции api в структуру, поэтому клиент может выбрать собственный префикс?
Я очень с нетерпением жду того, как я получаю ответы на ваши вопросы!
Уважаемые участники, спасибо за ваши ответы! Теперь я вижу, что префиксы - единственный способ безопасно избежать конфликтов имен. Итак, я хотел бы модифицировать мой вопрос: Какие у меня есть возможности, чтобы клиент мог выбрать свой префикс?
Ответ Unwind, это один из способов. Он не использует префиксы в обычном смысле, но нужно приписать каждый api-вызов "api- > ". Какие существуют другие решения (например, с использованием #define)?
Все это сводится к одному из двух подходов:
Я не буду принимать никакого ответа, потому что я думаю, что нет правильного ответа. Решение, которое выбирается, зависит от конкретного случая и от собственных предпочтений. Я, сам по себе, опробую все подходы, которые вы упомянули, чтобы узнать, что мне подходит лучше всего в этой ситуации. Не стесняйтесь публиковать аргументы за или против определенных оценок в комментариях соответствующих ответов.
Наконец, я хотел бы особо поблагодарить:
Если кто-то сочтет нужным закрыть этот вопрос (как не ожидая дальнейших ожиданий), он/она должен быть свободен делать это - я не могу это решить, поскольку я не являюсь гуру.
Я не C гуру, но из библиотек, которые я использовал, довольно часто используется префикс для разделения функций.
Например, SDL будет использовать SDL, OpenGL будет использовать gl и т.д.
Структурный способ, который упоминает Кен, будет выглядеть примерно так:
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;
}
Это позор, который вы испугали на С++, поскольку у него есть пространства имен, чтобы справиться именно с этой проблемой. В C вы в значительной степени ограничены использованием префиксов - вы, безусловно, не можете "поместить операции api в структуру".
Изменить: В ответ на ваш второй вопрос относительно того, как пользователи могут указать свой собственный префикс, я бы избегал его, как чума. 99,9% пользователей будут довольны любым префиксом, который вы предоставляете (при условии, что он не слишком глупый), и будет очень НЕОПРЕДЕЛЕННО на обручах (макросы, структуры и т.д.), Которые им придется преодолевать, чтобы удовлетворить оставшиеся 0,1%.
Как пользователь библиотеки, вы можете легко определить свои сокращенные пространства имен через препроцессор; результат будет немного странным, но он работает:
#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;
}
К сожалению, нет уверенного способа избежать столкновений имен в C. Поскольку в нем не хватает пространств имен, вы остаетесь с префиксом имен глобальных функций и переменных. Большинство библиотек выбирают короткий и "уникальный" префикс (уникальные в кавычках по понятным причинам), и надеемся, что никаких столкновений не произойдет.
Следует отметить, что большая часть кода библиотеки может быть статически объявлена - это означает, что она не будет сталкиваться с аналогичными именованными функциями в других файлах. Но экспортируемые функции действительно должны быть тщательно префиксны.
Поскольку вы открываете функции с тем же именем, клиент не может включать файлы заголовков библиотеки вместе с другими файлами заголовков, которые имеют коллизию имен. В этом случае перед прототипом функции вы добавите следующее в файл заголовка, и это не повлияет на использование клиента.
#define add myuniquelibname_add
Обратите внимание: это быстрое решение и должно быть последним вариантом.
Для действительно огромного примера метода struct взгляните на ядро Linux; 30-нечетный миллион строк C в этом стиле.
Префиксы - это только выбор на уровне C.
На некоторых платформах (поддерживающих отдельные пространства имен для компоновщиков, таких как Windows, OS X и некоторые коммерческие структуры, но не Linux и FreeBSD), вы можете обходить конфликты, набирая код в библиотеке и экспортировать только символы из библиотеки, которую вы очень нужно. (и, например, сглаживание в importlib в случае возникновения конфликтов в экспортируемых символах)