Управляемые оболочки C ++ для устаревших библиотек C ++ - PullRequest
6 голосов
/ 08 января 2009

Мы смотрим на написание .Net-вызываемой оболочки для некоторых устаревших библиотек C ++ с использованием управляемого C ++.

Все выглядит довольно просто. Есть ли что-то, что мы должны остерегаться?

Ответы [ 5 ]

6 голосов
/ 09 января 2009

Я обнаружил, что в целом довольно легко обернуть некоторые существующие библиотеки C ++ в C ++ / CLI и столкнулся с относительно небольшим количеством ошибок. Те, что я помню, были:

  • Смешивать неуправляемый код C ++ и код C ++ / CLI в одном исполняемом файле / DLL - очень плохая идея. Таким образом, у меня возникли проблемы с конкурирующими менеджерами памяти во время завершения работы (в основном, во время выполнения .NET и в обычном режиме C ++, когда наступали друг другу на ноги при очистке памяти при завершении работы, что приводило к недетерминированному поведению в отношении который освободил что). Вместо того чтобы связывать статическую устаревшую библиотеку C ++ с библиотекой C ++ / CLI, я создал библиотеку DLL, содержащую устаревшую библиотеку C ++, и связал ее с библиотекой C ++ / CLI, что решило проблему раз и навсегда.
  • Если ваш код использует перечисления, вы должны заключить их в соответствующие классы перечислений C ++ / CLI, иначе другие языки .NET не смогут их видеть и использовать.
  • Объекты C ++ / CLI могут содержать указатели только на неуправляемые объекты C ++. К сожалению, в некоторых случаях это означает, что вам придется создавать тонкие слои-обертки для обработки определенных объектов. Моим «фаворитом» было то, что мне пришлось либо обернуть boost :: shared_ptrs таким образом (и, таким образом, добавить еще один слой косвенности), либо поместить их в shared_ptrs с нулевыми удалителями после пересечения границы .NET / native. Ни то, ни другое не очень хорошо, когда вам приходится иметь дело с API, которые часто используют этот вид конструкции. RAII не пересекает эту границу, поэтому будьте осторожны, вам придется потратить некоторое время на его настройку в соответствии с подходом .NET.
  • C ++ / CLI не выполняет множественное наследование, поэтому, если ваша унаследованная библиотека использует это, вам, возможно, придется смоделировать это с использованием интерфейсов и т. Д.
  • Внутренний код маршаллинга, кажется, в состоянии обработать большинство преобразований POD, но у вас будет код для поиска / заимствования, который преобразует std :: strings и т. Д. Этот код уже через несколько минут в Google должен его вызвать (извините , здесь пока нет ссылок)
2 голосов
/ 08 января 2009

Это довольно просто и хорошо работает. Это намного проще, чем PInvoke.

Главное, на что нужно обратить внимание, это отсутствие каких-либо неуправляемых членов в управляемых заголовках, в том числе закрытых членов, сигнатур методов и т. Д. Вполне нормально иметь закрытые члены, указывающие на управляемые типы, просто используйте forward объявления для ваших классов.

Также следите за временем жизни объекта. Утечки памяти легко представить, так как многие программисты .NET не привыкли убирать за собой. Убедитесь, что все создаваемые вами классы-оболочки являются одноразовыми, если они содержат указатели, и убедитесь, что вы избавляетесь от них в своем управляемом коде. Синтаксис для IDisposable в управляемом C ++ также странный, но он есть в документации.

Кроме того, помните, что каждый раз, когда вы пересекаете управляемую / неуправляемую границу, вы получаете небольшой удар, поэтому постарайтесь соответственно спланировать свой интерфейс. Если что-то вызывается в циклах несколько раз, вероятно, лучше переместить этот цикл через границу, чтобы вы пересекались только один раз. Не беспокойтесь об этом, если только вы не говорите миллионы звонков.

Эта статья идет другим путем, но в ней есть несколько полезных моментов.

Используйте нашу библиотеку ManWrap, чтобы получить лучшее из .NET в родном коде C ++

Смотри также

Управляемый код в Visual Studio 2005 и
Удаление управляемых объектов, упаковка библиотеки и многое другое

1 голос
/ 10 февраля 2010

Как говорили другие: 98% времени он просто работает, его можно отлаживать и быстро.

То, с чем я столкнулся за пределами упомянутого до сих пор:

  • Не компилируйте весь ваш унаследованный код на С ++ как управляемый. Было несколько статей, предлагающих, что это было бы полезно, но обычно это медленнее.
  • Не забудьте перехватить неуправляемые исключения и выбросить их как управляемые.
  • Если вы используете MFC, обратите внимание, что вы не можете использовать среду выполнения .lib, поэтому вы также будете развертывать среды выполнения MFC.
  • OpenMP (библиотека потоков) не будет работать в C ++ / CLI.
  • у нас были некоторые проблемы со сборкой в ​​VS2005, когда мы делали библиотеку C ++ / CLI зависимой от библиотек C # из нашего собственного кода.

Это даже сработало настолько хорошо, что я начал писать код C ++ / CLI для запуска модульных тестов кода C ++. NUnit / Resharper с радостью найдет и запустит модульный тест в C ++ / CLI DLL, которая может напрямую вызывать ваш нативный код на ЛЮБОМ УРОВНЕ, даже тестировать классы вашего контейнера шаблонов.

1 голос
/ 09 января 2009

Я просто добавлю к тому, что все уже сказали,

pin_ptr wch = PtrToStringChars (string); (где string - это System :: String)

станет вашим другом.

Вы не можете напрямую включить неуправляемый класс в управляемый класс, но вы можете поместить указатель на неуправляемый класс и добавить его в свой конструктор и удалить его в своем деструкторе.

У меня не было проблем, упомянутых Тимо Гюшем, со смешиванием кода C ++ и C ++ / CLI в одной DLL. Моя DLL широко использует оба без проблем.

C ++ / CLI не сложен (если вы знаете C ++ и .NET) и отлично работает.

1 голос
/ 09 января 2009

Только некоторые проблемы, с которыми мы столкнулись:

  • Управление временем жизни памяти / ресурсов (GC / IDisposable и Destructors). Я думаю, что это хорошо известно, и в посте Роба есть пара вещей, поэтому я не буду здесь вдаваться в подробности ...
  • Строковое кодирование / декодирование. Если ваш нативный код представляет собой сборку UNICODE, вам не придется об этом беспокоиться, но если нет, будьте внимательны с кодировками при преобразовании между нативными строками и .Net-строками.
  • C ++ не соблюдает [Conditional ("Debug")], это означает, что Debug.Assert, Debug.Trace и т. Д. Также будут вызываться в сборках выпуска. Вместо этого используйте традиционные макросы C ++.
  • 64-битная поддержка: .Net по умолчанию генерирует 32- или 64-битный код в зависимости от платформы, но ваш собственный код, вероятно, будет только 32-битным ...
...