За что я люблю и ненавижу C++ одновременно - так это за то, что он может заставить себя почувствовать дураком и нубом себя в любой момент работы с ним.
TL;DR никогда ни за что, не мешайте extern "C" и пространства имён в определениях функций. Это не работает нормально ни в одном компиляторе и, как обычно, это описано только где-то в дебрях stackoverflow, хоть и разрешено в компиляторах MinGW и MSVC .
Замечу, что эта проверка есть в SonarSource но почему-то в виденных мной учебниках плюсов это правило отсутствует, что принципиально неправильно.
Логичный вопрос - а зачем это вообще мне понадобилось? Начну с оригинальной проблемы. Дело, в том, что в Saddy нет как-таковой поддержки MinGW x64. И это ай-яй-яй как хочется. А почему нет? Потому что у библиотеки от которой он частично зависит в паре плагинов - irrKlang - нет билдов по MinGW x64. Ну вот нет их и всё. И я долго ждал, уже подумывал менять библиотеку, чтобы как-то обеспечить билды под этот вариант, но недавно набрёл на то, что можно слинковаться с DLL от MSVC используя команды вида
gendef a.dll
dlltool -v -d a.def -l liba.a
где gendef - это тула, которая по умолчанию не идёт с MinGW которая создаёт DEF-файлы для DLL. Её легко собрать, ничего сложного в этом нет, главное чтобы она была нужной разрядности Выглядит просто?
Не совсем так. Библиотека сгенерируется, но вот ABI у MinGW и MSVC сильно отличаются для C++ и поэтому методы будут внутри заmangleны по-другому. И vtable тоже будут отличаться, что позволит виртуальным вызовам счастливо падать. Зато ABI одинаков в C API, поэтому если скомпилировать DLL с C API в MSVC, а потом, через метод выше, подлинковать с MinGW, то всё будет работать. В результате получается логичный план - обернуть irrKlang API в DLL с C API и так всё собирать, после чего использовать. вместо оригинальных методов сишные. Главное - из унаследованных классов не кидать исключения вовне, а то это тоже не совместимо.
Первый вариант такого API был прост - заворачиваем всё в extern "C" и внутри размещаем по namespace на каждый класс irrKlang с соответствующими функциями, внутри дёргаем оригинальные с правильными параметры. Это работает с MSVC, MinGW. Первый - точно игнорирует пространства имён, так что коллизии по именам возможны, но это работает. И в MinGW работает (хотят там GCC). А вот GCC 11 на Ubuntu 20.04, в упор ругался на реализацию функции, которая находится в пространстве имён что она должна в нём находиться, но не находится (irrklangProxy::XXX::YYY must be in namespace irrklangProxy::XXX::YYY). Это одно из самых невнятных сообщений компилятора, что я видел и я видел некоторое дерьмо, поверьте мне. К этому моменту в библиотеке были уже все нужные методы и довольно много кода, который не хотелось переписывать вручную.
Добрые полчаса интенсивного гугления - показали результат, что так делать нельзя (это действительно мой косяк и я это не отрицаю). Но у меня по итогу в воздухе повисли несколько вопросов:
- Почему такие конструкции изначально не запрещены комитетом? Ну, come on, это же просто пройтись по синтаксическому дереву и выкинуть ошибку, a.k.a "Don't mix namespaces and extern "C"" Выглядит несложно и куда понятнее ошибки выше. Это же не какие-то лямбды, что первая, что вторая фича живут с нами ещё с C++-03 и позволяют отстрелить себе ногу.
- Почему об этом не написано на каждом заборе? Вот как перечислить фичи языка - за этим просто очередь, как о таком писать, а об этом НАДО писать, то только stackoverflow и гугл спасают.
Когда у нас будут нормальные UTF-8 строки в STL, без головной боли? Уже везде они есть, даже в Ruby уже 10 версий вышло, а плюсовики всё с огрызком вместо(это не относится к посту)
Комментариев нет:
Отправить комментарий