Элегантно вызывать C ++ из C

Мы разрабатываем некоторый проект в простой C (C99). Но у нас есть одна библиотека в качестве исходных кодов (математическая библиотека) на C++ . Нам нужна эта библиотека, поэтому я хотел бы спросить, что является самым элегантным способом интеграции этих исходных кодов?

Соотношение между размерами C и C++ составляет 20:1 поэтому переход на C++ не является опцией. Должны ли мы использовать статическую библиотеку? DLL? (Это все в Windows).

EDIT: На основе обсуждения в комментарии я должен указать, что разделение вещей на C-совместимую struct duck и производный class Duck , вероятно, не нужно. Вероятно, вы можете безопасно перевернуть реализацию в struct duck и исключить class Duck , тем самым избавившись от real(…) . Но я не знаю C ++ достаточно хорошо (в частности, как он взаимодействует с вseleniumной C), чтобы дать окончательный ответ на это.


Нет причин, по которым вы не можете просто связать все ваши C и C ++-коды вместе в один бинарный файл.

Взаимодействие с кодом на C ++ требует, чтобы вы завернули API C ++ в API C. Вы можете сделать это, объявив кучу функций внутри extern "C" { ... } при компиляции кода C ++ и без объявления extern при компиляции кода клиента C. Например:

 #ifdef __cplusplus extern "C" { #endif typedef struct duck duck; duck* new_duck(int feet); void delete_duck(duck* d); void duck_quack(duck* d, float volume); #ifdef __cplusplus } #endif 

Вы можете определить структуру утки в источнике C ++ и даже наследовать от нее настоящий class Duck :

 struct duck { }; class Duck : public duck { public: Duck(int feet); ~Duck(); void quack(float volume); }; inline Duck* real(duck* d) { return static_cast(d); } duck* new_duck(int feet) { return new Duck(feet); } void delete_duck(duck* d) { delete real(d); } void duck_quack(duck* d, float volume) { real(d)->quack(volume); } 

Математическая библиотека C ++ вполне может быть реализована в classах служебных программ (только для статических членов). В этом случае можно было бы сделать гораздо более простой подход:

 class FPMath { public: static double add(double, double); static double sub(double, double); static double mul(double, double); static double div(double, double); }; 

Тогда заголовок для интерфейса C:

 double FPMath_add(double, double); double FPMath_sub(double, double); double FPMath_mul(double, double); double FPMath_div(double, double); 

И соответствующая реализация может быть:

 double FPMath_add(double a, double b) { return FPMath::add(a, b); } double FPMath_sub(double a, double b) { return FPMath::sub(a, b); } double FPMath_mul(double a, double b) { return FPMath::mul(a, b); } double FPMath_div(double a, double b) { return FPMath::div(a, b); } 

Но, возможно, это говорит о явном ….

Единственной причиной, по которой нужно унаследовать структуру утки, было бы разоблачить некоторые ее атрибуты в API C, которые в любом случае считаются плохими. Без наследования ваш заголовок C будет выглядеть так:

 struct Duck; struct Duck* new_Duck(int feet); void delete_Duck(struct Duck* d); void Duck_quack(struct Duck* d, float volume); 

И это была бы соответствующая реализация, без необходимости приведения типов:

 extern "C" { #include "Duck.h" } class Duck { public: Duck(int feet) : {} ~Duck() {} void quack(float volume) {} }; struct Duck* new_Duck(int feet) { return new Duck(feet); } void delete_Duck(struct Duck* d) { delete d; } void Duck_quack(struct Duck* d, float volume) { d->quack(volume); } 

Точно так же C API может быть создан для интерфейса C ++ (чистый виртуальный class) и его реализации. В этом случае только конструктор должен основываться на конкретной реализации (например, new_RubberDuck (2)). Деструктор и все другие функции будут автоматически работать с правильной реализацией, как и на C ++.

Существует способ создания «хака», который позволяет вам напрямую вызвать функции-члены некоторых объектов.

Первое, что вам нужно сделать, это создать extern "C" фабричную функцию extern "C" , которая возвращает указатель (как void* ) на объект.

Второе, что вам нужно, – это искаженное имя функции-члена.

Затем вы можете вызвать функцию, используя измененное имя, и передать указатель, возвращенный из заводской функции, в качестве первого аргумента.

Предостережения:

  • Конечно, не будет работать функция вызывающего члена, которая хочет другие объекты, ссылки или другие материалы C ++, или функции, возвращающие объекты или типы, не совместимые с типами C
  • Не будет работать над виртуальными функциями-членами и, возможно, не на объектах с виртуальными функциями, даже если это не виртуальная функция, вызываемая
  • Исправленное имя должно быть действительным символом C
  • Любые многие другие …

Это не то, что я рекомендую, совершенно наоборот. Я настоятельно рекомендую не делать что-то вроде изложенного в этом ответе. Это неподдерживаемое и, вероятно, неопределенное поведение и может нарушить странные и непредсказуемые способы.

  • Обнаружить, если метод был переопределен с помощью Reflection (C #)
  • Используйте Unity API из другого streamа или вызовите функцию в главной теме
  • Как преобразовать массив байтов в строку
  • Как правильно отменить регистрацию обработчика событий
  • О развитии векторов
  • Как найти веб-браузер по умолчанию с помощью C #?
  • Как десериализовать объект JSON с недопустимым именем поля в нем
  • Dispatcher Invoke (...) против BeginInvoke (...) путаницы
  • String.Format целое число, чтобы использовать разделитель тысяч без десятичных знаков или число 0 для малых целых чисел
  • Не удалось найти устанавливаемый ISAM
  • статический член constexpr того же типа, что и class
  • Interesting Posts

    Android: сохранение имени пользователя и пароля?

    Как автоматически указывать заголовки в Microsoft Word?

    Как загрузить ImageView по URL в Android?

    Статический IP и DHCP на одном сетевом адаптере в Windows 7

    Большой запрос веб-службы WCF с ошибкой (400) Ошибка HTTP-запроса

    Как предоставить пространство между двумя ячейками в виде таблицы?

    Как случайным образом выбрать элемент из массива

    C # Entity-Framework: как я могу объединить .Find и .Include в объекте модели?

    Каков правильный способ отправки HTTP 404-ответа из действия ASP.NET MVC?

    Зарегистрировать 32-битную COM-DLL до 64 бит Windows 7

    Нужно ли назначать строку переменной перед ее сопоставлением с другим?

    Сортировка списка из другого идентификатора списка

    Повреждают ли драйверы Nvidia и Catalyst друг с другом?

    Есть ли ограничение, которое ограничивает мой общий метод численными типами?

    Как я могу написать псевдоним PowerShell с аргументами в середине?

    Давайте будем гением компьютера.