#cpp #c #dll #lib #extern
Мне бы хотелось разобраться в вопросе (не)совместимости между различными компиляторами C/C++. Часто оказывается, что между собой несовместимы даже разные версии одного и того же компилятора. Доступной и детальной информации по этой теме мне найти не удалось. Вопросы такие: 1) Я знаю, что с совместимостью компиляторов C++ все очень плохо, потому что язык слишком переусложнен, имеет бесконечное количество тонкостей, исключений из правил и пунктов, которые определяются реализацией. Поэтому я не очень понимаю, зачем используют всякие export "C" и extern "C", если двоичные файлы (например, lib) практически всегда оказываются совместимы лишь с тем компилятором, которым они сделаны. Так в чем смысл? 2) Если разработка lib, dll и a файлов ведется с использованием языка C, то как в этом случае обстоят дела с совместимостью? Все так же плохо, как если бы разработка велась на C++? Или нет? 3) Есть ли способы делать двоичные библиотеки максимально совместимыми? Чтобы написанная однажды библиотека dll могла быть подключена в самых разных языках без боли и страданий?
Ответы
Ответ 1
Несовместимость бинарных модулей (далее, для краткости, просто "модулей"), произведенных разными компиляторами, определяется в основном следующими тремя аспектами: Разные правила декорирования имен экспортируемых символов Разное устройство объектов стандартной библиотеки Разные правила расположения полей структур в памяти Первый пункт характерен фактически только для С++: в Си существует набор характерных для конкретной аппаратной платформы соглашений о вызове (например, stdcall, fastcall и cdecl для x86), которые довольно четко прописывают правила декорирования имен. Второй пункт относится и к Си и к С++, но в Си не очень много "объектов стандартной библиотеки" - в голову приходит только FILE*, и экспортировать его через границы модулей нет никакого смысла. Таким образом да, действительно можно сказать, что С++ "хуже" чем Си в плане бинарной совместимости. Это разумеется не значит, что не нужно на нем писать, это лишь значит, что на границе модулей нужно использовать интерфейс в стиле Си (либо использовать стандартизированный объектно-ориентированный интерфейс, например Component Object Model в Windows). Есть ли способы делать двоичные библиотеки максимально совместимыми? Чтобы написанная однажды библиотека dll могла быть подключена в самых разных языках без боли и страданий? Использование DLL на С/С++ в других языках это больше чем вопрос бинарного интерфейса (например, в них может просто не быть концепции заголовочных файлов, указателей и т.п.), но обычно да, библиотека с интерфейсом в стиле Си может быть использована и из других языков с тем или иным количеством дополнительных телодвижений. Рекомендации для обеспечения максимальной бинарной совместимости: Экспортируйте через границы бинарного модуля только простые функции с припиской extern "C" (т.е, никаких классов, шаблонов, перегруженных функций, пространств имен и т.п.) Передавайте через границы модулей только простые типы, указатели на них и указатели на функции. Если все же передаете структуры, сделайте первым членом структуры ее размер. Это позволит, если вы натолкнетесь на различия по выравниванию полей, обнаружить несоответствие в общем размере структуры и хотя бы нормально вернуть ошибку. Не передавайте через границы модулей объекты стандартной библиотеки, например указатели FILE*. Блоки динамической памяти должны освобождаться всегда в том же модуле, в котором были выделены. Т.е., если библиотека возвращает программе-клиенту указатель на блок памяти, выделенный malloc внутри себя, она должна предоставлять специальную функцию для его освобождения (вызывающую внутри себя free), вместо того, чтобы полагаться на вызов free в программе-клиенте.Ответ 2
Толстый троллинг по поводу "недостатков" языка С++ продолжается. Ну что же, ринемся на защиту детища Страуструпа. 1) Я знаю, что с совместимостью компиляторов C++ все очень плохо, С совместимостью компиляторов С++ все очень хорошо, гораздо лучше, чем с совместимостью компиляторов с каких-либо других языков. Вплоть до того, что многие языки имеют компилятор только под одну платформу и/или от одного производителя. Конечно, такой подход улучшает совместимость, но ухудшает переносимость и тормозит развитие. При отказе единственного производителя компилятора от поддержки языка 100500 разработчиков повисают в воздухе. Достаточно вспомнить отказ даже такого гиганта как Микрософт от обратной совместимости в языке Visual Basic при переходе от версии VB6 к версии VB.net. потому что язык слишком переусложнен, имеет бесконечное количество тонкостей, исключений из правил и пунктов, которые определяются реализацией. Язык С++ не переусложнен. Просто в языке С++ не включена "защита от дурака". В награду за не включенную "защиту от дурака" пользователи имеют возможность делать то, что им нужно, а не то, что позволяет им язык. Чтобы пользоваться всеми возможностями языка С++ недостаточно выучить синтаксис С++ по первому изданию книжки Страуструпа. Надо еще читать книжки, которые выходят по мере развития языка и в которых обсуждаются как раз те новые возможности, которые некоторых разработчиков приводят к созданию крашащегося кода, а другим разработчикам позволяют съэкономить 100500 часов рабочего времени. Внесение в язык С++ новых возможностей (например шаблонов) порождает взаимодействие этих новых возможностей со старыми возможностями (например с классами). Это взаимодействие может служить как источником ошибок, так и источником новых идей и подходов. Многие достойные люди занимаются исследованиями в этих направлениях и публикуют свои результаты исследований. Изучая эти исследования можно избежать ошибок и воспользоваться новыми подходами. Еще раз повторю что в языке С++ не включена "защита от дурака". И поэтому, чтобы не делать ошибок в языке С++, надо точно понимать, что именно ты делаешь. Поэтому я не очень понимаю, зачем используют всякие export "C" и extern "C", Как тут правильно заметили это отключает манглинг имен. если двоичные файлы (например, lib) практически всегда оказываются совместимы лишь с тем компилятором, которым они сделаны. Так в чем смысл? Под совместимостью различных компиляторов языка С++ имеется ввиду совместимость на уровне исходного текста. Двоичную совместимость объектников, бинарников и библиотек lib никто и никогда не гарантировал. Кстати, это относится и к другим языкам (за исключением языков с виртуальными машинами, но там за это платят тем, что приходится за собой таскать все тупиковые решения и ошибки ради обратной совместимости). 2) Если разработка lib, dll и a файлов ведется с использованием языка C, то как в этом случае обстоят дела с совместимостью? Все так же плохо, как если бы разработка велась на C++? Или нет? Кстати, никто не гарантировал совместимость объектников, бинарников и библиотек lib для языка Си. Трансляторы с языка Си от разных производителей производят объектники разного формата. Ну и что в этом такого? 3) Есть ли способы делать двоичные библиотеки максимально совместимыми? Чтобы написанная однажды библиотека dll могла быть подключена в самых разных языках без боли и страданий? Если в системе Windows dll имеет "pure Си" интерфейс, то ее можно вызвать из многих других языков. Количество боли и страданий при этом зависит от разработчика интерфейса между dll и этим самым языком, из которого dll вызывается. Собственно языки С/С++ к этому не имеют никакого отношения. UPD1: Я не начинал толстый троллинг по поводу недостатков C++, просто это мое мнение, которое основывается на определенном опыте работы с этим языком, в том числе в международных группах разработчиков. – Максим 2 минуты назад Уважаемый Максим, количество использующих язык С++ разработчиков в мире (в том числе в международных группах разработчиков) убедительно показывает, что язык C++ вполне жизнеспособен. Если бы это было не так, то от языка С++ давно бы отказались, как отказались в свое время от 100500 других языков. UPD2: С тем, что С++ жизнеспособен, никто не спорит. Но все-таки в нем действительно много разных тонкостей и хитростей. – HolyBlackCat 10 секунд назад Как я уже сказал, все эти "тонкости и хитрости" это следствие того, что новые возможности добавляются в язык и на уровне транслятора отсутствуют семантические и синтаксические ограничения на использование этих новых возможностей. Также на уровне транслятора отсутствуют семантические и синтаксические ограничения на взаимодействие новых возможностей со старыми возможностями. Это, с одной стороны, добавляет мощи. А с другой стороны, ограничения на использование новых средств должны храниться в голове программиста, а не прошиваются в синтаксисе языка. В любом случае, все, кому С++ кажется слишком сложным/непонятным/переусложненным/плохо совместимым может пользоваться любыми другими языками вместо того, чтобы перечислять мнимые недостатки С++. Потому что все эти якобы недостатки С++ это вовсе не недостатки, а это такой подход при котором транслятор не мешает Вам выстрелить себе в ногу, но зато дает возможность выстрелить на 100500 километров при правильном использовании. На свете есть 100500 трансляторов, которые мешают программисту выстрелить себе в ногу, но зато и ограничивают возможности. UPD3: Извините за частое употребление идиомы 100500, но уж очень она тут подходит. :-)
Комментариев нет:
Отправить комментарий