Re[25]: Чудеса инкремента...
От: Павел Кузнецов  
Дата: 10.09.02 11:53
Оценка:
Здравствуйте dupamid, Вы писали:

ПК>>>>Нет, любая реализация вольна "определить" любое неопределенное поведение.

D>>>Может, но программа от этого все равно останется ill-formed, так как полагается на неопределенное поведение.

ПК>>ill-formed означает, что в программе есть синтаксические ошибки и/или диагностируемые семантические и/или нарушено One Definition Rule. Выполнение well-formed программы может приводить к undefined behavior.


D>Вот определения undefined и unspecified из Стандарта


И где из них следует, что программа, выполнение которой приводит к undefined behavior является ill-formed?

D>(выдрано из FD так как нет Стандарта под рукой, но думаю, что отличий нет). Так что программа unspecified является well-formed, а вот undefined – это ошибочная программа, для которой Стандарт ничего не гарантирует.


То, что стандарт не накладывает никаких ограничений на поведение программы, не означает, что она ill-formed. Ill-formed в общем означает, что компилятор должен выдавать диагностику (за исключением ODR, что оговорено отдельно).

D>Так что даже если реализация все уточнит программа все равно останется невалидной с точки зрения Стандарта.


Ее выполнение в общем случае, согласно стандарту, будет приводить к undefined behavior, но ill-formed она от этого не станет. Вот определение ill-formed и well-formed, медитируй

1.3.4 ill-formed
program input to a C++ implementation that is not a well-formed program (1.3.14).


1.3.14 well-formed program
a C++ program constructed according to the syntax rules, diagnosable semantic rules, and the One Definition Rule (3.2).
... << J 1.0 alpha 4 >>
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[21]: Чудеса инкремента...
От: Павел Кузнецов  
Дата: 11.09.02 07:17
Оценка:
Здравствуйте dupamid, Вы писали:

ПК>>Например, в случае "++*p + ++*q" — на всех мне известных. Например, нельзя сгенерировать последовательность такую: <...>


Я вспомнил, что имелось в виду, краткость — сестра неясности Пример должен быть таким:

// (1)

поместить *p в регистр A
поместить *q в регистр B

// (2)

нарастить A
нарастить B
сложить A и B

// (3)

сохранить значение из регистра A в *p
сохранить значение из регистра B в *q


Учитывая, что если к моменту вычисления выражения значения *p и *q уже использовались и p и q не volatile, они уже могут быть во вполне подходящих регистрах. Это означает, что действия (1) и (3) в этом случае вообще могут быть опущены и сгенерирована еще более простая последовательность:

нарастить A
нарастить B
сложить A и B


Если же потребовать, чтобы результат вычисления выражения "++*p + ++*q" был определен даже в случае, если p и q указывают на один и тот же скалярный объект, то подобная оптимизация (встречающаяся на каждом шагу), станет невозможной.
<< J 1.0 alpha 4 >>
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[26]: Чудеса инкремента...
От: dupamid Россия  
Дата: 11.09.02 08:16
Оценка:
Здравствуйте Павел Кузнецов, Вы писали:

D>>Формула sizeof(тип) * CHAR_BIT вообще в корне не верна – она не позволяет вычислить число бит в типе.


ПК>Формула sizeof(тип) * CHAR_BIT позволяет вычислить число бит в объектном представлении типа (object representation), т.е. число бит, которое необходимо для хранения значения данного типа. Возможно, что не все из этих бит будут значащими (value representation).


ПК>3.9 Types

ПК>4 The object representation of an object of type T is the sequence of N unsigned char objects taken up
ПК>by the object of type T, where N equals sizeof(T). The value representation of an object is the set of bits that hold the value of type T. For POD types, the value representation is a set of bits in the object representation that determines a value, which is one discrete element of an implementationdefined set of values.


Спасибо, Вы опять покинули мне очень интересные ссылки!

D>>Пример, может быть система, на которой sizeof(type) для всех встроенных типов и «обычных указателей» равен 1, но число бит и диапазоны значений для типов разные.


ПК>Нет, такая система невозможна.


ПК>3.9.1 Fundamental types

ПК>1 For character types, all bits of the object representation participate in the value representation. For unsigned character types, all possible bit patterns of the value representation represent numbers. These requirements do not hold for other types.


ПК>Кроме того,


ПК>2 “signed char”, “short int”, “int”, and “long int.” In this list, each type provides at least as much storage as those preceding it in the list.


ПК>Таким образом, т.к. все биты представления char являются значащими, и short представляет не меньший диапазон значений, чем char, а также sizeof(short) == 1, все биты short также являются значащими. То же по индукции для остальных целочисленных типов.


Пункта 3.9\4 достаточно, чтобы понять, что система, где sizeof всех встроенных типов 1, а число бит разное, невозможна. Размеры всех встроенных типов могут быть одинаковы, но тогда число бит в object representation char должно быть столько же, сколько в long и в любом другом типе и все должны быть значимы.

ПК>>>Прерывания, так же как и (почти?) любая асинхронность, являются понятиями за пределами стандарта. Поэтому, фактически, любое асинхронное взаимодействие само по себе автоматом приводит к undefined behavior.


D>>Сигналы не являются понятиями за пределами Стандарта, а они асинхронны.


ПК>Именно поэтому я и написал "почти". Но стандарт, фактически, не определяет семантику сигналов, а просто констатирует их наличие. Однако, если в твоем примере заменить прерывание на сигнал, то:


ПК>[i]9 When the processing of the abstract machine is interrupted by receipt of a signal, the values of objects with type other than volatile sig_atomic_t are unspecified, and the value of any object not of volatile sig_atomic_t that is modified by the handler becomes undefined.[/b]


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

ПК>То, что стандарт не накладывает никаких ограничений на поведение программы, не означает, что она ill-formed. Ill-formed в общем означает, что компилятор должен выдавать диагностику (за исключением ODR, что оговорено отдельно).


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

Вообще вопрос, видимо, перерос все разумные рамки, то его можно переместить в философию.
Re[27]: Чудеса инкремента...
От: Павел Кузнецов  
Дата: 11.09.02 09:01
Оценка:
Здравствуйте dupamid, Вы писали:

ПК>>если в твоем примере заменить прерывание на сигнал, то:


ПК>>9 When the processing of the abstract machine is interrupted by receipt of a signal, the values of objects with type other than volatile sig_atomic_t are unspecified, and the value of any object not of volatile sig_atomic_t that is modified by the handler becomes undefined.


D>Обработчик сигнала ничего менять не будет, просто сам факт возникновения сигнала может сорвать запись в ячейку, если не предпринимать особых усилий. Суть моего примера в том, что Стандарт должен подталкивать реализации к более безопасным программа с определенным поведением,


Именно это он и делает, где это возможно. Например, в случае сигналов, результат как раз unspecified. Более ничего гарантировать нельзя.

D>а не говорить, что если где-то что-то может привести к неопределенному поведению, то на всех системах будет неопределенное поведение.


