Pull to refresh

Comments 53

Си++, созданный в режиме совместимости с С, состоит из необъятного набора идиотских дефолтов и предположений. Компилятор — это как миноискатель. Не надо игнорировать его предупреждения, когда идёшь по минному полю.


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

Минное поле, небезопасно, переходите на язык Абырвалг.
Типично.
Неинтересно.

Опять эти сказки. Если бы в нем «безопасность кода была обязательной», то в нем не было бы директивы unsafe (ну это не говоря уж о том, что и в нем UB вполне себе достижим даже в safe коде). Но без применения unsafe (как минимум на уровне отдельных модулей, а также стандартной библиотеки) ничего путного там не написать. Утверждения про «обязательную безопасность» кода в расте всегда напоминают мне берлинских экоактивистов, закрывавших паллетами от любопытных глаз дизельный генератор в своем лагере.

Даже в быту вы можете себе сделать очень плохо. Можно, например, развести костёр в середине комнаты и пойти спать.


Разница между "развести костёр в середине комнаты" и "случайно наступить на мину" определяет разницу между мирным и (после)военным временем.


Лямбды с замыканием, которое вызывает use after free — это один из примеров, когда С++ пытается на С натянуть шкуру приличного языка, но получается даже хуже, чем было до.

Я бы, скорее, сравнил C++ с газопроводом или газовым котлом. Полезная вещь в быту? Безусловно. Что будет, если неподготовленный человек туда полезет? Скорее всего — взрыв, или, как нынче принято выражаться, «хлопок газа». А раст я бы сравнил с электричеством — несколько более безопасно на бытовом уровне (хотя регулярно и случаются пожары из-за коротких замыканий), но в высоковольтный трансформатор без допуска все равно лучше не лезть.

Эта аналогия обладает одним свойством — "безопасность" при этом выглядит производной от физических (неизменных) свойств. Газы горючи и ничего с этим не сделать.


А в реальности же — Си++ писали люди. Ладно, Си, его писали во времена, когда struct был слаще мёда для людей из ассемблера. Но Си++ писался уже по мотивам боли индустрии, и в этот момент было принято множество нетривиальных решений о дефолтах компилятора, дающих unsound код (выглядит прилично, а soundness нет).


Если бы в С++ были другие дефолты поведения — язык бы от этого медленнее не стал, а безопаснее — стал бы.

Ну что поделать, таковы были трейдоффы за возможность без модификации и написания разнообразных оберток использовать уже существовавшую огромную массу кода, написанного на C. Я бы сказал, что это решение себя оправдало. Оправдает ли в случае раста решение разработать очередной язык «с нуля» — будем поглядеть, пока это не очевидно. Вполне возможно, что он так и останется нишевым, как Ada, Oberon или D.
таковы были трейдоффы за возможность без модификации и написания разнообразных оберток использовать уже существовавшую огромную массу кода, написанного на C.

О, я обожаю этот вопрос. Действительно вызов Си кода из С++ кода не выглядит очень сложным или страшным, но там все равно целая бездна всяких нюансов ) Обратное — это вообще сплошная боль, чего только стоит extern C и всякий манглинг

Ну, это все равно попроще, чем, скажем, тот же JNI на мой взгляд.

и в нем UB вполне себе достижим даже в safe коде

А можно про это поподробнее?

Почитайте про флаг -Zsaturating-float-casts и про историю его появления.

Так они же это исправили?

Ну, sort of — через добавление костыля в виде опционального флага. А опциональный он потому, что в случае его использования производительность в ряде сценариев работы с числами с плавающей точкой заруливается в минуса. Думайте сами, считать это «исправлением» или нет.
Ну, sort of — через добавление костыля в виде опционального флага

Э… ну насколько я знаком с разработкой rustc это не так. Флаги начинающиеся -Z это так называемые фича флаги, они работают только в альфа/ночном релизе и служат для тестирования новых идей. В данном случае насколько исправление замедляет код тестеров. При запуске со стабильной версией компилятор просто скажет или выругается или просто проигнорирует '-Z" опции.


в ряде сценариев работы с числами с плавающей точкой заруливается в минуса

Увидел только один вариант который замедлился i128 -> f32, но и его исправили:
https://github.com/rust-lang/rust/pull/67328

Насколько я вижу, проблема существует до сих пор, даже на nightly:

play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=5c295dd26d187d13c059125bb604c40a

В целочисленных переменных после конверсии по-прежнему произвольный мусор, к тому же разный в debug и release.

P.S. Ссылка про i128 -> f32 не имеет к этой проблеме никакого отношения. Вот ссылка на соответствующий issue:

github.com/rust-lang/rust/issues/10184

