8
votes

Emballage C ++ pour une utilisation en C #

OK, il existe fondamentalement un grand projet C ++ (refonte) que je veux envelopper Pour que je puisse l'utiliser dans mon projet C #.

J'essaie de le faire depuis un moment et c'est ce que j'ai jusqu'à présent. J'utilise C ++ / CLI pour envelopper les classes dont j'ai besoin pour que je puisse les utiliser dans c #. P>

Cependant, il y a une tonne de structures et d'énumjets dont j'aurai également besoin dans mon projet C #. Alors, comment puis-je envelopper ces? P>

La méthode de base que j'utilise actuellement est d'ajouter des appels DllexPort au code natif C ++, compilair à une DLL / LIB, en ajoutant ce projet de C ++ / CLI et importation Les en-têtes C ++, puis compilent le projet CLI dans une DLL, ajoutant enfin cette DLL comme référence à mon projet C #. J'apprécie toute aide. P>

Voici quelques codes..Je besoin de manière gérable de le faire depuis que le projet C ++ est si grand. P>

//**Native unmanaged C++ code
//**Recast.h

enum rcTimerLabel
{
   A,
   B,
   C
};

extern "C" {

class __declspec(dllexport) rcContext
{
   public:
   inline rcContect(bool state);
   virtual ~rcContect() {}
   inline void resetLog() { if(m_logEnabled) doResetLog(); }

   protected:
   bool m_logEnabled;
}

struct rcConfig
{
   int width;
   int height;
}

} // end of extern


// **Managed CLI code
// **MyWrappers.h
#include "Recast.h"

namespace Wrappers
{
   public ref class MyWrapper
   {
   private:
     rcContect* _NativeClass;
   public:
     MyWrapper(bool state);
     ~MyWrapper();
     void resetLog();
     void enableLog(bool state) {_NativeClass->enableLog(state); }
   };
}

//**MyWrapper.cpp
#include "MyWrappers.h"

namespace Wrappers
{
   MyWrapper::MyWrapper(bool state)
   {
      _NativeClass = new rcContext(state);
   }

   MyWrapper::~MyWrapper()
   {
      delete _NativeClass;
   }
   void MyWrapper::resetLog()       
   {
      _NativeClass->resetLog();
   }
}


// **C# code
// **Program.cs

namespace recast_cs_test
{
   public class Program
   {
      static void Main()
      {
          MyWrapper myWrapperTest = new MyWrapper(true);
          myWrapperTest.resetLog();
          myWrapperTest.enableLog(true);
      }
   }
}


2 commentaires