А толку от таких требований стандарта? Так хоть заранее известно, что данная "фича" непереносима с системы на систему, а в противном случае в стандарте поведение будет определено, а в реальности — нет. Стандарт не предназначен для "дисциплинирования" реализаторов, он просто-напросто закрепляет общие моменты существующих реализаций. Иначе стандарт рискует попасть в положение игнорируемого реализаторами документа. Что и без того наблюдается в некоторых моментах, где комитет позволил себе "новаторство": export, exception specifications etc.

Таким образом, раз на части платформ поведение неопределено, то есть два выбора: либо объявить поведение undefined, либо объявить поведение unspecified, а реализации на этих платформах несоответствующими стандарту. Учитывая, что хрен (unspecified) редьки (undefined) не слаще и то, что в голосовании принимают участие и разработчики этих реализаций, выбор почти очевиден.

Кроме того, для меня, как и для многих других пользователей, выгоднее иметь более агрессивную оптимизацию со стороны компилятора, чем такую "фичу". А те, кто готовы платить за надежность ценой производительности, пусть используют обертки вокруг встроенных типов — там все в этом отношении определено.

D>Написав ill-formed я погорячился. Вообще любая реализация может выполнять программы содержащие ошибки, да она не будет полностью конформной Стандарту, но так как таких реализаций вообще не существует (на сколько мне известно), то это не имеет значения.


Любая реализация может скомпилировать программу, приводящую к undefined behavior, и при этом оставаться полностью соответствующей стандарту.

D>Вообще вопрос, видимо, перерос все разумные рамки, то его можно переместить в философию.


Ну, причем здесь "философия"? Вопрос-то чисто технический, по C++. Так что никуда ничего переносить не надо. А размеры... Так, непростой вопрос, вот обсуждение и затянулось. Главное, оставить его в "технических" рамках, не прибегая к "философским" аргументам
<< J 1.0 alpha 4 >>
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[28]: Чудеса инкремента...
От: dupamid Россия  
Дата: 11.09.02 10:38
Оценка:
Здравствуйте Павел Кузнецов, Вы писали:

D>>а не говорить, что если где-то что-то может привести к неопределенному поведению, то на всех системах будет неопределенное поведение.


ПК>А толку от таких требований стандарта? Так хоть заранее известно, что данная "фича" непереносима с системы на систему, а в противном случае в стандарте поведение будет определено, а в реальности — нет. Стандарт не предназначен для "дисциплинирования" реализаторов, он просто-напросто закрепляет общие моменты существующих реализаций. Иначе стандарт рискует попасть в положение игнорируемого реализаторами документа. Что и без того наблюдается в некоторых моментах, где комитет позволил себе "новаторство": export, exception specifications etc.


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

Стандарт не просто закрепляет общие моменты, но и вырабатывает новые свойства языка (что нас в скором будущем обязательно ждет). Язык С++ разрабатывался достаточно давно и ему не хватает некоторых свойств, и если он не хочет умереть и остаться одним языком, а не тучей реализаций, то должен развиваться. Так что я не согласен с таким констатирующим свойством Стандарта, в начале это было нужно, чтобы свести все реализации вместе, но сейчас он должен предлагать новые свойства и дисциплинировать реализации (хороший термин). Это не только мои мысли, но и соображения Страуструпа на будущее С++.

ПК>Таким образом, раз на части платформ поведение неопределено, то есть два выбора: либо объявить поведение undefined, либо объявить поведение unspecified, а реализации на этих платформах несоответствующими стандарту. Учитывая, что хрен (unspecified) редьки (undefined) не слаще и то, что в голосовании принимают участие и разработчики этих реализаций, выбор почти очевиден.


ПК>Кроме того, для меня, как и для многих других пользователей, выгоднее иметь более агрессивную оптимизацию со стороны компилятора, чем такую "фичу". А те, кто готовы платить за надежность ценой производительности, пусть используют обертки вокруг встроенных типов — там все в этом отношении определено.


Обертки намного более медленные. Как известно, какая бы не была агрессивная оптимизация компилятора, она обычно дает только небольшой прирост производительности по сравнению с выбором других алгоритмов внутри программы. Оптимизация почти никогда (стараюсь быть менее категоричным) не может сделать из алгоритма O(n) алгоритм O(log(n)). Здесь же проигрыш в эффективности на большинстве систем вообще никакой, и незначительный на системах, где потребуется специальная поддержка.

D>>Написав ill-formed я погорячился. Вообще любая реализация может выполнять программы содержащие ошибки, да она не будет полностью конформной Стандарту, но так как таких реализаций вообще не существует (на сколько мне известно), то это не имеет значения.


ПК>Любая реализация может скомпилировать программу, приводящую к undefined behavior, и при этом оставаться полностью соответствующей стандарту.


Здесь возникает вопрос о теоретической возможности существования реализации полностью соответствующей Стандарту. Речь идет, конечно же, не о рекламных заявлениях, которые часто мало чему соответствуют, а о реальной жизни, Стандарт вещь достаточно противоречивая и содержит ошибки, опечатки и т.д.
Re[29]: Чудеса инкремента...
От: Павел Кузнецов  
Дата: 11.09.02 12:07
Оценка:
Здравствуйте dupamid, Вы писали:

D>Данная фича переносима в том смысле, что можно ее записать так чтобы в выражении появилась точка следования и тогда все должно быть точно определено в любом случае (может быть будет нужна дополнительная временная переменная, а может быть она будет нужна в любом случае при генерации).


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

D>Стандарт не просто закрепляет общие моменты, но и вырабатывает новые свойства языка (что нас в скором будущем обязательно ждет).


Это, интересно, откуда такие сведения? Все известные мне высказывания членов комитета стандартизации говорят как раз об обратном.

D>Язык С++ разрабатывался достаточно давно и ему не хватает некоторых свойств, и если он не хочет умереть и остаться одним языком, а не тучей реализаций, то должен развиваться.


Именно для того, чтобы реализации могли бы догнать оторвавшийся от жизни стандарт, в ближайшей ревизии запланировано минимизировать изменения/дополнения языка, и, в основном, планируется ограничиться модификацией спецификаций стандартных библиотек. Более того, в дальнейшем, в целях избежания казусов, подобных export или exception specification, запланировано включать в стандарт только вещи, прошедшие проверку, желательно в качестве расширения хотя бы на одной из существующих реализаций.

D>Так что я не согласен с таким констатирующим свойством Стандарта, в начале это было нужно, чтобы свести все реализации вместе, но сейчас он должен предлагать новые свойства и дисциплинировать реализации (хороший термин).


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

Если ты действительно хочешь, чтобы модификация скалярных объектов между соседними точками следования не приводила к неопределенному поведению — пожалуйста. Самым легким способом достичь этого является примерно следующий (использован текст сообщения Daniel Miller <daniel.miller@tellabs.com> из comp.std.c++):

