Являются ли встроенные переменные уникальными вне границ?

Это продолжение этот вопрос.
Как упоминалось в комментариях к ответу:

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.

Другими словами, неясно, гарантированно ли встроенная переменная будет уникальной за пределами границ, когда задействованы общие библиотеки. Кто-то эмпирически доказал, что оно работает на некоторых платформах, но это неправильный ответ, и он может просто сломать все на других платформах.

Есть ли какие-либо гарантии относительно уникальности встроенной переменной, когда она используется за пределами границ, или это просто деталь реализации, на которую мне не следует полагаться?

Стандарт языка C++ просто не знает ни одного понятия, такого как «разделяемые библиотеки». Так что это гарантия, которую должна предоставить ваша платформа.

BoBTFish 17.07.2018 08:29

@BoBTFish Похоже, что сложно сказать - нет это не. :-) Если вы можете вставить это в ответ и добавить несколько дополнительных деталей, я буду признателен. Спасибо.

skypjack 17.07.2018 08:30

@skypjack Каждый компоновщик обеспечивает уникальность встроенной переменной, в противном случае реализация не соответствует требованиям. Проблема со встроенной переменной точно такая же, как и со статическими переменными, которые являются локальными для встроенной функции. Проблема однозначности определения этих локальных статических переменных решена давно. Это называется нечеткая связьздесь.

Oliv 17.07.2018 09:47

Здесь оригинальная статья очень надежного автора объясняет то же самое: open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4424.pdf

Oliv 17.07.2018 09:55

@Oliv Я думаю, что в твоей ссылке что-то пошло не так. :-)

skypjack 17.07.2018 09:56

@Oliv: Обновил мой ответ в ответ на первую опубликованную вами ссылку. Спасибо, что подняли этот вопрос.

Paul Sanders 17.07.2018 11:03

@Oliv "Linker" здесь неоднозначен, когда задействованы общие библиотеки. Динамический (также известный как время выполнения) компоновщик может быть не таким мощным, как компоновщик, используемый для создания общих библиотек, или разрешение символов может быть намеренно ограничено (например, когда вы указываете флаг RTLD_PRIVATE для dlopen в Linux). Я довольно хорошо разбираюсь в различных факторах Linux, поэтому, наверное, мне стоит написать ответ…

Arne Vogel 17.07.2018 11:37

@ArneVogel Если бы ты мог это сделать, это было бы очень поучительно, по крайней мере, для меня.

Oliv 17.07.2018 13:47
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
20
8
988
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

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. Вот что я имел в виду, говоря «солгать компоновщику». Потому что, объявляя переменные по-разному в разных ЕП, вы именно это и сделали. Ой!

Будущим читателям (и мне на самом деле) будет полезно, если вы расскажете подробнее о том, что означают обеспечить это и солгать компоновщику. Может примеры? Не могли бы вы подробнее рассказать о своем ответе? Спасибо.

skypjack 17.07.2018 08:41

Ржу не могу. Хорошо, сделал это, спасибо, хорошее замечание. ... А теперь я его еще немного улучшил, как раз для спешки перед завтраком.

Paul Sanders 17.07.2018 08:51

Не проблема. Спасибо за ваше терпение. У меня вопрос. Вы говорите - Это зависит от вас (убедившись, что все объявления на самом деле одинаковы). Это означает, что он отлично работает без границ, если я помещаю объявление встроенной переменной в файл .hpp библиотеки только для заголовков, а затем включаю его из единиц перевода как исполняемого файла, так и общей библиотеки, связанной самим исполняемым файлом ? Учтите, что библиотека может быть создана кем-то еще позже.

skypjack 17.07.2018 08:54

Не волнуйтесь. Терпение - это добродетель, и я сам не очень хорошо с этим справляюсь, поэтому возможность попрактиковаться всегда приветствуется. И ответ на ваш вопрос, я думаю, положительный, но если у вас есть какие-либо сомнения, я бы создал программу быстрого тестирования, чтобы быть уверенным.

Paul Sanders 17.07.2018 09:02