Как видите, она до сих пор open (уже больше 6 лет), и в ней написано про negative performance impact от saturating float casts. Судя по всему, решения без performance impact до сих пор нет.
UFO just landed and posted this here
Ну если «безопасность языка» является единственным значимым фактором, то безусловно :) Пусть берут.
UFO just landed and posted this here
-Wall -Wextra конечно обязательны. Полезно было прочесть про другие флаги и рекомендации.

Уточнение:
-Woverloaded-virtual
Предупреждает о попытке в классе-наследнике переопределить виртуальную функцию базового класса
Переопределение виртуальной функции базового класса в классе-наследнике как раз нормально — она для этого, собственно, и предназначена, а вот перегрузка — это да, скорее всего ошибка.

Спасибо, поправлю. Речь, конечно же, о ситуации, когда новое определение функции в наследнике скрывает виртуальную функцию базового класса.

UFO just landed and posted this here
И на такое
int a = 0;
if (cond)
   a = 2;
else
   a = 1;
получаем предупреждение: что-то на тему «присваивание значения, которое не используется».
UFO just landed and posted this here

В реальном коде бывает и не такое.

UFO just landed and posted this here
UFO just landed and posted this here

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


if(нажата кнопка) a=1;
else a=2;

Каким значением инициализировать переменную a, нулем? Я вот когда вижу проинициализированную переменную сразу начинаю беспокоится: что её текущее значение означает для системы? Возвращаясь к примеру, может же так быть, что 0 — кодирует факт двойного нажатия на кнопку? А в случае двойного нажатия система выключается… Это ведь совсем не то, что я хотел.
А когда переменная не инициализирована — я спокоен, что инициализирую её ниже корректным значением в зависимости от условий.

int keyKode=KEY_NONE;
if(нажата кнопка) keyKode=KEY_ONE;
Хороший язык C++. Так много хороших способов, после того как отстрелишь себе ногу, исправить ситуацию, пришить ногу обратно, пересадить другую ногу, сделать протез, протез всегда можно взять чуть подлиньше, чуть по короче.
UFO just landed and posted this here

Ещё есть любопытный ключик -Weffc++, который ссылается на книжку "Эффективный и современный С++". Его правила тоже было бы не плохо хотя бы перечислить.

Офигенный ключ, но, к сожалению, в нём есть неотключаемое правило, которое вообще не даёт создать невиртуальный деструктор. А это слишком сильное требование.
Но ради интереса можно временно включать, да.

Разве нельзя данное конкретное предупреждение подавить через pragma disable?

Я такого способа не знаю. Если знаете, покажите, пожалуйста, буду благодарен.

Можно заигнорить как "-W...", так и определённые файлы/строки. А вот валится ли правило про невиртуальный деструктор в общую кучу -Weffc++ или у него есть собственный ключ, чтобы не мучаться с исключением по строкам — не знаю. Вообще этот ключ не советуют использовать из-за размытости, а с -Werror с ним можно застрелиться.

Это всё понятно. Мы же говорим о том, как исключить из -Weffc++ одну конкретную диагностику. Отдельного ключа у неё, естественно, нет.

Тогда в тексте программы, где ругается на виртуальный деструктор


  //Вот тут -Weffc++ ещё работает   
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
  //Вот тут -Weffc++ не сработает                      
#pragma GCC diagnostic pop
  //Вот тут -Weffc++ уже работает   

Так можно, да. Но многовато будет прагм.

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

И удивительно, что очень малая их часть включена по умолчанию

А ничего удивительного — сначала находятся уникумы, компилирующие с «treat warnings as errors», потом выходит новая версия с новыми предупреждениями, потом багтрекер компилятора заваливают репортами «ааааа вы сломали нам билд», потом разработчики компилятора решают, что ну его нафиг и делают новые предупреждения выключенными по умолчанию.
Коротче, те же яйца что и с ABI, только в профиль.

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

UFO just landed and posted this here

Прошу помощи у сообщества!
Можно ли защититься от случая, когда в одном файле
char arr[1];
а в другом extern char arr[999];
? Компилятор же должен видеть, что arr это и есть arr, почему его не смущает разный их размер?

А С++17 можно делать inline вместо extern, и объявлять таким образом один раз только в хедере. Так же в С++ имеет смысл использовать std::array.

А вообще компилятор ругается когда extern определение не совпадает с обычным. Видимо вы не везде хедер заинклюдили.

По поводу -Woverloaded-virtual. По совету этой статьи решил попробовать поиспользовать этот флаг. И внезапно столкнулся с проблемой в большом проекте, использующего boost.

Возьмем вот такой код. Как мы видим, он компилируется без варнов. А теперь создадим идентичный код (прототипы методов срисованы с бустовых один-в-один). И внезапно, компилятор начинает говорить о shadow параметра. Что это? Баг компилятора GCC? Потому что Clang, например, не дает варна в таком случае.

Sign up to leave a comment.

Articles