1) Реализуй подобный компилятор (или измени существующий) сам, или вместе с теми, кто разделяет твою точку зрения. Если ты сам не можешь сделать этого, и не можешь найти никого, кто согласен тебе в этом помочь, то, считай, что ничего из твоей идеи не выгорит — если ты не можешь убедить одного разработчика, как ты надеешься убедить целый скептически настроенный комитет? Кроме того, без reference implementation изменения языка не рассматриваются. Учти, что то, что ты делаешь на этой стадии может иметь существеннейшие последствия для всех реализаций, поэтому твоей задачей является учесть все подводные камни, встречающиеся при реализации этой "фичи". Это как раз и есть то время, когда приветствуются изобретательность, новаторство, исследования и т.п. В течение дальнейших шагов изменения вносить все тяжелее и тяжелее, т.к. все большее кол-во людей зависят от деталей спецификации. Если что-то будет упущено на этом этапе, идея будет все больше закрепляться в ущербном виде.

2) Если ты преуспел на первом этапе — хорошо. Теперь настало время получить результаты более широкого тестирования этой идеи путем включения ранее тестовой версии в распространяемую версию какого-нибудь компилятора. Это, естественно, автоматически означает некоторое закрепление спецификации "фичи", так как она со временем тиражируется все шире и шире.

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

4) Наконец, очередной шаг стандартизации неминуемо подхватит "фичу" как образец существующей практики и она уже будет зацементирована в тексте стандарта.

D>Это не только мои мысли, но и соображения Страуструпа на будущее С++.


Цитату, пожалуйста. Такого он, насколько я знаю, не говорил, напротив, по крайней мере, в тех интервью, которые мне встречались, он высказывался в духе минимизации core language additions в ближайшей ревизии стандарта.

ПК>>для меня, как и для многих других пользователей, выгоднее иметь более агрессивную оптимизацию со стороны компилятора, чем такую "фичу". А те, кто готовы платить за надежность ценой производительности, пусть используют обертки вокруг встроенных типов — там все в этом отношении определено.


D>Обертки намного более медленные.


Ты сам себе противоречишь. Обертки медленнее именно из-за наличия точек следования. Если тебя это устраивает — пользуйся ими, если нет — будь готов мириться с некоторыми нюансами использования скалярных типов. Кроме точек следования, ничто не заставляет компилятор трактовать простые обертки по-другому в отношении оптимизации, чем скалярные типы. (1)

D>Как известно, какая бы не была агрессивная оптимизация компилятора, она обычно дает только небольшой прирост производительности по сравнению с выбором других алгоритмов внутри программы.


Да?.. Например, на некоторых задачах, использование более агрессивно оптимизирующего Intel C++ Compiler по сравнению с Visual C++ 6.0 приводило к двойному ускорению программы в целом.

D>Оптимизация почти никогда (стараюсь быть менее категоричным) не может сделать из алгоритма O(n) алгоритм O(log(n)).


Ну и что? Замедление фрагмента программы, отвечающего за отрисовку картинки в игре на 50% запросто может приводить к плюс-минус соответствующему уменьшению fps, что уж никак нельзя назвать незначительным.

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


Без достаточного обоснования выглядит как достаточно поверхностное суждение. См. (1). Если ты хочешь убедить меня в том, что определенность неоднократной модификации скалярных объектов между соседними точками следования действительно не приводит к существенному снижению способности компилятора к оптимизации — пожалуйста.

1) Опиши, к какому результату, согласно предлагаемой спецификации, должно приводить выполнение выражения "++*p + ++*q" в случае, если p и q указывают на один и тот же объект.

2) Напиши, как по-твоему в таком случае, должен быть сгенерирован следующий фрагмент кода:

int foo(int* p, int *q)
{
  return ++*p + ++*q;
}


3) Сравни предложенный результат с, например, таким:

[code]
mov eax, DWORD PTR _p$[esp-4]
inc DWORD PTR [eax]
mov ecx, DWORD PTR _q$[esp-4]
mov edx, DWORD PTR [ecx]
inc edx
mov DWORD PTR [ecx], edx
mov eax, DWORD PTR [eax]
mov ecx, edx
add eax, ecx
ret 0[/ccode]

Сравнение должно включать в себя времена выполнения.

4) То же самое в случае (++*p + ++*q) + (++*r + ++*s).

5) Опиши необходимые изменения в "типичной" кодогенерации по сравнению с текущим состоянием дел.
<< J 1.0 alpha 4 >>
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[30]: Чудеса инкремента...
От: dupamid Россия  
Дата: 11.09.02 12:28
Оценка: 5 (1)
Здравствуйте Павел Кузнецов, Вы писали:

ПК>Это, интересно, откуда такие сведения? Все известные мне высказывания членов комитета стандартизации говорят как раз об обратном.


Пока я пишу подробный ответ по остальным вопросам, посмотри ссылочку http://www.research.att.com/~bs/kbh-C++0x.pdf, это он говорил перед комитетом по стандартизации в мае прошлого года в Копенгагене. Хорошо, что я нашел это в открытом виде, а то этот документ у меня был из источника, откуда сведения я не могу разглашать, наше через www.altavista.com.
Re[31]: Чудеса инкремента...
От: dupamid Россия  
Дата: 11.09.02 12:48
Оценка:
Еще одна ссылка в догонку http://www-cdserver.fnal.gov/cd_public/sag/J16/J16.htm. С этого же сайта http://www-cdserver.fnal.gov/cd_public/sag/J16/J16_files/slide0037.htm. Цитата:

Core Language Ideas
• Improve consistency and portability:
§ Unify lookup between functions & functors
§ Minimize “implementation-defined” & “undefined”

• Provide guarantees for general concurrency
§ Atomicity of selected operations
§ Signal-handling requirements

• Remove language impediments to use of
garbage-collection for memory management
Re[32]: Чудеса инкремента...
От: Павел Кузнецов  
Дата: 11.09.02 12:53
Оценка:
Здравствуйте dupamid, Вы писали:

D> Core Language Ideas

D>• Improve consistency and portability:
D> § Unify lookup between functions & functors
D> § Minimize “implementation-defined” & “undefined”

D>• Provide guarantees for general concurrency

D> § Atomicity of selected operations
D> § Signal-handling requirements

D>• Remove language impediments to use of

D> garbage-collection for memory management

Снова-таки, ни слова в подтверждение твоего высказывания о том, что "Стандарт не просто закрепляет общие моменты, но и вырабатывает новые свойства языка". Minimize “implementation-defined” & “undefined” ни в коей мере не является аргументом в поддержку твоих высказываний о точках следования.
<< J 1.0 alpha 4 >>
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[31]: Чудеса инкремента...
От: Павел Кузнецов  
Дата: 11.09.02 13:35
Оценка:
Здравствуйте dupamid, Вы писали:

ПК>>Это, интересно, откуда такие сведения? Все известные мне высказывания членов комитета стандартизации говорят как раз об обратном.


D>Пока я пишу подробный ответ по остальным вопросам, посмотри ссылочку <...>


Это все очень старое и очень известное.
1) Там ничего не говорится о том, что комитет будет источником новшеств.
2) Подтверждается, что "No major new language features are needed", "Avoid major (language) extensions".
3) Все предлагаемые core language ideas сводятся к исправлению "ляпов" ("identical lookup for functions and function objects", "Minimize “implementation dependent/undefined/…”"), узакониванию существующей практики ("Typedef templates, maybe typeof()") и некоторым "политическим" шагам типа совместимости с C, о чем в CUJ было напечатано три статьи.
4) "Encourage implementers to introduce new features in a common defined order" дополнительно подчеркивает, что источником новшеств должны служить существующие реализации.