Что ж, этот ответ не дает никакого прогресса в направлении ясного решения вопроса. Гарантировано ли стандартом предположение об уникальности в пределах границ библиотеки? Вот в чем вопрос. И все, что я здесь вижу, это «[...] Я верю, что да». Да, я тоже так думаю. Но OP хочет не этого. Ему нужны факты и гарантии. И простого тестирования его для каждой платформы может быть недостаточно, поскольку, если это не гарантируется, обновление компилятора / компоновщика может позже изменить его поведение. Это просто поспешный ответ без фактов. Извините

phön 17.07.2018 09:24

Объявляя переменные по-разному в разных ЕП, вы нарушаете ODR, что делает программу некорректной, независимо от статической или динамической компоновки. Есть много других способов сделать программу некорректной.

n. 1.8e9-where's-my-share m. 17.07.2018 11:30

Очевидно, что вы должны предоставить точно такое же объявление. Это первое предварительное условие, и мы это уже знаем, потому что это четко указано в стандарте. Но это не совсем вопрос, а именно: у меня есть встроенная переменная v в заголовке, включаю этот заголовок в статическую библиотеку s, динамическую библиотеку d и в исполняемый файл e. Достаточно ли «умны» компоновщик и среда выполнения, чтобы управлять тем, что все (библиотеки и exe) ссылаются на ИДЕНТИЧНУЮ переменную с одним и тем же адресом. Или может случиться так, что один из них работает со своей собственной копией, и поэтому мы по-настоящему не разделяем ее.

phön 17.07.2018 11:35

@nm Ах да. Но что происходит, когда вы это делаете (а почему)?

Paul Sanders 17.07.2018 11:40

@PaulSanders Неважно, что происходит, так как это неопределенное поведение.

phön 17.07.2018 11:41

@PaulSanders Мне интересно. Я хочу знать, могу ли я использовать уникальность встроенной переменной или нет. Мне не интересно знать, что может случиться, если я нарушу правила. (И просто чтобы ответить на ваше любопытство: компоновщик, вероятно, просто выберет первое определение, которое он видит. Но технически приложение может делать что угодно, например, формировать ваш жесткий диск (но я думаю, что этого не будет ;-)))

phön 17.07.2018 11:48

В документе gcc «во время компоновки» означает во время фазы компоновки различных объектных файлов (.o) создание исполняемого файла или разделяемой библиотеки. Это происходит до выполнения кода, поэтому UB здесь нет. Проблема возникает, когда изображения из общей библиотеки или загружаются в память во время выполнения.

Oliv 17.07.2018 13:53

@oliv ОК. Представьте, что я написал «вы солгали компоновщику и / или загрузчику». Такая же разница. Или, по-моему, иначе: «Не объявляйте встроенные переменные в файлах заголовков в коде библиотеки». Потому что, давайте посмотрим правде в глаза, в чем смысл? Что задумано как хороший способ сказать: «ребята, ребята, давайте не будем слишком зацикливаться на этом, мы рискуем ошибочно принять лес за деревья здесь». Или как там.

Paul Sanders 17.07.2018 14:33

@PaulSanders Это C++, мы в опасности (из-за необходимости Валлона), и поэтому мы должны понимать, что будет дальше. На самом деле нет UB, UB - это стандартизированные термины, которые определяют операцию, которую компилятор может предполагать, что она никогда не произойдет (что помогает оптимизации). Единственная проблема здесь в том, что его номера будут меняться от компиляции к компиляции и будут меняться, если разделяемая библиотека динамически загружается в разных порядках.

Oliv 17.07.2018 15:11

Извините, @Oliv, я не понимаю, о чем вы говорите, и предпочел бы, чтобы вы мне больше не писали.

Paul Sanders 17.07.2018 16:15
Ответ принят как подходящий

Вот как я интерпретирую стандарт. Согласно basic.link/1:

A program consists of one or more translation units linked together.

Здесь ничего не говорится ни о статической, ни о динамической компоновке. Программа - это связанные между собой единицы перевода. Не имеет значения, выполняется ли связывание в два этапа (сначала создайте .dll / .so, а затем динамический компоновщик связывает вместе все динамические библиотеки + исполняемый файл).

