2012-11-04 1 views
1

Mon projet actuel est une bibliothèque de taille moyenne qui doit avoir une interface C et C++ en même temps. Il est centré sur un seul type de données que je veux être accessible depuis les fonctions C et C++, parce que je veux encourager les tiers à étendre la bibliothèque en écrivant des fonctions dans les deux langues.Comment concevoir une bibliothèque avec des interfaces parallèles en C et C++

Je sais sur les bases de C/C++ mélange (comparer par exemple http://www.parashift.com/c++-faq-lite/mixing-c-and-cpp.html) et ont mis au point la solution suivante:

Mes centres de conception de base autour de la création d'une struct en C avec toutes les données exposées (ce qui est ce que mes programmeurs en C attendent) et en dériver une classe qui cache l'accès des membres, ce qui, espérons-le, conduira à un accès plus sûr à la structure pour les programmeurs C++. Le problème vient avec la dérivation: Je veux utiliser les espaces de noms en C++ et cacher l'interface C. Bien sûr, la structure C elle-même ne peut pas être cachée (sans recourir à l'idiome PIMPL), mais ça me convient. L'exemple de code suivant compile et s'exécute sans erreurs apparentes avec les programmes "client" C et C++. Cependant, je me demande si cette solution est valide ou s'il existe de meilleures solutions.

code Exemple:

#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 

Répondre

3

En fait, une autre façon serait d'écrire le code complet en C++ et écrire seulement une interface mince C sur ce, en utilisant les données techniques qui se cachent.

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

Et dans un en-tête C-compatible:

#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 

Avec la mise en œuvre d'accompagnement:

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

Les deux principaux avantages sont les suivants:

  • C en pleine activité ++ code, avec des données entièrement encapsulées et tout le goodne art d'un fort système de type
  • stabilité binaire dans les versions plus facile dans l'interface C

Note: voici comment traiter Clang et LLVM avec compatibilité C, par exemple.

+0

Merci beaucoup @ matthieu-m. C'est une approche très sensée et vous avez raison sur ses avantages. Malheureusement, il faut que le côté C utilise des méthodes accesseur pour obtenir/définir les données de la propriété - ce que mes utilisateurs n'accepteront pas: -/ –

+0

@MarkAsbach: Eh bien, au lieu d'un 'void *' vous pouvez remplir un 'struct' si vous le souhaitez ... mais vous ne pouvez pas appliquer d'invariants avec une structure' '' 'et la compatibilité binaire sort de la fenêtre. –

Questions connexes