"Evolution WG Proposal Skeleton" (N1364) специально построен так, что внесение какого-либо предложения по добавлению или изменению чего-либо в языке/стандартных библиотеках, требует reference implementation. Проще говоря, если твои источники говорят тебе, что комитет будет что-то изобретать, то они просто не верны. Для подтверждения полистай comp.std.c++. Это не означает, что в новой версии стандарта не будет ничего нового. Однако, новшества должны быть обязательно опробованы на практике, что для core language additions означает введение их в какой-либо реализации в качестве расширения до того.
<< J 1.0 alpha 4 >>
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[33]: Чудеса инкремента...
От: dupamid Россия  
Дата: 11.09.02 13:41
Оценка:
Здравствуйте Павел Кузнецов, Вы писали:

ПК>Снова-таки, ни слова в подтверждение твоего высказывания о том, что "Стандарт не просто закрепляет общие моменты, но и вырабатывает новые свойства языка".


Комитет уже сейчас обсуждает новые свойства языка (я это знаю абсолютно точно), которые пока никто в С++ не реализовал, это новые идеи, которые только вырабатываются, так что Стандарт не просто закрепляет общие моменты.

ПК>Minimize “implementation-defined” & “undefined” ни в коей мере не является аргументом в поддержку твоих высказываний о точках следования.


Сокращение implementation-defined & undefined это именно то, о чем я говорю, Стандарт должен больше требовать от реализаций, у нее должно быть меньше свобод, Стандарт должен гарантировать выполнение программ.
Re[34]: Чудеса инкремента...
От: Павел Кузнецов  
Дата: 11.09.02 13:59
Оценка:
Здравствуйте dupamid, Вы писали:

D>Комитет уже сейчас обсуждает новые свойства языка (я это знаю абсолютно точно), которые пока никто в С++ не реализовал, это новые идеи, которые только вырабатываются, так что Стандарт не просто закрепляет общие моменты.


Например? Мне кажется ты путаешь понятия "комитет" и "члены комитета".

ПК>>Minimize “implementation-defined” & “undefined” ни в коей мере не является аргументом в поддержку твоих высказываний о точках следования.


D>Сокращение implementation-defined & undefined это именно то, о чем я говорю,


Но, это не означает, что упомянутые “implementation-defined” & “undefined” включают в себя сокращение undefined в случае неоднократной модификации скалярных объектов между соседними точками следования До тех пор, пока не будет показано обратное, аргументом в пользу твоих рассуждений это не станет.
<< J 1.0 alpha 4 >>
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[32]: Чудеса инкремента...
От: dupamid Россия  
Дата: 11.09.02 14:03
Оценка:
Здравствуйте Павел Кузнецов.

Это похоже на разговор слепого с глухим.

Мои сведенья основываются на чтении внутренней переписки комитета, т.е. из самых первых рук. Я вижу, как выдвигаются предложения по новым свойствам, вижу насколько они сырые, и как оттачиваются. Это предложения — реализациями там пока и не пахнет. Может быть, перед введением в Стандарт они будут где-то обкатываться, но предложения о новых свойствах поступают изнутри комитета, а не снаружи, в частности и от самого Страуструпа.
Re[33]: Чудеса инкремента...
От: Павел Кузнецов  
Дата: 11.09.02 14:10
Оценка:
Здравствуйте dupamid, Вы писали:

D>Это похоже на разговор слепого с глухим.


На твое усмотрение.

D>Мои сведенья основываются на чтении внутренней переписки комитета, т.е. из самых первых рук.


Это и есть то, что я подразумевал под смешением понятий "комитет" и "члены комитета".

D>Я вижу, как выдвигаются предложения по новым свойствам <...>


Тем не менее, они не войдут в стандарт, пока не будет их практического подтверждения. В случае core language additions это означает почти обязаетельную реализацию в одном из компиляторов с последующей более-менее широкой апробацией.
<< J 1.0 alpha 4 >>
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[34]: Чудеса инкремента...
От: dupamid Россия  
Дата: 11.09.02 21:17
Оценка:
Здравствуйте Павел Кузнецов.

Intel’овский процессор выбран крайне не удачно, так как на нем гарантируется, что при попытке одновременной записи в ячейку, операции будут выполняются последовательно. Поэтому ни один из приводимых примеров для процессоров intel’не приводит к аппаратным исключениями или другому неопределенному поведению. Вернемся к моим начальным примерам:
[ccode]
#include <iostream>
using namespace std;

int i, j, k;

int main()
{
i = i = 1;
cout << "i == " << i << endl;

i = ++i;
cout << "i == " << i << endl;

i = (i = j + k) + (i = j — k);
cout << "i == " << i << endl;
}
[\ccode]
Вывод:
[ccode]
i == 1
i == 2
i == 0
[\ccode]
Приведите пример реализации, на которой вывод этой программы будет другим или произойдет какое-нибудь неопределенное поведение. Теоретически это возможно, но для процессов intel такой компилятор, видимо, найти вообще не удастся.

Теперь другой пример:
[ccode]
#include <iostream>
using namespace std;

int f(int* p, int* q)
{
return ++*p + ++*q;
}

int main()
{
int i = 0;
int j = f(&i, &i);
cout << "i == " << i << "\tj == " << j << endl;
}
[\ccode]
На MSVC.NET вывод:
[ccode]
i == 2 j == 4
[\ccode]
Здесь для intel возможны варианты в зависимости от компилятора и настроек, i может принимать два значения 1 и 2, j 2 и 4, остальные варианты, кажется, не возможны. Но опять таки никакого неопределенного поведения, максимум это можно назвать неспецифицированным поведением. Подчеркиваю еще раз теоретически могут быть другие варианты. Опять нужны примеры реализаций где переменные i и j будут принимать другие значения и/или происходить аппаратные исключения и т.д.

Так что на процессорах intel никаких накладных расходов, на то чтобы сделать поведение программы вместо неопределенным неспецифицированным нет. Если говориться, что существуют процессоры где есть С++ и накладные расходы на неспецифицированное поведение настолько велики, что Стандарт не может их потребовать, то прошу приводить конкретные примеры иначе спор становиться бессмысленным.

Что касается комитета и его членов, то комитет это его члены. Более того некоторые члены комитета, бОльшие члены, чем другие, и личный фактор имеет большое значение в комитете по стандартизации С++.

Что же касается широкой апробации новых свойств С++ и обязательного наличия реализации, то EDG что-то реализует и можно сказать, что есть реализация, так что проблемы с апробацией новых свойств языка в рамках комитета по Стандартизации я не вижу. Более того требование наличия реализация может быть просто вредно, так как если какая-то крупная компания типа MS что-то реализует в своем компиляторе, то это станет стандартом де-факто, хотя это может быть сделано совсем не удачно, но изменить уже ничего будет нельзя. Так что лучше все хорошо продумать чем воплощать в продуктах, которые станут стандартом де-факто, а потом рвать на себе волосы, что получилось не самым лучшим образом и не переноситься на другие платформы.
Re[35]: Чудеса инкремента...
От: Павел Кузнецов  
Дата: 12.09.02 13:27
Оценка:
Здравствуйте dupamid, Вы писали:

