Обращался ли C ++ 11 к передаче объектов std lib между границами динамической / разделяемой библиотеки? (т.е. дллс и тд)?

34

Одна из моих главных претензий к C ++ заключается в том, насколько трудно на практике передавать объекты библиотеки std за пределы динамической библиотеки (т.е. dll / so).

Библиотека std часто только для заголовков. Что отлично подходит для выполнения некоторых потрясающих оптимизаций. Однако для dll они часто создаются с различными настройками компилятора, которые могут влиять на внутреннюю структуру / код контейнеров библиотеки std. Например, в MSVC одна dll может собираться с включенной отладкой итератора, а другая - без него. У этих двух библиотек могут возникнуть проблемы, связанные с передачей стандартных контейнеров. Если я представлю std::stringв своем интерфейсе, я не могу гарантировать, что код, для которого клиент использует std::string, точно совпадает с кодом моей библиотеки std::string.

Это приводит к трудностям при отладке, головным болям и т. Д. Вы либо жестко управляете настройками компилятора в своей организации, чтобы предотвратить эти проблемы, либо используете более простой интерфейс C, который не будет иметь этих проблем. Или укажите своим клиентам ожидаемые параметры компилятора, которые они должны использовать (что отстой, если другая библиотека указывает другие параметры компилятора).

Мой вопрос заключается в том, пытался ли C ++ 11 что-либо сделать, чтобы решить эти проблемы?

Дуг Т.
источник
3
Я не знаю ответа на ваш вопрос, но могу сказать, что ваши проблемы разделяются; они являются ключом к тому, почему я не буду использовать C ++ в своих проектах, так как мы ценим стабильность ABI по сравнению с выжатием каждого последнего цикла потенциальной эффективности.
Donal Fellows
2
Пожалуйста, различайте. Это трудно между DLLс. Между тем SOон всегда работал просто отлично.
Ян Худек
1
Строго говоря, это не только проблема C ++. Возможно иметь эту проблему с другими языками.
MrFox
2
@JanHudec Я могу гарантировать, что между SO не работает почти так волшебно, как вы, кажется, указываете. Учитывая видимость символов и то, как часто работает искажение имен, вы можете быть более изолированными от проблемы, но компиляция одного .so с разными флагами / и т. Д. И при условии, что вы можете связать его в программе с другими флагами, - это спасение для катастрофы.
SDG
3
@sdg: с флагами по умолчанию и видимостью по умолчанию это работает. Если вы меняете их и попадаете в неприятности, это ваша проблема, и никто другой.
Ян Худек

Ответы:

20

Вы правы в том, что в любом общедоступном API C ++ лучше избегать всего, что связано с STL, а именно чего-либо из сторонней библиотеки. Вы также хотите следовать длинному списку правил по адресу http://www.ros.org/reps/rep-0009.html#definition, чтобы запретить разрыв ABI, что делает программирование общедоступных API C ++ рутинным занятием.

И ответ относительно C ++ 11 - нет, этот стандарт не касается этого. Более интересно, почему нет? Ответ в том, что C ++ 17 очень трогает это, и для реализации модулей C ++ нам нужны экспортированные шаблоны для работы, и для этого нам нужен компилятор типа LLVM, такой как clang, который может записать полный AST на диск, а затем выполнять поиск, зависящий от вызывающего, для обработки множества случаев нарушения ODR в любом крупном проекте C ++, который, кстати, включает в себя множество кода GCC и ELF.

Наконец, я вижу много комментариев MSVC о ненависти и про-GCC. Они очень дезинформированы - GCC на ELF принципиально и безвозвратно неспособен произвести корректный и правильный код C ++. Причин для этого много и легион, но я быстро процитирую один пример: GCC на ELF не может безопасно создавать расширения Python, написанные с использованием Boost.Python, где в Python загружено более одного расширения на основе Boost.Python. Это связано с тем, что ELF с его глобальной таблицей символов C просто неспособна предотвратить нарушения ODR, вызывающие сбои, тогда как PE и MachO, а также предлагаемая спецификация модулей C ++ используют таблицы символов для каждого модуля - что, кстати, также означает значительно более быстрое время инициализации процесса. И есть еще много проблем: посмотрите на StackOverflow, на который я недавно ответилhttps://stackoverflow.com/questions/14268736/symbol-visibility-exceptions-runtime-error/14364055#14364055, например, где выбросы исключений в C ++ безвозвратно фундаментально нарушены в ELF.

