Почему искажение имени не стандартизировано

Мне просто интересно, почему искажение имен никогда не было стандартизировано стандартом C++. Очевидно, что наличие разных алгоритмов искажения имен наносит ущерб совместимости [1], и я не вижу никаких преимуществ в том, чтобы определять его в зависимости от реализации.

То есть, вопреки правилам вызова или размеру примитивов, сама машина не заботится и даже не знает, как вызывается функция. Так почему же он не был стандартизирован и почему он до сих пор не стандартизирован? В прошлом компиляторы меняли правила между версиями.

[1] все эти люди экспортируют функции как extern "C" говорить о многом

2 ответа

Решение

Стандарт не касается деталей реализации. Есть много, много вещей, которые зависят от реализации и которые мешают программам работать вместе: как устроены классы, структура vtableи т. д. Как правило, компиляторы изменят название искажения, если они изменят любое из них. Это сделано намеренно, так как предотвращает связывание кода, который не будет работать.

Для данной платформы возможно определить ABI C++; все компиляторы, которые придерживаются его, будут использовать совместимые реализации и иметь общее искажение имен. Однако это проблема для поставщиков платформы; по тем или иным причинам очень немногие поставщики определили ABI C++.

И причина extern "C" работает потому, что почти все платформы определяют C ABI.

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

Пока не было широко распространенного ABI, на которое можно было бы положиться, GCC изо всех сил старалась использовать другую схему искажения имен, чем ее конкуренты:

G ++ не выполняет искажение имен так же, как другие компиляторы C++. Это означает, что объектные файлы, скомпилированные с одним компилятором, не могут использоваться с другим.

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

Перенос имен также сложнее, чем понимают многие программисты. Например, как Стандарт определит приемлемые соглашения о вызовах для всех платформ, на которых может работать C++? Нужна ли система RISC для поддержки x86? stdcall хотя системы RISC обычно передают свои аргументы в регистрах, а не в стеке?

Я тоже всегда хотел увидеть стандарт изменения имен C++. Я не говорю, что этот "стандарт" должен быть частью спецификации ISO C++ (больше, чем это сделано для ISO C). Но есть большое преимущество в том, что он где-то стандартизирован, особенно если вы создаете динамические библиотеки (DLL в Windows, dylibs в большинстве реализаций Unix/Mac), которые, по вашему мнению, могут использоваться другими исполняемыми файлами.

Если у меня есть код C, которым я хочу поделиться, размещение его в динамической библиотеке очень полезно, а экспорт прототипов функций C может использоваться многими другими разработчиками в совершенно разных средах разработки. Программисты Visual Basic могут легко получить доступ к таким DLL, как и программы Java, использующие JNI и т. Д. Python-привязки также легко выполняются вокруг них.

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

Я считаю, что мне нужно создать оболочку C API для классов C++, которые переходят в объект C++. Например, я могу использовать непрозрачный 64-битный "дескриптор", который под капотом представляет собой карту для указателей объектов, и поэтому открытая функция C "Foo(objHdl,x,y,z)" отображается на "obj->foo(x,y,z)". Затем я перехожу к другому пути на стороне клиента, так что если мой клиентский код тоже является C++, то я создаю клиентские классы с зеркальными методами, которые переходят к функциям C.

Как видите, все это становится немного громоздким.

И мне совершенно ненужно. Если бы существовал стандартизированный протокол изменения имен C++, клиент C++ мог бы просто вызвать его. (Возможно, я захочу продолжить экспорт C API в DLL, для этих клиентов на C, но было бы неплохо предложить выбор.)

Даже если бы такие стандарты были специфичными для платформы, это было бы началом. Поскольку проблема в настоящее время такова, одна среда разработки C++ может управлять вещами совершенно иначе, чем другая среда разработки C++, даже в той же ОС. И даже в одной и той же среде разработки между версиями этой среды происходят изменения.

Эквивалентная ситуация в C может быть не идеальной, но ее гораздо легче обойти. Например, в моих экспортированных функциях C API я обычно не использую обобщенные типы данных, такие как "int" или "short", в моих открытых функциях, а, скорее, конкретно "int32_t" и т. Д. Я обычно избегаю проблем с выравниванием структуры, делая свой C API возвращает каждое поле отдельно, что-то вроде GetXXXField(xxxHandle, e_xxxFieldEnum, &xxxfieldValue). В конце концов, они становятся котлом, и этот образец используется годами.

Другие вопросы по тегам