Мой текущий проект представляет собой библиотеку среднего размера, которая должна иметь интерфейс 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
На самом деле, другим способом было бы написать полный код на 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();
}
Два основных преимущества:
Примечание: так Clang и LLVM работают, например, с C-совместимостью.
Других решений пока нет …