Это продолжение этот вопрос.
Как упоминалось в комментариях к ответу:
An inline variable has the property that - It has the same address in every translation unit. [...] Usually you achieved that by defining the variable in a cpp file, but with the inline specifier you can just declare/define your variables in a header file and every translation unit using this inline variable uses exactly the same object.
Причем из самого ответа:
While the language does not guarantee (or even mention) what happens when you use this new feature across shared libraries boundaries, it does work on my machine.
Другими словами, неясно, гарантированно ли встроенная переменная будет уникальной за пределами границ, когда задействованы общие библиотеки. Кто-то эмпирически доказал, что оно работает на некоторых платформах, но это неправильный ответ, и он может просто сломать все на других платформах.
Есть ли какие-либо гарантии относительно уникальности встроенной переменной, когда она используется за пределами границ, или это просто деталь реализации, на которую мне не следует полагаться?
@BoBTFish Похоже, что сложно сказать - нет это не. :-) Если вы можете вставить это в ответ и добавить несколько дополнительных деталей, я буду признателен. Спасибо.
@skypjack Каждый компоновщик обеспечивает уникальность встроенной переменной, в противном случае реализация не соответствует требованиям. Проблема со встроенной переменной точно такая же, как и со статическими переменными, которые являются локальными для встроенной функции. Проблема однозначности определения этих локальных статических переменных решена давно. Это называется нечеткая связьздесь.
Здесь оригинальная статья очень надежного автора объясняет то же самое: open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4424.pdf
@Oliv Я думаю, что в твоей ссылке что-то пошло не так. :-)
@Oliv: Обновил мой ответ в ответ на первую опубликованную вами ссылку. Спасибо, что подняли этот вопрос.
@Oliv "Linker" здесь неоднозначен, когда задействованы общие библиотеки. Динамический (также известный как время выполнения) компоновщик может быть не таким мощным, как компоновщик, используемый для создания общих библиотек, или разрешение символов может быть намеренно ограничено (например, когда вы указываете флаг RTLD_PRIVATE
для dlopen
в Linux). Я довольно хорошо разбираюсь в различных факторах Linux, поэтому, наверное, мне стоит написать ответ…
@ArneVogel Если бы ты мог это сделать, это было бы очень поучительно, по крайней мере, для меня.
Is there any guarantee regarding the uniqueness of an inline variable when it is used across boundaries or is it simply an implementation detail that I should not rely on?
Это зависит от вас (убедившись, что все объявления на самом деле одинаковы).
Компилятор, очевидно, не может это проверить, и компоновщик не беспокоит. Так что, если вы солгаете компоновщику (нет делает то же самое), то у вас будут проблемы.
Хорошо, поскольку не все понимают, что я имею в виду под «ложью компоновщика», я немного уточню это.
@oliv любезно предоставил эта ссылка, который среди прочего говорит следующее (мой комментарий):
Duplicate copies of these constructs [i.e. variables declare inline in multiple TU's] will be discarded at link time.
Что хорошо, это то, что нам нужно. Дело в том, что ты не знаешь какие (очевидно, сохраняется только один, поэтому по расширению вы не знаете, какой из них будет).
Итак, если они отличаются, вы не знаете, какой из них у вас получится, поэтому в итоге вы получите (особенно коварную форму) UB. Вот что я имел в виду, говоря «солгать компоновщику». Потому что, объявляя переменные по-разному в разных ЕП, вы именно это и сделали. Ой!
Будущим читателям (и мне на самом деле) будет полезно, если вы расскажете подробнее о том, что означают обеспечить это и солгать компоновщику. Может примеры? Не могли бы вы подробнее рассказать о своем ответе? Спасибо.
Ржу не могу. Хорошо, сделал это, спасибо, хорошее замечание. ... А теперь я его еще немного улучшил, как раз для спешки перед завтраком.
Не проблема. Спасибо за ваше терпение. У меня вопрос. Вы говорите - Это зависит от вас (убедившись, что все объявления на самом деле одинаковы). Это означает, что он отлично работает без границ, если я помещаю объявление встроенной переменной в файл .hpp библиотеки только для заголовков, а затем включаю его из единиц перевода как исполняемого файла, так и общей библиотеки, связанной самим исполняемым файлом ? Учтите, что библиотека может быть создана кем-то еще позже.
Не волнуйтесь. Терпение - это добродетель, и я сам не очень хорошо с этим справляюсь, поэтому возможность попрактиковаться всегда приветствуется. И ответ на ваш вопрос, я думаю, положительный, но если у вас есть какие-либо сомнения, я бы создал программу быстрого тестирования, чтобы быть уверенным.
Что ж, этот ответ не дает никакого прогресса в направлении ясного решения вопроса. Гарантировано ли стандартом предположение об уникальности в пределах границ библиотеки? Вот в чем вопрос. И все, что я здесь вижу, это «[...] Я верю, что да». Да, я тоже так думаю. Но OP хочет не этого. Ему нужны факты и гарантии. И простого тестирования его для каждой платформы может быть недостаточно, поскольку, если это не гарантируется, обновление компилятора / компоновщика может позже изменить его поведение. Это просто поспешный ответ без фактов. Извините
Объявляя переменные по-разному в разных ЕП, вы нарушаете ODR, что делает программу некорректной, независимо от статической или динамической компоновки. Есть много других способов сделать программу некорректной.
Очевидно, что вы должны предоставить точно такое же объявление. Это первое предварительное условие, и мы это уже знаем, потому что это четко указано в стандарте. Но это не совсем вопрос, а именно: у меня есть встроенная переменная v в заголовке, включаю этот заголовок в статическую библиотеку s, динамическую библиотеку d и в исполняемый файл e. Достаточно ли «умны» компоновщик и среда выполнения, чтобы управлять тем, что все (библиотеки и exe) ссылаются на ИДЕНТИЧНУЮ переменную с одним и тем же адресом. Или может случиться так, что один из них работает со своей собственной копией, и поэтому мы по-настоящему не разделяем ее.
@nm Ах да. Но что происходит, когда вы это делаете (а почему)?
@PaulSanders Неважно, что происходит, так как это неопределенное поведение.
@PaulSanders Мне интересно. Я хочу знать, могу ли я использовать уникальность встроенной переменной или нет. Мне не интересно знать, что может случиться, если я нарушу правила. (И просто чтобы ответить на ваше любопытство: компоновщик, вероятно, просто выберет первое определение, которое он видит. Но технически приложение может делать что угодно, например, формировать ваш жесткий диск (но я думаю, что этого не будет ;-)))
В документе gcc «во время компоновки» означает во время фазы компоновки различных объектных файлов (.o) создание исполняемого файла или разделяемой библиотеки. Это происходит до выполнения кода, поэтому UB здесь нет. Проблема возникает, когда изображения из общей библиотеки или загружаются в память во время выполнения.
@oliv ОК. Представьте, что я написал «вы солгали компоновщику и / или загрузчику». Такая же разница. Или, по-моему, иначе: «Не объявляйте встроенные переменные в файлах заголовков в коде библиотеки». Потому что, давайте посмотрим правде в глаза, в чем смысл? Что задумано как хороший способ сказать: «ребята, ребята, давайте не будем слишком зацикливаться на этом, мы рискуем ошибочно принять лес за деревья здесь». Или как там.
@PaulSanders Это C++, мы в опасности (из-за необходимости Валлона), и поэтому мы должны понимать, что будет дальше. На самом деле нет UB, UB - это стандартизированные термины, которые определяют операцию, которую компилятор может предполагать, что она никогда не произойдет (что помогает оптимизации). Единственная проблема здесь в том, что его номера будут меняться от компиляции к компиляции и будут меняться, если разделяемая библиотека динамически загружается в разных порядках.
Извините, @Oliv, я не понимаю, о чем вы говорите, и предпочел бы, чтобы вы мне больше не писали.
Вот как я интерпретирую стандарт. Согласно basic.link/1:
A program consists of one or more translation units linked together.
Здесь ничего не говорится ни о статической, ни о динамической компоновке. Программа - это связанные между собой единицы перевода. Не имеет значения, выполняется ли связывание в два этапа (сначала создайте .dll / .so, а затем динамический компоновщик связывает вместе все динамические библиотеки + исполняемый файл).
Итак, в моей интерпретации не имеет значения, связана ли программа динамически или статически, реализация должна вести себя одинаково: статическая переменная класса должна быть уникальной (независимо от того, встроена она или нет).
В Linux это правда.
В Windows это работает не во всех обстоятельствах, поэтому, в моей интерпретации, в этих обстоятельствах он нарушает стандарт (если вы создаете отдельный .dll, который содержит статическую, не встроенную переменную и все другие .dll и exe относится к этой переменной, работает).
Привет @geza. Я думаю, что ваш ответ + мой ответ = полный ответ, проголосовали за.
Другими словами (поправьте меня, если я ошибаюсь), он должен работать должным образом, но не в Windows (это платформа, о которой я забочусь), поэтому я не могу полагаться на это решение. Понятно?
@skypjack: Я бы сказал, вам стоит попробовать это в Windows. Или даже задайте новый вопрос с тегом Windows об этой проблеме в Windows. Надеюсь, здесь есть гуру по Windows, которые могут подробнее рассказать об этом. Но даже вы должны попробовать это. В прошлом MSVC была известна плохим соответствием стандартам. В настоящее время они стараются соответствовать стандартам, но они также должны оставаться совместимыми со старым.
@skypjack: но, насколько я знаю, если вы поместите статическую переменную в dll (а не в exe) и свяжете эту dll с другими dll (которые используют эту переменную), вы тоже в Windows.
Я думаю, еще одно, что нужно добавить к списку нелепостей dll для Windows.
@PasserBy: Я не эксперт по Windows, поэтому я порекомендовал задать вопрос, касающийся новых окон. Насколько я знаю, в Windows нужно указывать все зависимости при связывании dll. В linux мы можем оставить символы неразрешенными при создании .so, поэтому проблема OP решается автоматически простым способом. Это важное различие между двумя системами. Но, возможно, это уже не так, я в последнее время не в курсе окон (и, возможно, есть обходной путь, который работает для всех старых окон, кто знает ...).
Да, @skypjack. Если вас это беспокоит [в Windows], попробуй это! (И отправьте ответ. Я согласен, что это законный договор, эти парни из MSVC всегда проделывают эти сумасшедшие трюки :). Но я думаю, что уже сказал, что вся эта болтовня просто загромождает нить, ну ладно, ребята.
@skypjack для Windows уже был дан ответ: stackoverflow.com/q/19373061/1207195 (и, по крайней мере, в этом случае, речь идет не о соответствии компилятора)
@AdrianoRepetti Спасибо за ссылку !!
@AdrianoRepetti Другой вопрос - динамические библиотеки на самом деле не определены стандартом C++, это территория, специфичная для платформы, что означает, что они намного менее надежны / переносимы. Я имею в виду, похоже, что все предполагает, что это могло сработать, но у вас нет гарантии, что он будет работать, даже если он сделает это сегодня. Я ошибся? Поскольку у меня также есть альтернативные решения, возможно, начинать таким образом борьбу с разделяемыми библиотеками - не лучшая идея. Имеет ли это смысл?
Да, это зависит от платформы, и это МОЖЕТ быть сделано как в Windows, так и в Linux (я полагаю, также и в большинстве других * nix). Портативный? Нет. Стабильный? Для конкретной платформы, но да (если это не взлом, но вы используете ОС). Не уверен, какова ваша конечная цель.
В C++ в настоящее время нет концепции разделяемых библиотек. Таким образом, поведение inline
в разделяемых библиотеках будет зависеть от реализации и платформы.
Тот факт, что [basic.link] / 1 утверждает, что "Программа состоит из одной или нескольких связанных между собой единиц перевода." не совсем означает, что программа, связанная вместе с другим, уже связанным модулем, должна вести себя так же.
За прошедшие годы было подано множество предложений по исправлению ситуации (N1400, N1418, N1496, N1976, N2407, N3347, N4028), ни одно из которых не сдвинулось с мертвой точки. Это просто сложно реализовать в общем виде, и C++ обычно старается не вдаваться в детали реализации. Как GCC положи это:
For targets that do not support either COMDAT or weak symbols, most entities with vague linkage are emitted as local symbols to avoid duplicate definition errors from the linker. This does not happen for local statics in inlines, however, as having multiple copies almost certainly breaks things.
По умолчанию MSVC не предоставляет никаких символов. Любой «внешний» символ должен быть явно объявлен с помощью специфичного для платформы __declspec(dllexport)
.
Из-за этого нельзя утверждать, что Windows несовместима с C++. Ни одно из правил C++ здесь не нарушается, потому что их нет.
Стандарт языка C++ просто не знает ни одного понятия, такого как «разделяемые библиотеки». Так что это гарантия, которую должна предоставить ваша платформа.