Итак, в моей интерпретации не имеет значения, связана ли программа динамически или статически, реализация должна вести себя одинаково: статическая переменная класса должна быть уникальной (независимо от того, встроена она или нет).

В Linux это правда.

В Windows это работает не во всех обстоятельствах, поэтому, в моей интерпретации, в этих обстоятельствах он нарушает стандарт (если вы создаете отдельный .dll, который содержит статическую, не встроенную переменную и все другие .dll и exe относится к этой переменной, работает).

Привет @geza. Я думаю, что ваш ответ + мой ответ = полный ответ, проголосовали за.

Paul Sanders 17.07.2018 10:26

Другими словами (поправьте меня, если я ошибаюсь), он должен работать должным образом, но не в Windows (это платформа, о которой я забочусь), поэтому я не могу полагаться на это решение. Понятно?

skypjack 17.07.2018 11:09

@skypjack: Я бы сказал, вам стоит попробовать это в Windows. Или даже задайте новый вопрос с тегом Windows об этой проблеме в Windows. Надеюсь, здесь есть гуру по Windows, которые могут подробнее рассказать об этом. Но даже вы должны попробовать это. В прошлом MSVC была известна плохим соответствием стандартам. В настоящее время они стараются соответствовать стандартам, но они также должны оставаться совместимыми со старым.

geza 17.07.2018 11:16

@skypjack: но, насколько я знаю, если вы поместите статическую переменную в dll (а не в exe) и свяжете эту dll с другими dll (которые используют эту переменную), вы тоже в Windows.

geza 17.07.2018 11:17

Я думаю, еще одно, что нужно добавить к списку нелепостей dll для Windows.

Passer By 17.07.2018 11:27

@PasserBy: Я не эксперт по Windows, поэтому я порекомендовал задать вопрос, касающийся новых окон. Насколько я знаю, в Windows нужно указывать все зависимости при связывании dll. В linux мы можем оставить символы неразрешенными при создании .so, поэтому проблема OP решается автоматически простым способом. Это важное различие между двумя системами. Но, возможно, это уже не так, я в последнее время не в курсе окон (и, возможно, есть обходной путь, который работает для всех старых окон, кто знает ...).

geza 17.07.2018 11:36

Да, @skypjack. Если вас это беспокоит [в Windows], попробуй это! (И отправьте ответ. Я согласен, что это законный договор, эти парни из MSVC всегда проделывают эти сумасшедшие трюки :). Но я думаю, что уже сказал, что вся эта болтовня просто загромождает нить, ну ладно, ребята.

Paul Sanders 17.07.2018 11:43

@skypjack для Windows уже был дан ответ: stackoverflow.com/q/19373061/1207195 (и, по крайней мере, в этом случае, речь идет не о соответствии компилятора)

Adriano Repetti 17.07.2018 14:12

@AdrianoRepetti Спасибо за ссылку !!

skypjack 18.07.2018 08:54

@AdrianoRepetti Другой вопрос - динамические библиотеки на самом деле не определены стандартом C++, это территория, специфичная для платформы, что означает, что они намного менее надежны / переносимы. Я имею в виду, похоже, что все предполагает, что это могло сработать, но у вас нет гарантии, что он будет работать, даже если он сделает это сегодня. Я ошибся? Поскольку у меня также есть альтернативные решения, возможно, начинать таким образом борьбу с разделяемыми библиотеками - не лучшая идея. Имеет ли это смысл?

skypjack 18.07.2018 09:02

Да, это зависит от платформы, и это МОЖЕТ быть сделано как в Windows, так и в Linux (я полагаю, также и в большинстве других * nix). Портативный? Нет. Стабильный? Для конкретной платформы, но да (если это не взлом, но вы используете ОС). Не уверен, какова ваша конечная цель.

Adriano Repetti 18.07.2018 09:53

В 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++ здесь не нарушается, потому что нет никаких. Шапо. +1
skypjack 19.07.2018 09:47

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