Elegancko wywołaj C++ z C

Tworzymy jakiś projekt w zwykły C (C99). Ale mamy jedną bibliotekę jako kody źródłowe (biblioteka matematyczna) w C++. Potrzebujemy tej biblioteki, więc chciałbym zapytać, jaki jest najbardziej elegancki sposób integracji tych kodów źródłowych?

Stosunek wielkości C i C++ jest 20:1, więc przejście do C++ nie jest opcją. Powinniśmy użyć biblioteki statycznej? / Align = "left" / (Wszystko jest na Windows).

Author: Cartesius00, 2011-09-02

4 answers

EDIT: bazując na dyskusji w komentarzu, powinienem zauważyć, że rozdzielanie rzeczy na zgodne z C struct duck i pochodne class Duck jest prawdopodobnie niepotrzebne. Prawdopodobnie można bezpiecznie załadować implementację do struct duck i wyeliminować class Duck, eliminując w ten sposób real(…). Ale nie znam C++ na tyle dobrze (w szczególności, sposób, w jaki współdziała z uniwersum C), aby dać ostateczną odpowiedź na to pytanie.


Nie ma powodu, dla którego nie możesz po prostu połączyć całego kodu C i C++ razem w jedną całość.

Interfejs do kodu C++ wymaga zawinięcia API C++ w API C. Można to zrobić deklarując kilka funkcji wewnątrz extern "C" { ... } podczas kompilacji kodu C++ oraz bez deklaracji extern podczas kompilacji kodu klienta C. Np.:

#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

Możesz zdefiniować strukturę kaczki w swoim źródle C++, a nawet dziedziczyć z niej prawdziwą klasę Duck:

struct duck { };

class Duck : public duck {
public:
    Duck(int feet);
    ~Duck();

    void quack(float volume);
};

inline Duck* real(duck* d) { return static_cast<Duck*>(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); }
 53
Author: Marcelo Cantos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-18 23:28:59

Biblioteka matematyczna C++ może być zaimplementowana w klasach użyteczności for (tylko członkowie statyczni). W tym przypadku można przyjąć znacznie prostsze podejście:

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

Nagłówek interfejsu C będzie wtedy:

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

I odpowiadającą jej implementacją może być:

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); }
Ale może to jest oczywiste....
 7
Author: A. Robert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-06 13:55:57

Jedynym powodem, dla którego chcemy dziedziczyć po strukturze duck, byłoby ujawnienie niektórych jej atrybutów W C API, co i tak jest ogólnie uważane za zły styl. Bez dziedziczenia Twój nagłówek C wyglądałby tak:

struct Duck;

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

I to byłaby odpowiednia implementacja, bez potrzeby odlewów typu:

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); }

W ten sam sposób można utworzyć C API dla interfejsu C++ (pure virtual class) i jego implementacji. W takim przypadku tylko konstruktor musi być oparty na konkretną realizację (np. new_RubberDuck(2)). Destruktor i wszystkie inne funkcje będą działać automatycznie na poprawnej implementacji, tak samo jak w C++.

 6
Author: A.Robert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-06 13:29:58

Istnieje sposób na stworzenie "hakera", który pozwala na bezpośrednie wywołanie funkcji Członkowskich niektórych obiektów.

Pierwszą rzeczą, którą musisz zrobić, jest utworzenie extern "C" funkcji fabrycznej, która zwraca wskaźnik (jako void*) do obiektu.

Drugą rzeczą, której potrzebujesz, jest zniekształcona nazwa funkcji członka.

Następnie można wywołać funkcję używając zniekształconej nazwy i przekazać wskaźnik zwrócony z funkcji fabrycznej jako pierwszy kłótnia.

Zastrzeżenia:

  • oczywiście nie będzie działać wywołanie funkcji członka, która chce innych obiektów, referencji lub innych rzeczy C++, lub funkcji zwracających obiekty lub typy niezgodne z typami C
  • nie będzie działać na wirtualnych funkcjach członkowskich, i prawdopodobnie nie na obiektach z funkcjami wirtualnymi w nich, nawet jeśli nie jest to funkcja wirtualna wywoływana
  • nazwa zniekształcona musi być prawidłowym symbolem C
  • Any many many więcej...
To nie jest coś, co polecam, wręcz przeciwnie. Stanowczo odradzam zrobienie czegoś takiego, co jest opisane w tej odpowiedzi. Jest to nieobsługiwane i prawdopodobnie nieokreślone zachowanie i może pękać w dziwny i nieprzewidywalny sposób.
 0
Author: Some programmer dude,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-09 11:48:25