Написание библиотеки с интерфейсами C и С++, какой способ ее переноса?
При подготовке библиотеки (назовем ее libfoo), я обнаружил, что мне представлена следующая дилемма: я пишу ее как библиотеку С++ с оберткой C:
namespace Foo {
class Bar {
...
};
}
/* Separate C header. #ifdef __cplusplus omitted for brevity. */
extern "C" {
typedef void *FooBar;
FooBar* foo_bar_new() { return new Foo::Bar; }
void foo_bar_delete(FooBar *bar) { delete bar; }
}
Или лучше написать его как библиотеку C с оберткой С++:
/* foo/bar.h. Again, #ifdef __cplusplus stuff omitted. */
typedef struct {
/* ... */
} FooBar;
void foo_bar_init(FooBar *self) { /* ... */ }
void foo_bar_deinit(FooBar *self) { /* ... */ }
/* foo/bar.hpp */
namespace Foo {
class Bar {
/* ... */
FooBar self;
}
Bar::Bar() {
foo_bar_init(&self);
}
Bar::~Bar() {
foo_bar_deinit(&self);
}
}
Что вы предпочитаете и почему? Я одобряю последнее, потому что это означает, что мне не нужно беспокоиться о том, что мои функции C случайно имеют исключения, плюс я предпочитаю C как язык, поскольку я чувствую, что это меньшее семантическое минное поле. Что думают другие люди?
РЕДАКТИРОВАТЬ: так много хороших ответов. Спасибо всем. Это позор, что я могу только принять его.
Ответы
Ответ 1
Маленькие точки:
Когда вы пишете библиотеку C, это полезно где угодно: в C, на С++ (с оберткой) и на многих других языках, таких как Python, Java, с использованием привязок и т.д., и наиболее важно для этого требуется только время выполнения C.
Когда вы пишете С++-оболочку, вам также нужно написать оболочку C, но это не так просто, как вы думаете, например:
c_api.h:
extern "C" {
typedef void *Foo;
Foo create_foo();
}
c_api.cpp:
void *create_foo()
{
return new foo::Foo();
}
Что не так? он может бросить! и программа выйдет из строя, так как C не имеет стека
развязывающая семантика. Поэтому вам нужно что-то вроде:
void *create_foo()
{
try {
return new foo::Foo();
}
catch(...) { return 0; }
}
И это для каждой функции С++ api.
Поэтому я считаю, что создание библиотеки C и предоставление отдельной С++-оболочки - лучшее решение.
Также не требуется связывание с библиотекой времени выполнения С++.
Ответ 2
Напишите библиотеку на языке, который вы предпочитаете писать в библиотеках. Технически не важно, каким образом вы обертываете. Хотя некоторые проекты C могут быть направлены на исключение библиотек, которые не являются C, тогда было бы странным, если проект С++ исключает библиотеки, написанные на C, что в основном является философским возражением, чем практическим.
Обертка C в оболочке С++, скорее всего, приведет к немного большей оболочке, но будет более приемлемой для программистов C.
Обратите внимание, что если вы распространяете двоичные файлы, упрощение C выгодно.
Ответ 3
Если вы предпочитаете писать на C, зачем вам С++-оболочку? Клиент С++ может использовать интерфейс API C-стиля. С другой стороны, вы предпочитаете С++, для C-клиентов необходимо иметь C-оболочку.
Ответ 4
Если ваша библиотека будет когда-либо распространяться как двоичный + заголовок (вместо отправки исходного кода), вы обнаружите, что API C более универсально связывается, поскольку C обычно является наименьший общий API на любой платформе.
Вот почему мне обычно приходилось создавать C API с встроенными обертками С++ вокруг проектов, которые я делал за последнее десятилетие, которым нужен API. Поскольку все программы были на С++, это означало, что я должен был создать API-интерфейс обертки C вокруг кода С++, просто чтобы поместить вокруг него еще один обертывающий С++ API.
Ответ 5
Предполагая компиляцию без оптимизации времени ссылки, компилятор C не может встроить функции-обертки, поскольку он не знает, как обрабатывать вызовы С++, но компилятор С++ может легко встроить вызовы C.
Поэтому было бы неплохо создать С++-оболочку для библиотеки C, а не наоборот.
Ответ 6
Я лично предпочитаю использовать С++ и переносить его на C.
Но наполните это вопросом вкуса, и вам нужно будет принять собственное решение, как вам это понравится. Если вы чувствуете себя более комфортно, записывая библиотеку в C, переходите к ней и оберните ее для С++.
Об исключениях: вы можете поймать их в каждой функции, завернутой для C, и вернуть код ошибки для них, например. имея собственный класс исключений, который уже имеет числовое значение кода ошибки, которое вы можете вернуть к своим функциям C, другие, которые могли быть выброшены другими библиотеками, могут быть переведены на что-то еще, однако вы все равно должны были их поймать.
Ответ 7
Если вам комфортно писать свою библиотеку в C, сделайте это. Он будет более переносимым как библиотека C и не будет проблем с исключениями, как вы упомянули. Необычно начинать с библиотеки С++ и переносить ее на C.
Ответ 8
Это также сильно зависит от того, что вы планируете использовать в своей библиотеке. Если это в свою очередь может принести пользу другим С++-библиотекам, тогда используйте С++.
Можно также утверждать, что если ваша библиотека будет очень большой (внутренне, не обязательно API-интерфейс), ее проще реализовать на С++. (Это не моя чашка чая, я предпочитаю C, но некоторые люди клянутся С++.)
Также имейте в виду, что С++ использует среду выполнения, которая в значительной степени требует операционную систему для поддержки исключений.
Если вы предполагаете, что ваша библиотека будет использоваться в качестве основы для операционной системы или использоваться в средах без операционной системы, вам либо нужно знать, как отключить поддержку исключений, избегая многого (всего?) STL и предоставить свой собственный распределитель и освободитель. Это не невозможно, но вам нужно точно знать, что вы делаете.
C больше подходит для тех вещей низкого уровня.
Ответ 9
Лично я предпочитаю писать его на С++, а затем выставлять интерфейс C с помощью обертки.
В основном потому, что я предпочитаю писать на надлежащем языке OO. Я бы использовал обертку OO style C too, liek, которую я изложил в этом посте, я написал довольно подробное объяснение того, что вам нужно, чтобы вызвать OO С++ из C в этом сообщении Разработка C API-интерфейс обертки для объектно-ориентированного кода на С++