Как создать библиотеку с интерфейсами paralell на C и C++

Мой текущий проект представляет собой библиотеку среднего размера, которая должна иметь интерфейс C и C++ одновременно. Он основан на единственном типе данных, который я хочу сделать доступным из функций C и C++, потому что я хочу призвать третьи стороны расширять библиотеку, написав функции на любом языке.

Я знаю об основах смешивания C/C++ (сравните, например, http://www.parashift.com/c++-faq-lite/mixing-c-and-cpp.html) и придумала следующее решение:

Мой базовый дизайн сконцентрирован на создании структуры в C со всеми открытыми данными (именно этого ожидают мои программисты на C) и получении от нее класса, который скрывает доступ к элементам, что, как мы надеемся, приведет к более безопасному доступу к структуре для программистов на C++. Проблема приходит с выводом: я хочу использовать пространства имен в C++ и скрыть интерфейс C. Конечно, сама структура C не может быть скрыта (без обращения к идиоме PIMPL), но это хорошо для меня.

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

Пример кода:

#ifdef __cplusplus__
extern "C" {
#endif

struct base
{
    char * data;
}

#ifdef __cplusplus__
} // extern "C"
namespace {
extern "C" {
#endif

/* cleanly initialize struct */
struct base * new_base (struct base *);

/* cleanly destroy struct */
void del_base (struct base *);

#ifdef __cplusplus__
} } // namespace, extern "C"

#include<new>

namespace safe {

class base_plus : private base
{
public:
    base_plus () 
    { 
        if (! new_base(this)) 
            throw std::bad_alloc ();
    }

    ~base_plus ()
    {
        del_base (this);
    }
};

} // namespace safe

#endif

1 ответ

Решение

На самом деле, другим способом было бы написать полный код на C++ и написать только интерфейс C slim, используя технику сокрытия данных.

namespace Foo {
    class Bar {
    public:
        int property1() const;
        std::string const& property2() const;
    };
}

И в C-совместимом заголовке:

#ifdef __cplusplus__
extern "C" {
#endif

typedef void* Bar;

Bar foo_bar_new(int i, char const* s);

void foo_bar_delete(Bar b);

int foo_bar_property1(Bar b);

char const& foo_bar_property2(Bar b);

#ifdef __cplusplus__
}
#endif

С сопутствующей реализацией:

Bar foo_bar_new(int i, char const* s) {
    return new Foo::Bar(i, s);
}

void foo_bar_delete(Bar b) {
    delete static_cast<Foo::Bar*>(b);
}

int foo_bar_property1(Bar b) {
    return static_cast<Foo::Bar*>(b)->property1();
}

char const* foo_bar_property2(Bar b) {
    return static_cast<Foo::Bar*>(b)->property2().c_str();
}

Два основных преимущества:

  • Полноценный код C++, с полностью инкапсулированными данными и всеми преимуществами более сильной системы типов
  • Двоичная стабильность между выпусками упрощена в интерфейсе C

Примечание: так Clang и LLVM работают, например, с C-совместимостью.

Другие вопросы по тегам