48 votes

Appeler élégamment C ++ à partir de C

Nous développons des projets en clair C (C99). Mais nous avons une bibliothèque sous forme de codes sources (bibliothèque mathématique) en C++ . Nous avons besoin de cette bibliothèque et j'aimerais donc savoir quelle est la manière la plus élégante d'intégrer ces codes sources.

Le rapport entre les tailles de C et C++ est 20:1 alors passer à C++ n'est pas une option. Devrions-nous utiliser une bibliothèque statique? DLL? (Tout est sous Windows).

56voto

Marcelo Cantos Points 91211

EDIT: sur la Base de discussions dans les commentaires, je me dois de souligner que le fait de séparer les choses dans une C-compatible, struct duck et un dérivé class Duck est probablement inutile. Vous pouvez probablement vous en toute sécurité à la pelle la mise en œuvre en struct duck et d'éliminer class Duck, ce qui écarterait real(…). Mais je ne sais pas C++ assez bien (en particulier, la façon dont il interagit avec le C de l'univers) pour offrir une réponse définitive sur ce point.


Il n'y a aucune raison que vous ne pouvez pas simplement un lien vers tous vos code C et C++ en un seul binaire.

L'interfaçage avec le code C++ exige que vous enveloppez de l'API C++ en C API. Vous pouvez le faire en déclarant un tas de fonctions à l'intérieur d' extern "C" { ... } lors de la compilation du code C++, et sans la déclaration extern lors de la compilation du C du code client. E. g.:

#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

Vous pouvez définir le canard struct en C++ source, et même hériter du réel Duck class:

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

7voto

Arnd Strube Points 29

La seule raison de vouloir hériter du canard struct serait de les exposer certains de ses attributs dans l'API C, qui est généralement considéré comme mauvais style de toute façon. Sans héritage, votre en-tête C ressemblerait à ceci:

struct Duck;

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

Et ce serait la mise en œuvre correspondant, sans avoir besoin de type de conversions:

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

De la même manière, une API C peut être créé pour une interface C++ (classe virtuelle pure) et sa mise en oeuvre. Dans ce cas, seul le constructeur doivent être fondées sur la mise en œuvre concrète (par exemple, new_RubberDuck(2)). Le destructeur et toutes les autres fonctions fonctionnera automatiquement sur la bonne mise en œuvre, de même qu'en C++.

7voto

A. Robert Points 21

Une bibliothèque mathématique C ++ peut très bien être implémentée dans les classes d'utilitaires (membres statiques uniquement). Dans ce cas, une approche beaucoup plus simple pourrait être adoptée:

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

L'en-tête de l'interface C serait alors:

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

Et l'implémentation correspondante pourrait être:

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

Mais peut-être que c'est dire l'évidence ....

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X