D>Intel’овский процессор выбран крайне не удачно, так как на нем гарантируется, что при попытке одновременной записи в ячейку, операции будут выполняются последовательно. Поэтому ни один из приводимых примеров для процессоров intel’не приводит к аппаратным исключениями или другому неопределенному поведению.


В какой-то момент мне показалось, что ты вообще захотел, чтобы поведение в подобных случаях было определенным (не unspecified и не undefined). Мои аргументы в соответствующем сообщении относились именно к этому, можешь их в таком случае игнорировать. Я сейчас пересмотрел тред и увидел, что не вполне правильно тебя понял. Хорошо, "продолжаем разговор".

D>Вернемся к моим начальным примерам:

<... простые примеры неоднократной модификации скалярных объектов между соседними точками следования ...>
D>Приведите пример реализации, на которой вывод этой программы будет другим или произойдет какое-нибудь неопределенное поведение. Теоретически это возможно, но для процессов intel такой компилятор, видимо, найти вообще не удастся.

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

Compaq C Compiler умеет выдавать сообщения вида:
cc: Warning: miscplay.c, line 384: In this statement, the expression 
"*dest++=(unsigned char)(((int)*((signed char ...)(src++))+(int)*((signed char ...)(src++)))/2)" 
modifies the variable "src" more than once without an intervening sequence point.  
This behavior is undefined. (undefvarmod)
    *dest++ = (unsigned char)(((int)*((signed char *)(src++)) +
----^


Говорят, что GCC начиная с версии 3.0 с ключом -Wall тоже делает нечто подобное.

D>Теперь другой пример:

<... ++*p + ++*q ...>
D>Здесь для intel возможны варианты в зависимости от компилятора и настроек, i может принимать два значения 1 и 2, j 2 и 4, остальные варианты, кажется, не возможны. Но опять таки никакого неопределенного поведения, максимум это можно назвать неспецифицированным поведением. Подчеркиваю еще раз теоретически могут быть другие варианты. Опять нужны примеры реализаций где переменные i и j будут принимать другие значения

Ну, насчет других вариантов — легко, только на всякий случай перепишем пример так, чтобы компилятор "не знал" при генерации функции, что p и q указывают на один и тот же объект:

`foo.cpp'
int foo(int* p, int *q)
{
  return ++*p + ++*q;
}


`main.cpp'
#include <iostream>

int foo(int*, int*);

int main()
{
    int i = 0;
    int j = foo(&i, &i);
    std::cout << "i == " << i << "\tj == " << j << std::endl;
}


>icl /Ox main.cpp foo.cpp
Intel(R) C++ Compiler for 32-bit applications, Version 5.0.1   Build 010727Z
Copyright (C) 1985-2001 Intel Corporation.  All rights reserved.


>main.exe
i == 2  j == 3


А если несколько усложнить ситуацию, то получим еще более различные результаты:

`foo.cpp'
int foo(int* p, int* q, int* r, int* s)
{
  *p += ++*r + ++*s;
  *q += ++*r + ++*s;
  return ++*p + ++*q;
}


`main.cpp'
#include <iostream>

int foo(int*, int*, int*, int*);

int main()
{
    int i = 0;
    int j = foo(&i, &i, &i, &i);
    std::cout << "i == " << int(i) << "\tj == " << int(j) << std::endl;
}


>icl /Ox main.cpp foo.cpp
Intel(R) C++ Compiler for 32-bit applications, Version 5.0.1   Build 010727Z
Copyright (C) 1985-2001 Intel Corporation.  All rights reserved.


>main.exe
i == 22 j == 43


>cl /Ox /GX main.cpp foo.cpp
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 13.00.9466 for 80x86
Copyright (C) Microsoft Corporation 1984-2001. All rights reserved.


>main.exe
i == 26 j == 52


Кроме того, если отойти слегка в сторону, например, сообщалось, что SCO Optimizing C compiler на одной из платформ для:
int a = 123, b = 7654;
a ^= b ^= a ^= b;

генерирует код, устанавливающий b в 123 и a в 0.

D>и/или происходить аппаратные исключения и т.д.


Любая векторная платформа, на которой не разрешена одновременная запись в ячейку. В принципе, в свое время в comp.lang.c++.moderated сообщалось, что есть устройства, имеющие (кросс-)компилятор C, для которых запись в одну и ту же ячейку не может осуществляться без задержки. В противном случае, происходит что-нибудь "страшное".

D>Если говориться, что существуют процессоры где есть С++ и накладные расходы на неспецифицированное поведение настолько велики, что Стандарт не может их потребовать, то прошу приводить конкретные примеры иначе спор становиться бессмысленным.


(Почти?) любые векторные суперкомпьютеры или большинство (?) платформ, поддерживающих параллельное выполнение инструкций с записью в память. Например, тот же Cray. Вообще, одна из тенденций даже в современных "настольных" процессорах — поддержка параллельных вычислений (IA-64/EPIC/VLIW, Motorola G4+, G5). При "по-настоящему" параллельном выполнении, описывать возможные варианты результатов выражений с множественной модификацией скалярных объектов между соседними точками следования уже смысла не имеет, если вообще возможно, не говоря уж о дальнейшем поведении программы, если не все объектные представления являются допустимыми. Простейшие примеры: для float это может привести к совершенно неожиданному signaling NaN при вычислении совершенно "безобидного" выражения, для указателей — к null pointer в самом неожиданном месте и т.д. Короче, undefined behavior.

D>Что касается комитета и его членов, то комитет это его члены.


Не вполне. Каждый из членов комитета может на досуге "играться" с новыми/интересными "фичами", обсуждать свои "игрушки" с другими и т.д. Но когда дело дойдет до включения "фичи" в стандарт, кто-то должен будет предоставить reference implementation и результаты практического использования. В данный момент, насколько мне известно, комитет в целом решительно против введения в стандарт неопробованных "фич".

D>Более того некоторые члены комитета, бОльшие члены, чем другие, и личный фактор имеет большое значение в комитете по стандартизации С++.


Естественно, но это никак не влияет на высказанный тезис.

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


Это все равно лучше, чем включить что-то вообще нигде не реализованное в стандарт, т.е. менее продуманное, практические последствия чего полностью изучены быть не могут и т.д.

D>Так что лучше все хорошо продумать чем воплощать


Это не выбор: или — или. Должно быть: и — и.
<< J 1.0 alpha 4 >>
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[36]: Чудеса инкремента...
От: dupamid Россия  
Дата: 13.09.02 07:48
Оценка:
Здравствуйте Павел Кузнецов, Вы писали:

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


Она не просто «не велика», с практической точки зрения можно сказать, что она равна нулю, так как во многих случаях такая поддержка есть на аппаратном уровне и компиляторам вообще ничего специального делать не надо.

ПК>Compaq C Compiler умеет выдавать сообщения вида:


Это, насколько я понимаю, компилятор С, я же говорю о том, что С++ обладает сложной, часто скрытой семантикой и говорю, что для С++ можно и нужно сделать поведение в этом случае не специфицированным, для С его можно оставить не определенным. Тут мне могут сказать, что это будет очередная несовместимость С и С++, отвечу сразу, при наличии всех остальных несовместимостей это уже ничего не меняет. С язык более низкого уровня и может быть менее безопасным.

D>>Теперь другой пример:

ПК><... ++*p + ++*q ...>
D>>Здесь для intel возможны варианты в зависимости от компилятора и настроек, i может принимать два значения 1 и 2, j 2 и 4, остальные варианты, кажется, не возможны. Но опять таки никакого неопределенного поведения, максимум это можно назвать неспецифицированным поведением. Подчеркиваю еще раз теоретически могут быть другие варианты. Опять нужны примеры реализаций где переменные i и j будут принимать другие значения

ПК>Ну, насчет других вариантов — легко, только на всякий случай перепишем пример так, чтобы компилятор "не знал" при генерации функции, что p и q указывают на один и тот же объект


ПК>i == 2 j == 3[/code]


Через пять минут, после того как я послал пример, понял, что возможен вариант «j == 3», но это ничего не меняет, все равно количество вариантов для этого примера сильно ограничено.

ПК>А если несколько усложнить ситуацию, то получим еще более различные результаты:


Это все равно хорошо (я бы сказал великолепно) укладывается в не специфицированное поведение, в случае с порядком вычисления аргументов функции могут быть и еще более тяжелые случаи.

ПК>Кроме того, если отойти слегка в сторону, например, сообщалось, что SCO Optimizing C compiler на одной из платформ для:

ПК>
ПК>int a = 123, b = 7654;
ПК>a ^= b ^= a ^= b;

ПК>генерирует код, устанавливающий b в 123 и a в 0.

Опять компилятор С, и опять дело только в значении. Я слышал множество споров про всякие задачки с 5 плюсами, но никогда не слышал, чтобы они приводили к катастрофическим последствиям, всегда дело было только в значении.

ПК>Любая векторная платформа, на которой не разрешена одновременная запись в ячейку. В принципе, в свое время в comp.lang.c++.moderated сообщалось, что есть устройства, имеющие (кросс-)компилятор C, для которых запись в одну и ту же ячейку не может осуществляться без задержки. В противном случае, происходит что-нибудь "страшное".


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

D>>Если говориться, что существуют процессоры где есть С++ и накладные расходы на неспецифицированное поведение настолько велики, что Стандарт не может их потребовать, то прошу приводить конкретные примеры иначе спор становиться бессмысленным.


ПК>(Почти?) любые векторные суперкомпьютеры или большинство (?) платформ, поддерживающих параллельное выполнение инструкций с записью в память. Например, тот же Cray. Вообще, одна из тенденций даже в современных "настольных" процессорах — поддержка параллельных вычислений (IA-64/EPIC/VLIW, Motorola G4+, G5). При "по-настоящему" параллельном выполнении, описывать возможные варианты результатов выражений с множественной модификацией скалярных объектов между соседними точками следования уже смысла не имеет, если вообще возможно, не говоря уж о дальнейшем поведении программы, если не все объектные представления являются допустимыми. Простейшие примеры: для float это может привести к совершенно неожиданному signaling NaN при вычислении совершенно "безобидного" выражения, для указателей — к null pointer в самом неожиданном месте и т.д. Короче, undefined behavior.


Вот я и хочу увидеть хоть одну систему, где действительно происходит undefined behavior, я слышу только слова о теоретический возможности. Реальные примеры есть?

D>>Что касается комитета и его членов, то комитет это его члены.


ПК>Не вполне. Каждый из членов комитета может на досуге "играться" с новыми/интересными "фичами", обсуждать свои "игрушки" с другими и т.д. Но когда дело дойдет до включения "фичи" в стандарт, кто-то должен будет предоставить reference implementation и результаты практического использования. В данный момент, насколько мне известно, комитет в целом решительно против введения в стандарт неопробованных "фич".


Что значит не опробованные фичи? Значения по умолчанию для параметров шаблонных функций (Item 226 Default template arguments for function templates), это как опробованная фича или нет, или, например, (Default arguments in template template-parameters 184).

D>>требование наличия реализация может быть просто вредно, так как если какая-то крупная компания типа MS что-то реализует в своем компиляторе, то это станет стандартом де-факто, хотя это может быть сделано совсем не удачно, но изменить уже ничего будет нельзя. Так что лучше все хорошо продумать, чем воплощать


ПК>Это не выбор: или — или. Должно быть: и — и.


Я не утверждаю, что такой выбор это хорошо, но реальная жизнь такова, что часто вопрос стоит именно так «или – или».
Re[37]: Чудеса инкремента...
От: Павел Кузнецов  
Дата: 14.09.02 15:04
Оценка: 10 (1)
Здравствуйте dupamid, Вы писали:

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


D>Она не просто «не велика», с практической точки зрения можно сказать, что она равна нулю, так как во многих случаях такая поддержка есть на аппаратном уровне и компиляторам вообще ничего специального делать не надо.


Не равна. Например, в comp.lang.c[++][.moderated] сообщалось, что один из реальных компиляторов для `i = 2; ... i = ++i;' порождал код, в результате которого i == 4 (этот вопрос даже вынесен в comp.lang.c FAQ). Причем это легко объяснить, если рассмотреть `i = i++', например, в таком контексте:

int i = 2;
...
x = i + 1; // после этой точки `i + 1' уже может находиться в регистре
...
i = ++i;   // здесь компилятор вполне может этим воспользоваться


Последовательность инструкций, в данном случае схематично могла быть такой:

mov i, 2   ; i = 2                             i == 2
mov r, 2   ; поместить значение i в регистр    r == 2
...
inc r      ; вычислить i + 1                   r == 3
mov x, r   ; x = i + 1
...
mov i, r   ; i = i + 1                         i == 3
inc i      ; ++i                               i == 4


Как видишь, она вполне логична, т.к. нет лишних чтений значения i из памяти (т.к. i не является volatile, подобная оптимизация со стороны компилятора вполне допустима).

ПК>>Compaq C Compiler умеет выдавать сообщения вида:


D>Это, насколько я понимаю, компилятор С,


Без разницы.

Вообще:
1) Компиляторы C часто служат back-end'ом для компиляторов C++ или имеют общий back-end.
2) Все, что реализуемо в С относительно модификации скалярных объектов между точками следования, реализуемо и в C++.

