Везде наличие компилятора/интерпретатора языка даёт гарантию подключения зависимостей.
Еще раз - вот я хочу попробовать ваше приложение на винде. Вы считаете нормальным, что я для того, чтобы его попробовать, должен скачивать себе гошный тулчейн и сидеть собирать что-то там из исходников со всеми зависимостями (у которых могут быть свои тулчейны)? Или я все же должен иметь возможность скачать готовый бинарный пакет и установить его, как это делается обычно в винде, макоси и на большинстве линупсов (за исключением генту)? А если речь идет о бинарном пакете, то причем тут язык, и чем мне тут поможет установка гошного тулчейна (который нужен мне как собаке пятая нога)?
Честно говоря, я не очень понял претензии к языку. Установка неподписанных/ненотаризованных бинарей - это в любом случае проблемное дело в той же винде или в macOS вне зависимости от того, на каком языке написано то, из чего эти бинари собраны. А если вы хотите просто показать потенциальному пользователю свое приложение в режиме "скачать и запустить", то требовать, чтобы этот потенциальный пользователь имел тулчейн для его сборки (а скорее всего еще и тулчейны для сборки всех его зависимостей, ведь они могут быть написаны на разных языках), каким бы распрекрасным и удобным этот тулчейн ни был - ну, такое.
Вот мне кажется, что текущие компиляторы для х86/х64 кинут литерал в сегмент данных и ссылка никогда не пропадёт. Но это стандартом для всех систем и оптимизаторов! не гарантируется.
Вообще-то гарантируется: строковые литералы гарантированно имеют static storage duration:
Evaluating a string literal results in a string literal object with static storage duration.
Ну то есть в своих широковещательных заявлениях основываетесь не на какой-то статистике, а на собственных каких-то представлениях "из головы" и собственном опыте. Тогда стоит добавлять к своим заявлениям "ИМХО", а не преподносить их как факт. Не ведите себя как растофанатик, ведите себя как инженер.
какое-то C++ сектантство в духе конторы на букву "я", вынуждающее вас оставлять агрессивные комментарии и прибегать к демагогическим приемам
Здорово, здорово. Нечем подкрепить свои утверждения - обвини собеседника в сектантстве и демагогических приемах. С вами я на обозримое будущее разговор закончил.
Вас не смущает, что почти все программисты на Rust это и есть бывшие программисты на C++?
Я вот заметил, что вы очень любите делать широковещательные и ничем не подкрепленные заявления. Откуда дровишки что "почти все программисты на Rust это и есть бывшие программисты на C++"? Где можно ознакомиться с соответствующей статистикой?
а почему не foo(y >= 0 ? (unsigned)y : 0) (второй 0 можно заменить на логгирование/исключение/::exit(-146)/whatever)
Ну вообще это далеко не всегда нужно, особенно если ситуация когда y < 0 вполне штатная - просто в этом случае вызывать foo() не нужно вообще. Лично я предпочитаю использовать контекстозависимые линтеры типа сонара, в которых предупреждение можно пометить как "accepted", но при изменении контекста предупреждение будет расценено как "новое" и будет снова отображено. А явные касты тут только мешают.
Проверка не является одним целым с вызовом и при очередном рефакторинге может быть (ошибочно) стёрта.
Конечно! Но вся штука в том, что точно так же она может быть ошибочно стерта и при наличии явного приведения foo((unsigned)y), и компилятор тут вам ничем не поможет.
С точи зрения простого компилятора эта проверка ничего не добавляет.
Это уже другой вопрос. Для надежного отлавливания ошибок конверсии между знаковыми и беззнаковыми типами одного требования явного приведения мало - прописывание этого приведения само по себе ничего не дает, ошибка остается. Если в приведенном мной куске кода завтра один джун пропишет foo((unsigned)y), чтобы заткнуть компилятор, а послезавтра другой джун выкинет if по ошибке, то компилятор ничего не заметит (как и в расте, кстати).
Безопасности тут добавляет проверка if(y < 0), а не явное приведение y к unsigned. Если есть проверка, то явное приведение не добавляет никакой "безопасности". Бездумное приведение же без проверки просто заткнет компилятор, а ошибка останется - как и в расте, кстати.
А тут не на что ругаться, абсолютно ничего "небезопасного" в приведенном мной коде нет. Прописывание явного приведения никакой "безопасности" тут не добавляет.
Ну вообще зависимости обычно собирают отдельно с их родными ключами с использованием одного из пакетных менеджеров (я лично предпочитаю vcpkg), а собственный код - отдельно.
Ну вообще-то флаги, заставляющие компилятор делать соответствующие проверки, существуют - -Wconversion, -Wsign-conversion, и так далее, просто они не включены по умолчанию. А не включены они по умолчанию потому, что существует дофига абсолютно валидного и беспроблемного кода типа такого:
void foo(unsigned x) { ... }
void bar()
{
int y = baz();
if (y < 0) return;
foo(y);
}
Ошибки при работе с памятью тут всё равно возможны
Более того - они совершенно реальны (специально привожу пример такой ошибки в одной из coreutils, переписанной на раст "ради безопасности"). Забавно здесь то, что если бы автор писал на C, то этой ошибке было бы неоткуда взяться, он просто автоматически использовал бы "правильную" структуру из /usr/include/pwd.h. В расте же нужны специальные телодвижения, которые автор, по-видимому, не осилил, и решил эту структуру просто захардкодить.
Главный прикол этой статьи в том, что никто, включая автора, по-видимому даже не пытался запустить эти примеры. Все эти compile-time парсеры CSV, JSON и INI не компилируются, так как в них используются конструкции, недоступные в const fn.
Перевод кода, написанного в лучшем стиле C++98 на C++11 (до состояния без предупреждений компилятора) - это вообще задача сродни глубокому рефакторингу.
Неоднократно приходилось заниматься подобными вещами, в том числе в некоторых опенсорсных проектах типа того же fheroes2 (который писался старой командой еще в нулевые - до C++11, разумеется). Да, какие-то предупреждения компилятора бывали, особенно если до меня они были включены по минимуму, не без этого, но в целом проект как правило собирался и работал на новом стандарте без всякого "глубокого рефакторинга", а новый код (с конструкциями из новых стандартов) всегда можно было встраивать прямо "посреди старого" без особых проблем.
Одно дело - раздувается компилятор. Это где-то там кого-то там проблема.
Ошибаетесь. Как только вам нужно будет исправить ошибку в старом коде или добавить какой-то функционал, это тут же станет вашей проблемой. Причем эта проблема усугубляется еще и тем, что в отличие от языков, развивающихся с поддержкой обратной совместимости, вы не сможете реализовать этот функционал в старом коде с помощью новых подходов и конструкций языка (и просто указать компилятору ключ -std поновее), а только с помощью старых, потому что старая ревизия еще не поддерживает новые конструкции, а новая ревизия уже не поддерживает старые.
Теперь, по логике того объяснения, которое было в оригинальном примере, здесь UB нет, верно?
По идее, да. Смотрим:
every byte that would be reachable through the result is reachable through p (bytes are reachable through a pointer that points to an object y if those bytes are within the storage of an object z that is pointer-interconvertible with y, or within the immediately enclosing array of which z is an element).
p в данном случае указывает на объект типа int, который находится внутри immediately enclosing array типа int[10] (то есть все элементы этого массива доступны через адресную арифметику), который в свою очередь занимает все пространство x2, т.е. любой байт внутри x2 достижим через этот p, так же как он достижим и через int(*)[10].
В этом смысле объектов типа "массив" не существует. Косвенно это можно обнаружить по тому, что квалификаторы const/volatile к самому массиву не применимы, только к типам самих элементов.
Ну, это натянутое утверждение. Для типов "массив" любой вложенности можно создавать алиасы через тот же using, существует тип "указатель на массив", decltype тоже прекрасно в курсе, что там за массив в конкретном случае и какие размерности он имеет. У компилятора нет никаких проблем отличить один массив от другого, коль скоро у него есть доступ к соответствующей декларации.
Но они не говорят, как всё устроено, и что за всеми этими штуками стои́т.
Вы слишком много внимания уделяете конкретной гипотетической реализации. Я же воспринимаю стандарты как описание некоей виртуальной машины, в котором написано, что эта виртуальная машина гарантированно может/должна прожевать (с тем или иным результатом), и на что не дается никаких гарантий.
Если снять одно измерение и повторить рассуждения, то получается следующее:
Ну вообще-то не получается, ибо в данном случае, имея массив типа int[2]и указатель на первый элемент этого массива, есть легальный способ получить доступ к любому из его элементов. А с многомерными массивами (или, если угодно, массивами массивов), имея лишь указатель на первый элемент первого массива, нет легального способа получить доступ к элементам остальных массивов (требование "every byte that would be reachable through the result is reachable through p" не выполняется).
Еще раз - вот я хочу попробовать ваше приложение на винде. Вы считаете нормальным, что я для того, чтобы его попробовать, должен скачивать себе гошный тулчейн и сидеть собирать что-то там из исходников со всеми зависимостями (у которых могут быть свои тулчейны)? Или я все же должен иметь возможность скачать готовый бинарный пакет и установить его, как это делается обычно в винде, макоси и на большинстве линупсов (за исключением генту)? А если речь идет о бинарном пакете, то причем тут язык, и чем мне тут поможет установка гошного тулчейна (который нужен мне как собаке пятая нога)?
Честно говоря, я не очень понял претензии к языку. Установка неподписанных/ненотаризованных бинарей - это в любом случае проблемное дело в той же винде или в macOS вне зависимости от того, на каком языке написано то, из чего эти бинари собраны. А если вы хотите просто показать потенциальному пользователю свое приложение в режиме "скачать и запустить", то требовать, чтобы этот потенциальный пользователь имел тулчейн для его сборки (а скорее всего еще и тулчейны для сборки всех его зависимостей, ведь они могут быть написаны на разных языках), каким бы распрекрасным и удобным этот тулчейн ни был - ну, такое.
Вообще-то гарантируется: строковые литералы гарантированно имеют static storage duration:
Точно не могу сказать :)
Ну то есть в своих широковещательных заявлениях основываетесь не на какой-то статистике, а на собственных каких-то представлениях "из головы" и собственном опыте. Тогда стоит добавлять к своим заявлениям "ИМХО", а не преподносить их как факт. Не ведите себя как растофанатик, ведите себя как инженер.
Здорово, здорово. Нечем подкрепить свои утверждения - обвини собеседника в сектантстве и демагогических приемах. С вами я на обозримое будущее разговор закончил.
Я вот заметил, что вы очень любите делать широковещательные и ничем не подкрепленные заявления. Откуда дровишки что "почти все программисты на Rust это и есть бывшие программисты на C++"? Где можно ознакомиться с соответствующей статистикой?
Есть механизмы контекстозависимого отключения предупреждений, например.
Ну вообще это далеко не всегда нужно, особенно если ситуация когда
y < 0
вполне штатная - просто в этом случае вызыватьfoo()
не нужно вообще. Лично я предпочитаю использовать контекстозависимые линтеры типа сонара, в которых предупреждение можно пометить как "accepted", но при изменении контекста предупреждение будет расценено как "новое" и будет снова отображено. А явные касты тут только мешают.Конечно! Но вся штука в том, что точно так же она может быть ошибочно стерта и при наличии явного приведения
foo((unsigned)y)
, и компилятор тут вам ничем не поможет.Это уже другой вопрос. Для надежного отлавливания ошибок конверсии между знаковыми и беззнаковыми типами одного требования явного приведения мало - прописывание этого приведения само по себе ничего не дает, ошибка остается. Если в приведенном мной куске кода завтра один джун пропишет
foo((unsigned)y)
, чтобы заткнуть компилятор, а послезавтра другой джун выкинетif
по ошибке, то компилятор ничего не заметит (как и в расте, кстати).Безопасности тут добавляет проверка
if
(y < 0)
, а не явное приведениеy
кunsigned
. Если есть проверка, то явное приведение не добавляет никакой "безопасности". Бездумное приведение же без проверки просто заткнет компилятор, а ошибка останется - как и в расте, кстати.А тут не на что ругаться, абсолютно ничего "небезопасного" в приведенном мной коде нет. Прописывание явного приведения никакой "безопасности" тут не добавляет.
Ну вообще зависимости обычно собирают отдельно с их родными ключами с использованием одного из пакетных менеджеров (я лично предпочитаю vcpkg), а собственный код - отдельно.
Ну вообще-то флаги, заставляющие компилятор делать соответствующие проверки, существуют -
-Wconversion
,-Wsign-conversion
, и так далее, просто они не включены по умолчанию. А не включены они по умолчанию потому, что существует дофига абсолютно валидного и беспроблемного кода типа такого:Те, кому надо, могут включить.
Более того - они совершенно реальны (специально привожу пример такой ошибки в одной из coreutils, переписанной на раст "ради безопасности"). Забавно здесь то, что если бы автор писал на C, то этой ошибке было бы неоткуда взяться, он просто автоматически использовал бы "правильную" структуру из
/usr/include/pwd.h
. В расте же нужны специальные телодвижения, которые автор, по-видимому, не осилил, и решил эту структуру просто захардкодить.Главный прикол этой статьи в том, что никто, включая автора, по-видимому даже не пытался запустить эти примеры. Все эти compile-time парсеры CSV, JSON и INI не компилируются, так как в них используются конструкции, недоступные в const fn.
Неоднократно приходилось заниматься подобными вещами, в том числе в некоторых опенсорсных проектах типа того же fheroes2 (который писался старой командой еще в нулевые - до C++11, разумеется). Да, какие-то предупреждения компилятора бывали, особенно если до меня они были включены по минимуму, не без этого, но в целом проект как правило собирался и работал на новом стандарте без всякого "глубокого рефакторинга", а новый код (с конструкциями из новых стандартов) всегда можно было встраивать прямо "посреди старого" без особых проблем.
Ошибаетесь. Как только вам нужно будет исправить ошибку в старом коде или добавить какой-то функционал, это тут же станет вашей проблемой. Причем эта проблема усугубляется еще и тем, что в отличие от языков, развивающихся с поддержкой обратной совместимости, вы не сможете реализовать этот функционал в старом коде с помощью новых подходов и конструкций языка (и просто указать компилятору ключ
-std
поновее), а только с помощью старых, потому что старая ревизия еще не поддерживает новые конструкции, а новая ревизия уже не поддерживает старые.По идее, да. Смотрим:
p
в данном случае указывает на объект типаint
, который находится внутри immediately enclosing array типаint[10]
(то есть все элементы этого массива доступны через адресную арифметику), который в свою очередь занимает все пространствоx2
, т.е. любой байт внутриx2
достижим через этотp
, так же как он достижим и черезint
(*)[10]
.Ну, это натянутое утверждение. Для типов "массив" любой вложенности можно создавать алиасы через тот же
using
, существует тип "указатель на массив",decltype
тоже прекрасно в курсе, что там за массив в конкретном случае и какие размерности он имеет. У компилятора нет никаких проблем отличить один массив от другого, коль скоро у него есть доступ к соответствующей декларации.Вы слишком много внимания уделяете конкретной гипотетической реализации. Я же воспринимаю стандарты как описание некоей виртуальной машины, в котором написано, что эта виртуальная машина гарантированно может/должна прожевать (с тем или иным результатом), и на что не дается никаких гарантий.
Ну вообще-то не получается, ибо в данном случае, имея массив типа
int[2]
и указатель на первый элемент этого массива, есть легальный способ получить доступ к любому из его элементов. А с многомерными массивами (или, если угодно, массивами массивов), имея лишь указатель на первый элемент первого массива, нет легального способа получить доступ к элементам остальных массивов (требование "every byte that would be reachable through the result is reachable through p" не выполняется).