Pzz>Это сейчас про системные библиотеки речь или про самодельные? Pzz>У нас в системных библиотеках вроде нет такого, доп. проверки, которые включаются при сборке.
Конечно, и системные в том числе. У msvc полный набор дебажных рантаймовых библиотек поставляется вместе со студией. В gcc тоже всё похожим образом устроено. И в стандартной библиотеке, и в бусте, и в других библиотеках всё это используется. Хорошо известный трюк — итератор std::vector — это класс в дебажной конфигурации и обычный указатель в релизной. А внутренними ассертами там прямо насквозь всё везде нашпиговано.
Pzz>В своём коде я не вижу смысла выключать рантайм-проверки в зависимости от режима сборки.
Ну, проверки проверкам рознь.
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, rg45, Вы писали:
R>Конечно легален. Только ты походу не совсем понял, о чём мы вели речь. Началось всё с выражения: (flag ? sin : cos)(x), в котором тернарный оператор вычисляется в указатель на функцию. Потом мы плавно перешли к следующему: допустим у нас есть выражение, имеющее вид функционального вызова f(...). Вопрос: в каких случаях имя f преобразуется в указатель на функцию? Из std::min и std::max ты не получишь указатели на функции и выражение вида (flag ? std::min : std::max)(a, b) тоже не напишешь, хоть со скобками, хоть без, потому что это имена шаблонов, да ещё и перегруженных. Впрочем сишные min/max также не преобразуются в указатели на функции, т.к. это имена макросов. А вот сишные sin/cos преобразуются в т.ч. и в C++.
Не вижу препятствий:
using pT = decltype(y)(*)(decltype(y));
x = (flag ? (pT)std::sin : (pT)std::cos)(y);
using pF = const decltype(y)& (*)(const decltype(y)&,const decltype(y)&);
y = (flag ? (pF)std::min : (pF)std::max)(x, y);
хотя, конечно, столбовая дорога идёт в другом направлении...
Так как pF mm = std::min; компилируется без варнингов, то я сомневаюсь, что тут есть какие-то запреты.
Кстати, вместо указателей можно ссылки использовать:
Здравствуйте, B0FEE664, Вы писали:
BFE>Не вижу препятствий: BFE>
BFE> using pT = decltype(y)(*)(decltype(y));
BFE> x = (flag ? (pT)std::sin : (pT)std::cos)(y);
BFE> using pF = const decltype(y)& (*)(const decltype(y)&,const decltype(y)&);
BFE> y = (flag ? (pF)std::min : (pF)std::max)(x, y);
BFE>
BFE>хотя, конечно, столбовая дорога идёт в другом направлении...
Ну да, с шаблонами/перегрузками и явным преобразованием можно, конечно — по типу, как преобразуется к указателю на функцию шаблон std::endl. Но сейчас уже всё чаще в тренде функциональные объекты типа std::views::take, std::views::drop и множество других. Это все имена объектов, причём разных классов и тут уже даже явное преобразование к указателю/ссылке на функцию не проканает.
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, rg45, Вы писали:
Pzz>>Я подразумеваю под отладочной сборкой сборку с отладочной информацией, выключенной оптимизацйей и включенными проверками. А под релизной — без отладочной информации, с включенной оптимизацией и без проверок.
R>Всё верно, и я об этом же. В таких конфигурациях обычно генерируется дополнительный код облегчающий отладку. Например, память помечается специальными значениями при окончании времени жизни объектов, итераторы стандартных контейнеров снабжаются дополнительными средствами отслеживания валидности, бросаются исключения при обнаружении проблем наподобие тех, что были перечислены выше и т.п. Ну и assert-ы также выбрасывают исключения в случае нарушения ожиданий.
Он же сишечник, у него только разметка памяти deadbeaf'ами и может быть, остальное ему недоступно
Здравствуйте, Pzz, Вы писали:
Pzz>При этом в gcc санитайзер вроде еще не завезли.
поддержка ASAN появилась в GCC 4.8, в марте 2013.
MSan — да, clang-only.
Pzz> а clang-овский требует, чтобы все библиотеки были с ним пересобраны. Такое себе, полсистемы пересобирать и непонятно куда выкладывать, чтобы с предустановленными библиотеками не поссориться.
MSan — требует, ASAN — нет.
При этом MSan имеет бОльшие накладные расходы и делает меньше диагностик.
Если я не ошибаюсь, единственное, что он делает, недоступное средствами ASAN — это использование неициализированной памяти.
Здравствуйте, rg45, Вы писали:
R>Конечно легален. Только ты походу не совсем понял, о чём мы вели речь. Началось всё с выражения: (flag ? sin : cos)(x), в котором тернарный оператор вычисляется в указатель на функцию. Потом мы плавно перешли к следующему: допустим у нас есть выражение, имеющее вид функционального вызова f(...). Вопрос: в каких случаях имя f преобразуется в указатель на функцию?
Похоже я нашел тот источник, из которого у меня сложилось впечатление, что брать указатели на функции из стандартной библиотеки C++ небезопасно:
Здравствуйте, Marty, Вы писали:
A>>Мой субъективный опыт подсказывает, что: A>>1) Такие ошибки встречаются раз в жизни. Ну вот в моей жизни не разу не встретилась.
M>Крайне ограниченный опыт
Я думаю, что это у тебя крайне ограниченный опыт. А я из программистов советского поколения.
Мы на вступительных экзаменах сочинение писали. И надо было на четырёх страницах допустить не более 4-х ошибок.
То есть не более одной ошибки на страницу. И тогда это было нормально. Это не вызывало усилий и получалось само.
Без этого человек считался неграмотным, и его в ВУЗ просто не брали. Ну потому что учить дурака бесполезно.
Современные люди с высшим образованием такого даже представить себе такого не могут.
A>>2) Варнинги выдаются не когда "что-то подозрительное происходит", а когда ничего не происходит (переменная не меняется), A>> но компилятор исходя из его чувства прекрасного считает, что нужно поставить const.
M>Что за чудо? Никогда такого не видел. Покажешь?
FILE * my_fopen( char *filename, char *flags )
{
return fopen( filename,flags );
}
FILE *fr = my_fopen( "my_file", "r" ); // <--- вот тут будут два совершенно бессмысленных варнинга
Здравствуйте, alpha21264, Вы писали:
A>Я думаю, что это у тебя крайне ограниченный опыт. А я из программистов советского поколения. A>Мы на вступительных экзаменах сочинение писали. И надо было на четырёх страницах допустить не более 4-х ошибок.
Вы, блин, еще скажите, что один здесь такой.
A>
A>FILE * my_fopen( char *filename, char *flags )
A>{
A> return fopen( filename,flags );
A>}
A>FILE *fr = my_fopen( "my_file", "r" ); // <--- вот тут будут два совершенно бессмысленных варнинга
A>
Варнинги как раз более чем осмысленные и недвусмысленно намекающие на вашу привычку забивать болт на типизацию.
Кстати, вы хотя бы в прототип fopen заглядывали? Вас не смущает, что там оба параметра const char*?
A>FILE * my_fopen( char *filename, char *flags )
A>{
A> return fopen( filename,flags );
A>}
A>FILE *fr = my_fopen( "my_file", "r" ); // <--- вот тут будут два совершенно бессмысленных варнинга
A>
А RAII — это тоже зло, придуманное "миссионерами"?
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, rg45, Вы писали:
R>Повторяю ещё раз свой ответ, неопределённое поведение вот здесь:
R>
R>8.7.3 The return statement
R>2 . . . Otherwise, flowing off the end of a function other than main or a coroutine ([dcl.fct.def.coroutine]) results in undefined behavior.
ну так и такой код
char buffer[2] = {0x01, 0x02};
short value = *(reinterpret_cast<short*>(buffer));
строго говоря UB. Но никто же не отправляет программу в нокаут из-за этого. Это просто сломает невообразимую массу кода.
Так что нельзя говорить, что при UB компилятор волен делать что угодно. Нифига он не волен Он может делать оптимизации — это да. А вот так просто класть программу — это беспредел.
Здравствуйте, sergii.p, Вы писали:
SP>ну так и такой код
SP>
SP>char buffer[2] = {0x01, 0x02};
SP>short value = *(reinterpret_cast<short*>(buffer));
SP>
SP>строго говоря UB. Но никто же не отправляет программу в нокаут из-за этого. Это просто сломает невообразимую массу кода. SP>Так что нельзя говорить, что при UB компилятор волен делать что угодно. Нифига он не волен Он может делать оптимизации — это да. А вот так просто класть программу — это беспредел.
Я уже давал в соседнем обсуждении примечательную ссылку: https://godbolt.org/g/o4HxtU
Этому примеру уже лет 8 или 9. В свое время он настоящий фурор произвел. Как наглядный пример того, что могут делать компиляторы.
Просто компиляторостроители пока еще сдерживаются совместимостью и отсутствием в старых стандартах некоторых новых механизмов (std::launder, std::bit_cast, std::start_lifetime_as). Чем дальше, тем меньше они будут сдерживаться. Типа того, что вам дали в руки std::bit_cast, вот и переписывайте свой говнокод, иначе наткнетесь на то, что компилятор очередное UB стал эксплуатировать.
Далеко не всем такое развитие событий нравится, но и остановить компиляторщиков вряд ли возможно.
Здравствуйте, sergii.p, Вы писали:
SP>Здравствуйте, rg45, Вы писали:
R>>Повторяю ещё раз свой ответ, неопределённое поведение вот здесь:
R>>
R>>8.7.3 The return statement
R>>2 . . . Otherwise, flowing off the end of a function other than main or a coroutine ([dcl.fct.def.coroutine]) results in undefined behavior.
SP>ну так и такой код
SP>
SP>char buffer[2] = {0x01, 0x02};
SP>short value = *(reinterpret_cast<short*>(buffer));
SP>
SP>строго говоря UB. Но никто же не отправляет программу в нокаут из-за этого. Это просто сломает невообразимую массу кода. SP>Так что нельзя говорить, что при UB компилятор волен делать что угодно. Нифига он не волен Он может делать оптимизации — это да. А вот так просто класть программу — это беспредел.
У UB нет степеней и градаций — оно либо есть, либо нет. Так же, как нельзя быть чуть-чуть беременной.
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, rg45, Вы писали:
R>А ты уверен, что это UB? Подтвердить ссылкой можешь?
An object of dynamic type Tobj is type-accessible through a glvalue of type Tref if Tref is similar ([conv.qual]) to:
Tobj
a type that is the signed or unsigned type corresponding to Tobj
a char, unsigned char, or std::byte type.
If a program attempts to access ([defns.access]) the stored value of an object through a glvalue through which it is not type-accessible, the behavior is undefined
Здравствуйте, alpha21264, Вы писали:
M>>Крайне ограниченный опыт
A>Я думаю, что это у тебя крайне ограниченный опыт. А я из программистов советского поколения. A>Мы на вступительных экзаменах сочинение писали. И надо было на четырёх страницах допустить не более 4-х ошибок. A>То есть не более одной ошибки на страницу. И тогда это было нормально. Это не вызывало усилий и получалось само. A>Без этого человек считался неграмотным, и его в ВУЗ просто не брали. Ну потому что учить дурака бесполезно. A>Современные люди с высшим образованием такого даже представить себе такого не могут.
Какой у тебя огромный...
M>>Что за чудо? Никогда такого не видел. Покажешь?
A>
A>FILE * my_fopen( char *filename, char *flags )
A>{
A> return fopen( filename,flags );
A>}
A>FILE *fr = my_fopen( "my_file", "r" ); // <--- вот тут будут два совершенно бессмысленных варнинга
A>
Тут очень даже осмысленные варнинги. Внутри твоего my_fopen может происходить запись по переданном указателям, а ты передаёшь литералы. Эти литералы лежат в сегменте инициализированных данных, и туда писать нельзя. Вот тебе компилятор и говорит, что ты хрень творишь
SP>An object of dynamic type Tobj is type-accessible through a glvalue of type Tref if Tref is similar ([conv.qual]) to:
SP>
SP>Tobj
SP>a type that is the signed or unsigned type corresponding to Tobj
SP>a char, unsigned char, or std::byte type.
SP>
SP>If a program attempts to access ([defns.access]) the stored value of an object through a glvalue through which it is not type-accessible, the behavior is undefined
Здравствуйте, so5team, Вы писали:
S>Насколько я понимаю, после добавления в язык implicit lifetimes ситуация стала менее однозначной, т.к. вот такой код уже, вроде бы, и не UB вовсе: S>
Здравствуйте, sergii.p, Вы писали:
SP>Формально UB. Но если offset % alignof(int) == 0 то вполне корректное поведение, но гарантировать это в compile time невозможно.
Я так понимаю, компилятор тут не будет ничего изобретать, и будет делать как сказано. А UB в стандарт прописано потому, что некоторые платформы (особенно это было популярно в древние времена, и вроде, когда-то, даже на ARM-ах так было) будут бросать исключения при доступе по не выровненному адресу. Сейчас же вроде почти все научились нормально работать с не выровненными адресами (ну, может, это чуть дольше).
Я, когда был молодым, и не знал про все эти UB, часто так делал на x86, и всё работало. Сейчас иногда и на армах МКшных так делаю (от лени), и тоже работает.