Написание библиотеки с интерфейсами 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-интерфейс обертки для объектно-ориентированного кода на С++