Vous voudrez peut-être mettre à jour la description des liens (:


Cela pourrait aider


3 Réponses :


2
votes

Je regarderais créer un serveur COM avec ATL . Ce ne sera pas un port simple, cependant. Vous devrez créer des interfaces compatibles COM qui exposent les fonctionnalités de la bibliothèque que vous essayez d'envelopper. En fin de compte, vous aurez plus de contrôle et une interface COMT entièrement prise en charge.


7 commentaires

Je ne vois pas l'avantage de ce que vous suggérez à l'aide de P / invoke à la place. Soins à élaborer?


Bien sûr, cette approche vous permet de créer un composant pouvant être consommé par .NET, mais n'implique aucun mélange de code géré et non géré. IDL permet de définir les structures de données et les interfaces COMPATIVÉES et, par conséquent, compatibles .NET. Il y a des récipients: vous devrez exécuter sous forme de composant COM, qui peut créer des maux de tête d'installation et vous devez recréer complètement l'interface de votre composant. La prestation de base est que vous utilisez du code purement non géré. Il n'y a donc pas de complications. Ceci est une solution Windows uniquement, à cause de COM.


Sauf s'il est un glouton de punition, je recommanderais fortement à l'utilisation de ATL en particulier et de la COM en général. L'ancien en particulier va certainement causer beaucoup plus de problèmes que cela ne va pas.


ATL a ses points faibles; Je dirais que le grand est que cela cache beaucoup de code et peut être déroutant, surtout si vous n'avez aucune expérience avec les modèles C ++ ou COM. Cela dit, j'ai trouvé qu'il s'agissait d'un cadre fiable et de manière très préférable à écrire des interfaces COM personnalisées en C ++.


Le problème avec COM est que MSFT n'a fourni aucun outil de haut niveau pour les développeurs C ++ pour l'utiliser. J'ai fait beaucoup d'ATT Travailler une fois-up-upance et saignez mes doigts lisant «ATL Internals», probablement le livre de facto sur le sujet. Oui, cela fait le travail, mais il est arcanique de dire le moins (je suis d'être euphemestiquement gentil) et extrêmement difficile pour la plupart des programmeurs de se diriger vers la tête. La plupart des développeurs C ++ sont déjà assez contestés que cela. Si les coûts de stabilité et de maintenance du programme signifient quelque chose, la plupart des organisations devraient l'éviter comme la peste.


ATL n'est pas si mauvais du tout. J'ai trouvé écrire de nouveaux composants com en utilisant c'était assez simple. Bien entendu, vos plaintes concernant la complexité devraient vraiment être dirigées vers la communité. ATL est une fine enveloppe autour de là. Utilisez les nouveaux wizards de projet et vous n'avez même pas besoin de penser trop fort.


IMHO, la plupart des programmeurs n'ont pas une compréhension claire de COM ou ATL, même lorsqu'ils pensent qu'ils le font. Ce n'est pas adressé à vous personnellement (je ne vous connais pas), mais il est basé sur une grande expérience en examinant le code des autres (et essais des développeurs potentiels lors des entretiens). Vous trouverez fréquemment des exemples flagrants qui sauvegardent ma réclamation. Vous pouvez être l'exception plutôt que la règle, mais l'OP constatera probablement que ce n'est pas normalement le cas.



4
votes

En règle générale, les structures C / C ++ sont utilisées pour communiquer avec le code natif, tandis que vous créez des classes CLI pour communiquer avec le code .NET. Les structures C sont "muettes" en ce sens qu'ils ne peuvent stocker que des données. Les programmeurs .NET, en revanche, s'attendre à ce que leurs structures de données soient "intelligentes". Par exemple:

Si je change le paramètre "Hauteur" dans une structure, je sais que la hauteur de l'objet ne changera pas réellement tant que je passe cette structure à une fonction de mise à jour. Cependant, en C #, l'idiome commun est que les valeurs sont représentées comme des propriétés et la mise à jour de la propriété rendra immédiatement ces changements "en direct".

De cette façon, je peux faire des choses comme: myshape.dimensions.height = 15 et attendez-vous à ce qu'il "travaille".

Dans une certaine mesure, les structures que vous exposez au développeur .NET (comme classes) sont en réalité l'API, les comportements étant mappés sur des propriétés et des méthodes de ces classes. Bien que dans C, les structures sont simplement utilisées comme variables passées à et à partir des fonctions qui font le travail. En d'autres termes, .NET est généralement un paradigme orienté objet, tandis que c n'est pas. Et beaucoup de code C ++ sont en réalité C avec quelques bits de fantaisie, jetés pour épices.

Si vous écrivez une couche de traduction entre C et .NET, une grande partie de votre travail consiste à concevoir les objets qui constitueront votre nouvelle API et fournissez la traduction à vos fonctionnalités sous-jacentes. Les structures du code C ne font pas nécessairement partie de votre nouvelle hiérarchie de l'objet; ils font juste partie de l'API C.

modifier pour ajouter:

aussi à considérer

En outre, vous voudrez peut-être reprocher votre choix d'utiliser C ++ / CLI et envisager C # et P / invoquer à la place. Pour diverses raisons, j'ai écrit une fois une enveloppe pour OpenSSL à l'aide de C ++ / CLI, et alors qu'il était impressionnant de la facilité de construire et de la manière dont il a fonctionné sans soudure, il y avait quelques ennuis. Plus précisément, les liaisons ont été serrées, donc chaque fois que le projet parent (OpenSSL) a revu leur bibliothèque, je devais recharger mon enveloppe. En outre, mon wrapper a été toujours attaché à une architecture spécifique (64 bits ou 32 bits) qui devait également correspondre à l'architecture de construction de la bibliothèque sous-jacente. Vous obtenez toujours des problèmes d'architecture avec p / invoke, mais ils sont un peu plus faciles à gérer. De plus, C ++ / CLI ne joue pas bien avec les outils d'introspection tels que le réflecteur. Et enfin, la bibliothèque que vous construisez n'est pas portable pour mono. Je ne pensais pas que cela finirait par être un problème. Mais à la fin, je devais recommencer à zéro et réacquer tout le projet en C # en utilisant p / invoquer à la place.

D'une part, je suis heureux d'avoir fait le projet C ++ / CLI parce que j'ai beaucoup appris à travailler avec le code et la mémoire géré et non géré dans un seul projet. Mais d'autre part, il fut bien sûr beaucoup de temps que j'aurais pu passer pour d'autres choses.


3 commentaires

Avec un grand projet de 3ème partie C ++ comme celui-ci, la tâche de déchiffrer les parties à envelopper semble assez accablante.


@ erebel55: vrai, mais vous pouvez le faire progressivement. Enveloppez la fonctionnalité dont vous avez besoin pour ce que vous essayez de faire maintenant. Et aller de là.


Interopérabilité gérée - non gérée dans les années 2010: la partie 3, C ++ / CLI et d'autres options vaut la peine de lire pour quand C ++ / CLI est plus adaptée à Interop au lieu de P / invoke.



1
votes

Si vous êtes prêt à utiliser p / invoke, le logiciel Swig pourrait peut-être vous aider: http: // www. swig.org/


0 commentaires