Упаковка C ++ для использования в C # - PullRequest
8 голосов
/ 01 марта 2012

Хорошо, в основном есть большой проект C ++ (Recast) , который я хочу обернуть, чтобы я мог использовать его в своем проекте C #.

Я пытался сделатьэто на какое-то время, и это то, что я имею до сих пор.Я использую C ++ / CLI, чтобы обернуть классы, которые мне нужны, чтобы я мог использовать их в C #.

Тем не менее, есть тонна структур и перечислений, которые мне также понадобятся в моем проекте C #.Итак, как мне обернуть их?

Основной метод, который я сейчас использую, - это добавление вызовов dllexport к собственному коду C ++, компиляция в dll / lib, добавление этой lib в мой проект C ++ / CLI и импортЗаголовки c ++, затем компилирование проекта CLI в dll, наконец добавление этого dll в качестве ссылки на мой проект C #.Я ценю любую помощь.

Вот код ... Мне нужен управляемый способ сделать это, так как проект C ++ очень большой.

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

Ответы [ 4 ]

4 голосов
/ 01 марта 2012

Как правило, структуры C / C ++ используются для связи с собственным кодом, в то время как вы создаете классы CLI для взаимодействия с кодом .NET. Структуры C являются «глупыми» в том смысле, что они могут хранить только данные. С другой стороны, программисты .NET ожидают, что их структуры данных будут «умными». Например:

Если я изменю параметр "height" в структуре, я знаю, что высота объекта фактически не изменится, пока я не передам эту структуру функции обновления. Однако в C # общая идиома состоит в том, что значения представлены в виде свойств, и обновление свойства немедленно сделает эти изменения «живыми».

Таким образом, я могу сделать что-то вроде: myshape.dimensions.height = 15 и просто ожидать, что это "сработает".

В определенной степени структуры, которые вы предоставляете разработчику .NET (как классы), на самом деле являются API, а их поведение сопоставлено со свойствами и методами этих классов. В то время как в C, структуры просто используются как переменные, передаваемые и выполняемые функциями. Другими словами, .NET обычно является объектно-ориентированной парадигмой, а C - нет. И большая часть кода на C ++ - это на самом деле C с несколькими необычными битами, добавленными для специи.

Если вы пишете слой перевода между C и .NET, то большая часть вашей работы состоит в том, чтобы разработать объекты, которые будут составлять ваш новый API, и предоставить перевод для вашей основной функциональности. Структуры в коде C не обязательно являются частью вашей новой иерархии объектов; они просто являются частью C API.

изменить, чтобы добавить:

Также для рассмотрения

Кроме того, вы можете пересмотреть свой выбор использования C ++ / CLI и рассмотреть вместо этого C # и p / invoke. По разным причинам я однажды написал оболочку для OpenSSL с использованием C ++ / CLI, и, хотя было впечатляющим, насколько легко это было построить и насколько эффективно это работало, было несколько неприятностей. В частности, привязки были жесткими, поэтому каждый раз, когда родительский проект (OpenSSL) обновлял свою библиотеку, мне приходилось перекомпилировать свою оболочку для соответствия. Кроме того, моя оболочка была навсегда привязана к конкретной архитектуре (64-битной или 32-битной), которая также должна была соответствовать архитектуре сборки базовой библиотеки. Вы по-прежнему сталкиваетесь с проблемами архитектуры с p / invoke, но с ними немного легче справиться. Кроме того, C ++ / CLI не очень хорошо работает с инструментами для самоанализа, такими как Reflector. И, наконец, библиотека, которую вы создаете, не переносима в Mono. Я не думал, что это станет проблемой. Но, в конце концов, мне пришлось начать все заново и заново выполнить весь проект на C #, используя вместо этого p / invoke.

С одной стороны, я рад, что сделал проект C ++ / CLI, потому что я многое узнал о работе с управляемым и неуправляемым кодом и памятью в одном проекте. Но, с другой стороны, я наверняка потратил много времени на другие вещи.

2 голосов
/ 01 марта 2012

Я бы посмотрел на создание COM-сервера, используя ATL .Хотя это будет не простой порт.Вы должны будете создать COM-совместимые интерфейсы, которые раскрывают функциональность библиотеки, которую вы пытаетесь обернуть.В конце вы получите больше контроля и полностью поддерживаемый интерфейс COM Interop.

0 голосов
/ 01 марта 2012

Если вы готовы использовать P / Invoke, программное обеспечение SWIG может вам помочь: http://www.swig.org/

...