Последний пункт: что касается взаимодействия различных STL, это большая боль для многих крупных корпоративных пользователей, пытающихся смешать сторонние библиотеки, которые тесно интегрированы с некоторыми реализациями STL. Единственное решение - это новый механизм для C ++ для обработки взаимодействия STL, и, хотя они и есть, вы также можете исправить взаимодействие с компилятором, чтобы вы могли (например) смешивать MSVC, GCC и clang скомпилированные объектные файлы, и все это просто работает , Я бы посмотрел на C ++ 17 и посмотрел, что будет в ближайшие несколько лет - я был бы удивлен, если бы ничего не произошло.

Найл Дуглас
источник
Отличный ответ! Я только надеюсь, что Clang улучшает совместимость с Windows и может установить хороший стандартный компилятор по умолчанию. Система текстового включения / заголовка в C ++ ужасна, и я с нетерпением жду того дня, когда модули упростят организацию кода C ++, значительно сократят время компиляции и улучшат совместимость компилятора с перехватами, нарушающими ODR.
Алессандро Стаматто
3
Лично я ожидаю существенного увеличения времени компиляции. Быстрый обход внутримодульного AST очень труден, и нам, вероятно, понадобится его кэш-память в общей памяти. Тем не менее, почти все остальное, что плохо, становится лучше. Кстати, заголовочные файлы определенно остаются, текущие модули C ++ имеют интерфейсные файлы, отображающие 1-к-1 на заголовочные файлы. Кроме того, автоматически сгенерированные интерфейсные файлы будут легальными C ++, поэтому устаревший заголовок просто отфильтровывает макросы C и выводит их как файлы интерфейса. Хорошо а?
Найл Дуглас
Круто! У меня так много сомнений по поводу модулей. Будет ли модульная система учитывать текстовое включение против символического включения? С настоящей директивой include компилятор должен перекомпилировать десятки тысяч строк кода снова и снова для каждого исходного файла. Разрешит ли система модулей когда-нибудь код без предварительных объявлений? Это улучшит / облегчит строительные инструменты?
Алессандро Стаматто
2
-1 за предположение, что все сторонние шаблоны подозрительны. Изменение конфигурации не зависит от того, является ли конфигурируемая вещь шаблоном.
DeadMG
1
@Alessandro: Предложенные модули C ++ явно отключают макросы C. Вы можете использовать шаблоны, или сейчас. Предложенные интерфейсы являются легальными C ++, просто автоматически сгенерированы и могут быть предварительно скомпилированы для ускорения повторного анализа, т.е. не ожидают ускорения по сравнению с существующими предварительно скомпилированными заголовками. Последние два вопроса я на самом деле не знаю: это зависит :)
Найл Дуглас
8

В спецификации никогда не было этой проблемы. Это потому, что у него есть понятие, называемое «одно правило определения», которое требует, чтобы каждый символ имел ровно одно определение в рабочем процессе.

Windows DLL нарушают это требование. Вот почему есть все эти проблемы. Так что это решать Microsoft, а не комитет по стандартизации C ++. У Unix никогда не было этой проблемы, потому что общие библиотеки там работают по-другому и по умолчанию соответствуют одному правилу определения (вы можете явно нарушить его, но вы, очевидно, делаете это только в том случае, если знаете, что можете себе это позволить, и вам нужно выжать несколько дополнительных циклов).

Библиотеки Windows нарушают одно правило определения, потому что:

  • Они жестко определяют, из какой динамической библиотеки будет использоваться символ во время статической связи, и статически разрешают символы в библиотеке, которая их определяет. Таким образом, если один и тот же слабый символ генерируется в нескольких общих библиотеках и тех библиотеках, которые используются в одном процессе, динамический компоновщик не имеет возможности объединить эти символы. Обычно такие символы являются статическими членами или импедиментами класса экземпляров шаблона, и это вызывает проблемы при передаче экземпляров между кодом в разных библиотеках DLL.
  • Они жестко определяют, будет ли символ импортироваться из динамической библиотеки уже во время компиляции. Таким образом, код, связанный с некоторой библиотекой, статически несовместим с кодом, связанным с той же библиотекой динамически.

Unix, использующий экспорт в формате ELF, неявно импортирует все экспортированные символы, чтобы избежать первой проблемы, и не проводит различие между статически и динамически разрешаемыми символами до времени статической связи, чтобы избежать второй.


Другая проблема связана с флагами компилятора. Эта проблема существует для любой программы, состоящей из нескольких модулей компиляции, динамические библиотеки не должны быть задействованы. Однако в Windows все намного хуже. В Unix на самом деле не имеет значения, статически или динамически вы связываете, никто в любом случае статически не связывает стандартную среду выполнения (в Linux это может быть даже недопустимо), и нет специальной среды выполнения отладки, поэтому достаточно одной сборки. Но то, как Microsoft реализовала статическое и динамическое связывание, время отладки и выпуска и некоторые другие опции, означает, что они вызвали комбинаторный взрыв необходимых вариантов библиотеки. Снова проблема платформы, а не проблема языка C ++.

