DLL, написанная на C, против той же, что написана в Stack Overflow

У меня была беседа с коллегой сегодня. Он утверждал, что написание DLL на C позволит любому другому приложению, написанному на любом языке, использовать эту DLL. НО, если эта DLL написана на C ++, количество приложений, которые могут использовать эту DLL, ограничено (возможно, из-за языковых ограничений).

  1. Правильно ли он это сказал?
  2. Если бы вы написали DLL, которая должна использоваться всеми видами приложений, написанных на разных языках (но на одной и той же платформе; давайте немного забудем о переносимости), вы бы написали ее на C / C ++ и почему?

Я надеюсь, что этот вопрос не Горилла против Акулы своего рода вопрос. Если это так, пожалуйста, закройте его.

9

Решение

Большинство языков предоставляют (простой) способ вызова функции C из DLL. С C ++ это не так, поскольку C ++ ABI (двоичный интерфейс функции C ++) зависит от поставщика.

Кроме того, будет практически невозможно взаимодействовать с C ++ DLL, использующей передовой C ++ конструкции, такие как шаблоны или STL.

Однако содержимое вашей DLL может быть написано на C ++, вам нужно только убедиться, что ваш интерфейс совместим с C. Чтобы сделать это, не используйте конструкции C ++ в вашем интерфейсе и окружите ваши объявления:

#ifdef __cpluscplus
extern "C" {
#endif

/* You declarations here */

#ifdef __cpluscplus
}
#endif

… таким образом, вы оборачиваете свою библиотеку C ++ интерфейсом C.

РЕДАКТИРОВАТЬ: Как писал Матс Петерссон, не забудьте убедиться, что вы обрабатываете все возможные исключения C ++ в вашей оболочке.

11

Другие решения

1) Если ИНТЕРФЕЙС, предоставляемый DLL, действительно является интерфейсом C ++, то да, он затрудняет (если не делает невозможным) взаимодействие других языков с DLL.

C ++ более сложен для взаимодействия, чем C, из-за более сложной структуры классов (this передача указателя, указатели виртуальных функций / разметка VTABLE) и обработка исключений (когда вызываемый код должен обрабатывать тот факт, что исключение произошло каким-то образом, и для этого код должен иметь возможность «раскрутить» вызов -стек кода, который вызвал исключение и уничтожил любые объекты, созданные на пути, пока не найдет catch — если этого нет в стеке вызовов внутри DLL, у вас возникнут проблемы — это раскручивание не является частью стандарта C ++, потому что стандарт не желает ограничивать какие архитектуры и какие функции должен / должен реализовывать процессор C ++ более чем необходимо). Ловля исключений в DLL решит проблемы здесь.

Другими словами, если язык не является C ++ [и, возможно, от того же поставщика], который вызывает код, существует необходимость некоторой обработки C ++ для любого языка, который его вызывает. Это может быть довольно сложно.

Любой объект C ++ должен быть переведен с / на соответствующий язык в вызывающем коде. Для базовых типов, общих с C, это, как правило, не проблема, но классы, структуры и т. Д. Должны быть сопоставлены с чем-то совместимым на местном языке.

С другой стороны: функция C очень проста для взаимодействия: поместите аргументы в стек, вызовите функцию и очистите аргументы при возврате. Ничего странного не происходит, нет скрытых параметров функции, нет необходимости раскручивать стеки. Единственное небольшое осложнение — функции, возвращающие struct (это больше, чем определенный размер) — но это довольно частный случай в C. Объекты C гораздо проще, и большинство языков имеют типы, которые хорошо соответствуют основным типам языка C (но стиль C struct все еще может вызвать некоторые интересные проблемы, и union может быть настоящим испытанием, если его использовать «умно»).

2) Выбор языков — сложное дело, и оно во многом зависит от того, как предполагается использовать DLL или какой интерфейс она должна предоставлять, и к каким интерфейсам она должна подключаться сама — если ваша DLL взаимодействует с другим C ++ DLL (или другой код C ++), тогда вы, вероятно, захотите использовать C ++. Но есть способы создать C ++ DLL с интерфейсом C, используя extern "C" для функций интерфейса (и гарантируя, что ничего throws через стену в «С», потому что это, безусловно, вызовет проблемы).

Заключение:
Очевидно, что, ограничивая интерфейс «только использованием C ++», возникает еще одно осложнение, заключающееся в том, что любой, кто использует библиотеку, скажем, из C, Python или Lisp (все из которых могут, вероятно, довольно легко вызывать функции C), будет Обернуть код C ++ в оболочку языка C. И да, это может быть сделано и используется довольно регулярно, когда в C ++ доступна действительно хорошая библиотека, которую кто-то хочет подключить к языку с интерфейсом в стиле C. Это почти то же самое решение, что и «обеспечить интерфейс C к C ++ внутри DLL», за исключением того, что оно не предоставляется производителем DLL.

4

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

Я выбираю язык, который подходит мне лучше всего для моей задачи, а не наоборот. Когда я пишу приложения с графическим интерфейсом, я обычно использую Java, потому что это позволяет мне сосредоточиться на решении, а не на отслеживании памяти. 🙂 Когда мне нужна производительность, я использую C или C ++ или даже ассемблер, поэтому выбор языка зависит от того, что я собираюсь с ним делать или как выглядит моя среда.

0