В частности:
1) Есть сильное подозрение, что Compaq C++ Compiler делает то же самое.
2) Как уже было сказано, gcc 3.0 делает то же самое.

D>С++ обладает сложной, часто скрытой семантикой


Ладно, заканчиваем с "философией". Пример, пожалуйста, в котором нужна предлагаемая "фича". Не "вообще", а в частности, т.е. фрагмент кода.

D>и говорю, что для С++ можно и нужно сделать поведение в этом случае не специфицированным


(Даже, если бы это было возможно) Зачем? Для поощрения написания ошибочного кода? Все равно такой код не становится более переносимым, а программа — более корректной или безопасной. Описание вариантов поведения в общем случае заняло бы кучу ресурсов и еще больше усложнило бы стандарт и жизнь разработчиков компиляторов без каких-либо практических выгод. Как ты предлагаешь описать варианты поведения программы? Значения объектов — все возможные комбинации битов в объектном представлении? Это никак не тянет на unspecified (см. также пример ниже).

D>С язык более низкого уровня и может быть менее безопасным.


1) Какие более низкоуровневые возможности есть в С, которых нет в C++?
2) Один из основных принципов проектирования C++: "Не оставлять места ни для каких языков более низкого уровня, кроме ассемблера".
3) Большинство компиляторов C++ одновременно являются и компиляторами C, при этом back-end у них общий. Очень сомневаюсь, что комитет пойдет на порождение подобных дорогостоящих для реализации различий.
4) Одними из главных задач ближайшей редакции стандарта является улучшение поддержки низкоуровневого/системного программирования в C++ и совместимости с C.

