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