Ян Худек
источник
2
@ DougT .: GCC не имеет к этому никакого отношения. Платформа ABI имеет. В ELF, объектном формате, используемом большинством Unices, общие библиотеки экспортируют все видимые символы и импортируют все символы, которые они экспортируют. Поэтому, если что-то генерируется в нескольких библиотеках, динамический компоновщик будет использовать первое определение для всех. Простой, элегантный и рабочий.
Ян Худек
1
@MartinBa: нечего объединять, но это не имеет значения, если оно одинаково и до тех пор, пока оно не должно быть объединено в первую очередь. Да, если вы используете несовместимые настройки компилятора на платформе ELF, вы получите такой же беспорядок, как и везде и везде. Даже если не использовать разделяемые библиотеки, так что это несколько не по теме.
Ян Худек
1
@ Ян - это актуально для вашего ответа. Вы пишете: «... одно правило определения ... Windows DLL нарушают это требование ... разделяемые библиотеки работают по-другому [в UNIX] ...», но заданный вопрос касается проблем с содержимым std-lib (определенным в заголовках) и причина отсутствия проблем в Unix не имеет ничего общего с SO и DLL, но с тем фактом, что в Unix (очевидно) есть только одна совместимая версия стандартной библиотеки, в то время как в Windows MS решили иметь несовместимые (отладочные) версии (с расширенной проверкой и т. д.).
Мартин Ба
1
@MartinBa: Нет, основная причина проблемы в Windows заключается в том, что механизм экспорта / импорта, используемый в Windows, не может правильно объединять статические члены и классовые импедансы шаблонных классов во всех случаях и не может объединять статически и динамически связанные символы. Чем это усугубляется несколькими вариантами библиотек, но основная проблема заключается в том, что C ++ требует гибкости от компоновщика, которого нет у динамического компоновщика Windows.
Ян Худек
4
Я думаю, что это следствие того, что спецификация DLL нарушена, и соответствующее требование Msft «исправить это» неуместно. Тот факт, что DLL не поддерживают определенные функции C ++, не является дефектом спецификации DLL. Библиотеки DLL являются независимым от языка, независимым от производителя механизмом упаковки и ABI для предоставления точек входа в машинный код («вызовы функций») и двоичные объекты данных. Они никогда не предназначались для поддержки расширенных функций какого-либо конкретного языка. Это не ошибка Msft или спецификации DLL, что некоторые люди хотят, чтобы они были чем-то другим.
Евро Мицелли
6

Нет.

Продолжается большая работа по замене системы заголовков, функция, которая называется Модули и которая может повлиять на это, но, конечно, не очень большая.

Klaim
источник
2
Я не думаю, что система заголовков будет иметь какое-либо влияние на это. Проблемы заключаются в том, что библиотеки DLL Windows нарушают одно правило определения (что означает, что они не следуют спецификации C ++, поэтому комитет C ++ ничего не может с этим поделать) и что в Windows существует так много вариантов стандартной среды выполнения, которые комитет C ++ может ' ничего с этим не делать.
Ян Худек
1
Нет, они не Как они могли, в спецификации даже не упоминалось что-то подобное. Кроме того, когда (Windows) программа связана с Windows DLL, ODR удовлетворяется: все видимые (экспортируемые) символы должны подчиняться ODR.
Павел Михалик
@PaulMichalik C ++ охватывает связывание (фаза 9), и мне кажется, что по крайней мере связывание во время загрузки DLL / SO попадает в фазу 9. Это означает, что символы с внешними связями (независимо от того, экспортированы они или нет) должны быть связаны и соответствовать ОДР. Динамическое связывание с LoadLibrary / dlopen явно не подпадает под эти требования.
bames53
@ bames53: ИМХО, спецификации слишком слабы, чтобы допускать подобные заявления. .Dll / .so может рассматриваться как «программа» сами по себе. Чем были соблюдены правила. Нечто подобное загрузке других «программ» во время выполнения настолько недооценено стандартом, что любые утверждения относительно этого являются довольно произвольными.
Павел Михалик
@PaulMichalik Если исполняемый файл требует связывания во время загрузки, то перед связыванием во время загрузки есть внешние сущности, оставшиеся неразрешенными, и информация, необходимая для исполнения, отсутствует. LoadLibrary и dlopen находятся за пределами спецификации, но связывание времени загрузки должно быть явно частью фазы 9.
bames53