D>>>Теперь другой пример:

ПК>><... ++*p + ++*q ...>
ПК>>Ну, насчет других вариантов — легко
ПК>>i == 2 j == 3[/code]

D>Через пять минут, после того как я послал пример, понял, что возможен вариант «j == 3», но это ничего не меняет, все равно количество вариантов для этого примера сильно ограничено.


Если ты думаешь, что возможные варианты сводятся к i == [1, 2], j == [2, 3, 4], то я с легкостью нарисую тебе логичную последовательность инструкций, приводящую к другим результатам. Если ты предполагаешь другой диапазон — скажи, и я снова-таки предоставлю тебе логичную последовательность инструкций, не только приводящую к другому результату, но и влияющую на дальнейшее поведение программы, вплоть до нарушения семантики основных инструкций типа if. Учти, что следует рассматривать вычисления подобных выражений в контексте, где компилятору, например, может быть выгодно использовать результаты предыдущих вычислений и т.п.:

void foo(int* p, int* q)
{
  *p = ++*q;
  if (*p != *q)
    launch_rocket("SS-20");
}


Вполне возможна последовательность, в результате которой `(*p != *q) == true' если *p и *q указывают на один и тот же объект — типичный пример undefined behavior. В противном случае компилятор вынужден генерировать код, неоднократно загружающий значения *p и *q, хотя они и не являются volatile. Некоторые так и делают, но накладывать подобные ограничения на все компиляторы...

Описывать варианты в общем случае не представляется практически ценным, да и возможным. Собственно, это и есть главный аргумент, почему поведение undefined, а не unspecified. Существование SIMD (Single Instruction Multiple Data) архитектур, позволяющих параллельно выполнять операции с CREW (Concurrent Read Exclusive Write) памятью только усугубляет ситуацию.

ПК>>Кроме того, если отойти слегка в сторону, например, сообщалось, что SCO Optimizing C compiler на одной из платформ для:

ПК>>
ПК>>int a = 123, b = 7654;
ПК>>a ^= b ^= a ^= b;

ПК>>генерирует код, устанавливающий b в 123 и a в 0.

D>и опять дело только в значении.


Но значение уже не из множества предсказуемых вариантов, что совсем не тянет на unspecified.

ПК>>В принципе, в свое время в comp.lang.c++.moderated сообщалось, что есть устройства, имеющие (кросс-)компилятор C, для которых запись в одну и ту же ячейку не может осуществляться без задержки. В противном случае, происходит что-нибудь "страшное".


D>Вот я хотел бы увидеть такую систему


Мне тоже интересно, но лично не встречал. Насколько я понимаю, всякие контроллеры и т.п. вспомогательные железяки. Однако, мы с тобой не специалисты по embended системам и микроконтроллерам, а при написании стандарта таковые присутствовали. В любом случае, это не столь существенно, т.к. описывать варианты поведения даже без этих проблем не представляется нужным, а то и возможным.

D>>>Если говориться, что существуют процессоры где есть С++ и накладные расходы на неспецифицированное поведение настолько велики, что Стандарт не может их потребовать, то прошу приводить конкретные примеры иначе спор становиться бессмысленным.


Несмотря на то, что подобные архитектуры не являются главным аргументом, почему поведение undefined, они существуют. Еще раз: векторизирующие процессоры типа Cray и т.п., ILP процессоры типа Intel Itanium и т.п., всякие другие параллельные динозавры-суперкомпьютеры. Вся суть существования этих процессоров заключается как раз в том, чтобы инструкции выполнялись параллельно, а кроме того, "Intel Itanium architecture", например, "prohibits scheduling a ld.a and ld.c in the same cycle if both instructions have the same target register", что означает фактический запрет для компилятора использовать эту инструкцию в случае выражений с указателями в случае, если пытаться тем или иным образом уточнять поведение в рассматриваемом случае.

D>Вот я и хочу увидеть хоть одну систему, где действительно происходит undefined behavior, я слышу только слова о теоретический возможности. Реальные примеры есть?


Сколько угодно: undefined behavior сейчас происходит на всех системах. Undefined behavior это не "что-то страшное", а поведение, не определенное стандартом. Посмотри на другие источники undefined behavior:
— разыменование нулевого указателя (на подавляющем большинстве систем само по себе не приводит ни к каким проблемам до использования полученной ссылки);
— файл, не заканчивающийся символом новой строки (единственный из известных мне компиляторов, который хоть как-то на это прореагировал это Intel C++ Compiler);
— появление символов \ в именах заголовочных файлов (на DOS/Windows платформах этим даже пользуются стандартные API-библиотеки);
— целочисленная константа, не помещающаяся в long int;
— нестандартные escape-sequences;
— использование invalid pointer-value;
— использование типа из union не того, который был в нем сохранен (эта "фича" активно используется);
— использование неинициализированных данных (это уж и вовсе близко к обсуждаемой теме);
— переполнение знакового целого типа (что тоже вполне близко);
— операция взятие адреса объекта неполного типа, полный тип которого переопределяет operator &()... и т.д.
Имхо, множественная модификация скалярных объектов между соседними точками следования вполне неплохо вписывается в общую схему undefined behavior: те места, где стандарт по тем или иным причинам не вообще не ограничивает разработчиков компилятора или где предполагается возможность, что данные будут находиться в непредсказуемом состоянии.

Unspecified же относится к более ясным вещам, где есть определенный выбор вариантов:
— порядок вычисления подвыражений и аргументов (не ясен только порядок, данные запорчены быть не могут, и сами варианты предсказуемы);
— вызывается ли деструктор для type_info (какая кому разница?);
— вызывается ли функция распределения памяти до или после вычисления аргументов конструктора;
— некоторые сравнения указателей (результат неизвестен, но всегда true или false);
— нужно ли место для хранения ссылки (нам все равно, т.к. язык не позволяет узнать об этом)... и т.д.

Никто пока не посчитал нужным/не смог придумать простую схему описания вариантов в рассматривающемся случае. Если ты придумаешь такую, то появятся шансы (исчезающе маленькие) на изменение на unspecified, если, конечно, разработчики оптимизирующих компиляторов и приверженцы параллельных вычислений не будут слишком протестовать, а они будут, уж поверь

D>Что значит не опробованные фичи? Значения по умолчанию для параметров шаблонных функций (Item 226 Default template arguments for function templates), это как опробованная фича или нет, или, например, (Default arguments in template template-parameters 184).


Хотя это и достаточно тривиальное исправление непоследовательности, как и, скажем, template typedef, в наших же интересах, чтобы это не вошло в стандарт до экспериментальной проверки.

ПК>>Это не выбор: или — или. Должно быть: и — и.

D>Я не утверждаю, что такой выбор это хорошо, но реальная жизнь такова, что часто вопрос стоит именно так «или – или».

Вернемся на землю. Например, какая "реальная жизнь" применительно к процессу стандартизации? На мой взгляд, реальнее, чем existing practice уж и придумать что-нибудь тяжело.
<< J 1.0 alpha 4 >>
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[38]: Чудеса инкремента...
От: dupamid Россия  
Дата: 16.09.02 12:46
Оценка:
Здравствуйте Павел Кузнецов, Вы писали:

ПК>Если ты думаешь, что возможные варианты сводятся к i == [1, 2], j == [2, 3, 4], то я с легкостью нарисую тебе логичную последовательность инструкций, приводящую к другим результатам. Если ты предполагаешь другой диапазон — скажи, и я снова-таки предоставлю тебе логичную последовательность инструкций, не только приводящую к другому результату


Интересно как такой кусок код “++*p + ++*q” сам по себе, без дополнительных циклов вокруг, привел бы к значению, скажем 100. Как функция f будет приводить к другим значениям:
int f(int* p, int* q)
{
    return ++*p + ++*q;
}
[\ccode]

ПК> но и влияющую на дальнейшее поведение программы, вплоть до нарушения семантики основных инструкций типа if. Учти, что следует рассматривать вычисления подобных выражений в контексте, где компилятору, например, может быть выгодно использовать результаты предыдущих вычислений и т.п.:
ПК>[ccode]
ПК>void foo(int* p, int* q)
ПК>{
ПК>  *p = ++*q;
ПК>  if (*p != *q)
ПК>    launch_rocket("SS-20");
ПК>}


Точно то же самое можно сказать про не специфицированное поведение:
[ccode]
int i;

int f(int j)
{
return i = j;
}

void g(int, int);

void foo()
{
g(f(1), f(2));
if(i == 2) launch_rocket("SS-20");
}
[\ccode]
В зависимости от порядка вычисления аргументов будем запускать ракету – типичное не специфицированное поведение и ошибка в логике программы. Вообще, если этот кусок кода не из игрушки, то он проходит тестирование и сертификацию, скорее всего вместе с процессором, инструментальным компилятором и компилятором на котором собирался инструментальный компилятор и т.д.

D>>Вот я хотел бы увидеть такую систему


ПК>Мне тоже интересно, но лично не встречал. Насколько я понимаю, всякие контроллеры и т.п. вспомогательные железяки. Однако, мы с тобой не специалисты по embended системам и микроконтроллерам, а при написании стандарта таковые присутствовали. В любом случае, это не столь существенно, т.к. описывать варианты поведения даже без этих проблем не представляется нужным, а то и возможным.


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

ПК>Сколько угодно: undefined behavior сейчас происходит на всех системах. Undefined behavior это не "что-то страшное", а поведение, не определенное стандартом. Посмотри на другие источники undefined behavior:

ПК>Имхо, множественная модификация скалярных объектов между соседними точками следования вполне неплохо вписывается в общую схему undefined behavior: те места, где стандарт по тем или иным причинам не вообще не ограничивает разработчиков компилятора или где предполагается возможность, что данные будут находиться в непредсказуемом состоянии.
ПК>Unspecified же относится к более ясным вещам, где есть определенный выбор вариантов:

Примеры достаточно убедительны, но вот высказывание Jerry Schwarz как раз по подобному поводу:
«The value of this expression has been an issue for almost as long as there have been C compilers. My recollection is that Ritchie's original C compiler compiled this in "the obvious way" to give 5, but that Steve Johnson's pcc had code generating technology that always resulted in 6 (or maybe always resulted in 4, or maybe was platform specific) and was a common compiler at the time C was standardized. It was accepted at the time that the value was undefined (or maybe unspecified — in the early days we often didn't make that distinction).»
Это говорит только о том, что часть решений между undefined и unspecified просто сложилась исторически и веских оснований под собой не имеет, я не утверждаю что это тот самый случай, но кто знает...
Re[39]: Чудеса инкремента...
От: Павел Кузнецов  
Дата: 16.09.02 15:45
Оценка:
Здравствуйте dupamid, Вы писали:

D>Интересно как такой кусок код “++*p + ++*q” сам по себе, без дополнительных циклов вокруг, привел бы к значению, скажем 100. Как функция f будет приводить к другим значениям:

<... ф-я int f(int* p, int* q), содержащая только return ++*p + ++*q; ...>

Честно? Мне уже просто лень выписывать возможные последовательности инструкций. Важно другое: в общем случае может быть все, что угодно.

ПК>> но и влияющую на дальнейшее поведение программы, вплоть до нарушения семантики основных инструкций типа if. Учти, что следует рассматривать вычисления подобных выражений в контексте, где компилятору, например, может быть выгодно использовать результаты предыдущих вычислений и т.п.:

ПК>>
ПК>>void foo(int* p, int* q)
ПК>>{
ПК>>  *p = ++*q;
ПК>>  if (*p != *q)
ПК>>    launch_rocket("SS-20");
ПК>>}


D>Точно то же самое можно сказать про не специфицированное поведение:

<...>
D>В зависимости от порядка вычисления аргументов будем запускать ракету – типичное не специфицированное поведение и ошибка в логике программы.

Совсем не то же. Дело не в том, что будет запускаться ракета, а в том, что конструкция *p != *q "по идее" для p == q всегда должна возвращать false. Если она возвращает true, что это, как не проявление undefined behavior?

D>Примеры достаточно убедительны, но вот высказывание Jerry Schwarz как раз по подобному поводу:

D>«The value of this expression has been an issue for almost as long as there have been C compilers. My recollection is that Ritchie's original C compiler compiled this in "the obvious way" to give 5, but that Steve Johnson's pcc had code generating technology that always resulted in 6 (or maybe always resulted in 4, or maybe was platform specific) and was a common compiler at the time C was standardized. It was accepted at the time that the value was undefined (or maybe unspecified — in the early days we often didn't make that distinction).»
D>Это говорит только о том, что часть решений между undefined и unspecified просто сложилась исторически

Нет, это говорит о том, что в какое-то время не слишком задумывались над разницей между unspecified и undefined. Например, в книге K&R, если я правильно помню, этот случай помечен именно как unspecified. При стандартизации (на самом деле еще раньше) всем этим случаям было уделено пристальнейшее внимание. Но это вовсе не означает, что до стандартизации этот случай приводил "всего лишь" к unspecified behavior. Это примерно как говорить, что до Эйнштейна, Лоренца и компании проблем с Ньютоновской механикой не наблюдалось.
<< J 1.0 alpha 4 >>
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен