Comments 65
это язык, иерархия изменений в котором прослеживается вплоть до 1973 года, когда он был изобретён.
Я как самый объективный и не ангажированный программист из всей статьи, прикопаюсь конечно же к дате:)
С++ начинает отсчёт с 1983. Точнее Бьярни начал в это время, франкештеить экспериментировать с С, внедряя разные штуки. Что в итоге породило самый лучший язык программирования Pascal С++.
Около 1974 года Деннис Ритчи закончил работу над языком, который был следующим этапом эволюции после B и назвал его C.
История C++ начинается в 1979 году, в рамках докторской диссертации Страуструп решил создать инструмент, который был бы производительным, но при этом обеспечивал абстракцию. Его первой разработкой стал C with Classes.
В 1982 году он решил доработать C with Classes, чтобы язык мог работать над проблемой распределенных вычислений. Так появился C++.
Изначально C++ не был полноценным языком. Это был пакет препроцессоров для языка C.
Первый публичный релиз 1985 года, Cfront 1.0, был довольно приличным, но чтобы пользоваться им, надо было быть экспертом в C, чтобы не сломать свой же код.
К 1987 году GCC 1.15.3 начал поддерживать C++, а в 1989 году появился Cfront 2.0, который был гораздо лучше первой версии пакета.
В 1990 году был создан комитет ANSI C++, а в 1991 году – комитет ISO C++. В итоге появились C++98/03, затем C++11, затем C++14, C++17, C++20, и так далее.
Две недели спустя нам удалось на экране единственный статичный голубой квадратик
А вот на ps1 для этого требовалось пол года:) Вполне быстро.
За всё время работы с Rust я пользовался отладчиком, может быть, однажды, вся остальная отладка шла через printf.
Вот тут я что-то не уловил. В чём заключается сложность расставить брейкпойнты и запустить отладку Раста нажатием одной кнопки в VSCode, как вы описали это для C++?
Вы не понимаете, это другое:)
В чём заключается сложность расставить брейкпойнты и запустить отладку Раста нажатием одной кнопки в VSCode, как вы описали это для C++?
Совсем не улавливаете иронии в статьи? Вся статья же пропитана ею.
Этот текст в основном - ненавязчивый подкол C++. В данном месте автор подразумевает, что в Rust'е типа отладка практически не требуется... (Ну, по моему опыту потребность в отладчике на Rust'е действительно гораздо меньше, и отладочного вывода часто достаточно там, где на C++ уже очень хочется отладчика.)
Впрочем, надо отметить, что перевод текста не очень хороший, и он частично замыливает иронию и сарказм (не только по отношению к C++, но и к Rust'у, и к самому себе), достаточно неплохо поданные в оригинале.
Мне выше уже писали что вся статья - сплошная ирония, но, если честно, даже перечитав по второму кругу я не уловил ни иронии, ни стёба. Похоже, пора в отпуск.
Растокрузайдеры все выискивают тут иронию да постиронию главным образом потому, что трещит шаблон "ни один истинный шотландец растаман не может хвалить C++", хотя в реальности почему нет, особенно если ты инженер, а не зашоренный евангелист.
Коллега, мне растаманы карму обнулили за правду в глаза.😆
А я всего лишь утверждаю, что 80% якобы проблем плюсов надуманы, остальные 20 решаются знанием языка и избеганием его тёмных возможностей, особенно начиная с С++11.
Так же мой поинт в том, что создателям раст нужно пойти и крепко подумать, что они употребляли, работая над синтаксисом и названием некоторых сущностей, типа Rc и Аrc.
Ваши утверждения корректны только в случае, если вы работаете над проектом САМИ. А если людей много, то идёт забив, т.к. у каждого программиста своё понятие, что важно, а что нет. Можно, конечно, неугодных расстреливать, но так будет большая текучка кадров))
что 80% якобы проблем плюсов надуманы, остальные 20 решаются знанием языка и избеганием его тёмных возможностей, особенно начиная с С++11.
Не, ну в любом языке 95% проблем решаются знанием языка :)
В данном месте автор подразумевает, что в Rust'е типа отладка практически не требуется... (Ну, по моему опыту потребность в отладчике на Rust'е действительно гораздо меньше, и отладочного вывода часто достаточно там, где на C++ уже очень хочется отладчика.)
Ну тут ирония если и есть, то только наполовину. Я в своё время (пару лет назад) был бы рад использовать IDE с отладчиком, но вместо этого был вынужден пользоваться отладочной печатью из-за регулярных "съезжаний" точек останова и <not available> вместо значения переменной в lldb, хотя переменная на соответствующей строке была безусловно available. Сейчас вроде с этим получше, но сейчас я на расте практически не пишу. А уж в то время Visual Studio (не Code) реально был глотком свежего воздуха с его рабочими брейкпоинтами и удобной интроспекцией переменных по сравнению с растом.
Вот кстати только что попробовал ради любопытства rust-gdb
в консоли, вот для сравнения сначала C++:
Отладка программы на C++
$ cat hello.cpp
#include <iostream>
void foo()
{
constexpr int x = 100;
std::cout << x << std::endl;
}
int main(int argc, char **argv)
{
foo();
return 0;
}
$ g++ -o hello -g hello.cpp
$ gdb hello
[... шапку я выкинул ...]
Reading symbols from hello...
(gdb) break foo
Breakpoint 1 at 0x1175: file hello.cpp, line 5.
(gdb) run
Starting program: /home/oleg/tmp/hello
This GDB supports auto-downloading debuginfo from the following URLs:
<https://debuginfod.ubuntu.com>
Enable debuginfod for this session? (y or [n]) n
Debuginfod has been disabled.
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Breakpoint 1, foo () at hello.cpp:5
5 constexpr int x = 100;
(gdb) n
7 std::cout << x << std::endl;
(gdb) p x
$1 = 100
(gdb)
Точка останова по имени функции работает, печать значения constexpr
переменной тоже работает. Теперь раст:
Отладка программы на Rust
$ cat src/main.rs
fn foo()
{
const X : i32 = 100;
println!("{}", X);
}
fn main()
{
foo();
}
$ cargo build
Compiling hello-rs v0.1.0 (/home/oleg/tmp/hello-rs)
Finished dev [unoptimized + debuginfo] target(s) in 0.43s
$ rust-gdb target/debug/hello-rs
[... шапку я тоже выкинул ...]
Reading symbols from target/debug/hello-rs...
(gdb) break foo
Function "foo" not defined. <- Хрен
Make breakpoint pending on future shared library load? (y or [n])
(gdb) break *foo
No symbol 'foo' in current context <- Хрен-2
(gdb) break 3
Breakpoint 1 at 0x8d90: file src/main.rs, line 5. <- Ну хоть это работает
(gdb) run
Starting program: /home/oleg/tmp/hello-rs/target/debug/hello-rs
This GDB supports auto-downloading debuginfo from the following URLs:
<https://debuginfod.ubuntu.com>
Enable debuginfod for this session? (y or [n])
Debuginfod has been disabled.
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Breakpoint 1, hello_rs::foo () at src/main.rs:5
5 println!("{}", X);
(gdb) print X
No symbol 'X' in current context <- Хрен-3
(gdb)
Не работает ничего из вышеперечисленного - по крайней мере из коробки, может быть, какими-то дополнительными приседаниями можно чего-то добиться. Да, если заменить const
на let
, то печать будет работать, но ведь для C++ constexpr
переменная прекрасно печатается!
Вот этот тезис из начала треда не совсем верен:
В данном месте автор подразумевает, что в Rust'е типа отладка практически не требуется...
Из другой статьи автора:
I tend to stick to printf debugging because Rust debuggers are still pretty bad, so that's a wash for me. But in languages with good debugger support, I can see how this style might be a bit of a pain.
Так что тут вряд ли написано с иронией - прямо сказано, что дебаггер плюсов лучше
Ну вообще кстати брейкпоинт работает, если указывать полное имя (hello_rs::foo
), а не сокращенное. Но значение const
-символа мне напечатать в отладчике пока так и не удалось.
На мой взгляд, одно другому не мешает. Как я написал в "начале треда", автор иронизирует и над c++, и над Rust, и над собой. :)
Если посмотреть на контекст в той статье, ссылку на которую Вы дали, то там тоже звучит "мне-то норм, я привык отладчиками не пользоваться". Но в целом -спасибо, пожалуй, перечитаю статью ещё раз - может, там действительно меньше иронии, чем мне показалось.
Debugger плохой, иногда пропускает части кода, или в ассемблер кидает. В Rustrover также...
Ставишь бряку в файле xxx.rs подключаешь какую нибудь зависимость где есть файл с тем же именем и твоя бряка срабатывает в обоих. Я как то неудачно назвал файл очень популярным именем и получилось что одна бряка срабатывала в 5 местах - можете сами представить насколько это помогает отладке.
Допустим бряка сработала и к счастью где надо и нужно посмотреть строку. Так сложилось что мне надо дебажить много нетривиального динамически сгенерированного эскуэля, там вот строки с квериками получаются длинными, и дебагер или вскод (я не разобрался кто виноват) заботливо для вас обрезает строки, можете представить как это помогает в отладке.
И вот наконец то бряка сработала и там где надо, и даже строка с квериком оказалась не очень длинная, но у вас цикл по контейнеру в функциональном стиле т.е. лямбда в лембде и лямбдой погоняет. В студии я просто перемещаюсь по стэку и все локальные данные доступны, а вскоде это боль 80 уровня понять кто на ком стоит категорически не возможно.
Там ниже пишут про иронию сарказм и прочее в контексте принтфа вместо отладчика так вот это ни фига не ирония и не сарказм потому что не примитивная отладка в расте это настоящая боль, а когда она примитивная то оказывается она и действительно не нужна.
Сейчас еще готовят замену Rust и без borrow checker'a и проще (мнение авторов).
искать по YT, название: Hylo - The Safe Systems and Generic-programming Language Built on Value Semantics - Dave Abrahams
Также, Graydon Hoare (автор Rust), со слов Sean Parent в подкасте, положительно высказался о Hylo...
Создаём класс
GameObject
, затем объектыGameObject
, представляющие собой квадраты, жёстко вписанные в плиточную карту. Далее относим квадраты к подклассуSquareObject
, делаем объектыSquareObject
Почему люди предпочитают наследование агрегации?.. Сам таким был в начале карьеры, но автор-то вроде опытный - и туда же
наверно потому что к наследованию это не имеет отношения.
Я думаю тут автор акцентировал внимание на динамическом полиморфизме. А наследование просто один из способов достичь этого в C++
В данном случае не нужно ни наследование, ни агрегация. Если у игровых объектов есть своя иерархия, важная для игровой логики, она должна быть задана параллельно иерархии в классах, при помощи данных или DSL. А для движка это всё примерно одно и то же: вершинный буфер, индексный буфер, текстуры...
Некто Эрик Липперт написал такой же пример, доказывая, что DDD/ООП полная хрень. Но доказал лишь, что правильно высосанный из пальца пример доказывает что угодно.
Так тут цель обучения вообще новичка, начиная с языка. Сразу пытаться объяснить Entity-Component-System, пропуская "наивные" объектные реализации, чревато тем, что у обучаемого не будет всей картины, зачем всё это нужно, и какие проблемы решает.
Резюмирую недостатки С++ с точки зрения автора:
Недостатков самого языка нет
Нет общепринятых подходов к сборке и управлению зависимостями из-за распространённости на куче платформ (и, иногда, трудновато понимать тексты ошибок, хотя в большинство современных компиляторов вполне неплохо их выводят).
Добавлю от себя: вскользь читаю куда идёт многопоточка в с++26 и понимаю что надо садиться за книжки и лабораторные работы, не смотря на приличный стаж с этим языком. Хотя в целом идеи правильные.
Недостаток инструментария по нашим временам - это недостаток языка. :)
CMake + vcpkg/conan решают 99% вопросов. Просто этим тоже надо уметь пользоваться, как и языком. А для тех кому слишком сложно - есть какой нибудь js или python :)
Потому что программист должен изучать арбитрарный набор всякого (часто) плохо документированного инструментария с кривым интерфейсом и недостатком примеров, чтобы сделать совершенно типовую рутинную задачу (собрать проект или добавить в него зависимость).
Потом он прийдёт в другой проект, а там свои погремушки, поэтому надо учить новый набор опенсорсных вкусностей.
(Не важно что все эти QOL вещи есть в любом современном языке, не зависимо от его низко/высоко-уровневости, если ты не страдаешь по пустякам, то ты не Труъ программист)
Ну извините. В вашем мире походу вообще должна быть только одна операционка, одна архитектура с одним набором инструкций и тп. Одних способов деплоя под линукс вон сколько, не говоря о винде и маках.
Так же в вашем идеальном мире не нужны билд инженеры, девопсы и тп, потому что должны быть стандартные способы разворачивания и сборки.
Я правильно понял? Я не говорю, что это хорошо, что в плюсах это не совсем тривиально. Но это достаточно гибко и всё зависит от потребностей.
Ну извините. В вашем мире походу вообще должна быть только одна операционка, одна архитектура с одним набором инструкци...
Это вы о чём?
Так же в вашем идеальном мире...
Откуда вы про идеальный мир взяли? Пакетные менеджеры и средства сборки это что-то что есть в применяющихся на практике ЯП.
Я не говорю, что это хорошо, что в плюсах это не совсем тривиально
Вы по крайней мере не считаете это проблемой, а если кто-то считает, то "пусть идёт программировать на языках для ненастоящих программистов".
(Я ни в коем случае не адвокат js и питона, просто вы тут ложную дихотомию устроили).
Но это достаточно гибко и всё зависит от потребностей
Да и в языках с менеджерами зависимостей всё гибко. Только там не надо ломать голову над типовым сценарием, всё работает из коробки.
Я люблю C++, но у языка однозначно есть недостатки, особенно если сравнивать его с другими современными языками.
explicit, [[nodiscard]] и пр. должны быть по умолчанию
У лямбд нет короткого синтаксиса
enum class вовсе не class
Конечно язык развивается, но zip_view, expected, статические operator[],() появились только в C++23. Рефлексию и pattern matching все ещё ждём. Надеюсь что они получатся лучше чем корутины, которые ещё долго можно будет рассматривать как недостаток языка.
А лямбды чем не угодили?
При передаче даже простых функторов в алгоритмы вроде std::transform лябмды очень многословны. Меня устраивает явный захват переменных, но иногда хочется чего-то более простого.
В Cpp2 Герб Саттер сформировал очень приятный синтаксис, единый для функций и лямбд. Если вкратце, то auto
в аргументах функции опционален и предполагается по умолчанию. Хотя меня и немного расстраивает отсутствие возможности явного захвата, сам синтаксис приятнее текущего.
вкратце, то
auto
в аргументах функции опционален и предполагается по умолчанию
а какой конкретно? auto, auto& или auto&&?
Добавил бы, ещё спецификатор const для переменных по-умолчанию
С explicit не будет работать часть низкоуровневых либ по софтварной эмуляции чего-то. Так же не будет работать математика, что значит писать так больше нельзя int x = (3.14 + у)*2
, и надо будет учитывать все конверсии, что выглядит как лютый заморочь. А ещё с explicit не работает добрая половина способов инициализации перемнной, буквально ограничивая тебя 1-2 способами.
С [[nodiscard]] ты не сможешь пользоваться даже memcpy, ибо он неожиданно возвращает указатель который ты ему же и передал. Многие функции ОС тоже что-то возвращают и придётся писать кучу веток кода по обработке сколько символов в консоли ты написал, иначе тебе варнингов на пол экрана намолюет.
С лямбдами сложно, ибо в целом можно сделать чтоб компилятор сам строил лямбды, но это будет не везде решаемо. Мол как угадать переменная ссылка или значение? А это важно между прочим. Можно было бы сделать новый синтаксис как в С#, но для лямбд без захвата, тогда было бы прикольно, согласен.
С енумами непонятки какие-то, ибо это "отдельная сущность". Но всё же логично что класс означает что это "объект сам в себе с explicit кастом в дефолтный енум или специальный тип". Хотя меня бы устроило если бы дефолтный енум не сыпал своими членами в пространство имён, хотя это может через классы эмулировать, лол.
Я понимаю что некоторые изменения невозможно внести с сохранением обратной совместимости, и это одна из причин почему в c++ ещё долго будут проблемы. Тем не менее некоторые из данных примеров вполне могут работать даже с сохранением обратной совместимости, хотя я сомневаюсь что они будут изменены.
explicit
по умолчанию не значит отсутствие возможности обозначить конструктор как implicit используя ключевое слово или атрибут.
[[nodiscard]]
не препятствует игнорированию аргументов по умолчанию, а лишь вызывает предупреждение. После введения _
плейсхолдера в C++26 или используя каст в void
.
enum class X{}; уже не экспортирует свои значения в окружающее пространство имён, и не конвертируется имплиситно из/в нижележащий тип. Но у него все ещё не может быть членов, что делает работу с ним менее удобной.
Меня тут только explicit напрягает. Мол включить некоторые предупреждения по умолчанию это полезно, да, но не для explicit. Это буквально сломает добрую часть С++. Слишком много логики завязано на этой фиче и просто так запретить её было бы странно.
Лучше бы добавили правило в варнинги, мол тип который не является агрегатом должен указывать явно explicit он или нет. Тогда бы вещи типа С-структур бы работали, а вот всякие самописные менеджеры чего-либо - нет. Всё ещё бы потребовало переписывать кучу кода, но зато было бы +- понятным почему так сделано.
Все жду когда сделают передачу параметров в функцию по имени... Такая простая вроде вещь, даже в примитивном T-SQL от майков есть, а в плюсы так и не завезут... Приходится либо как-то агрегировать параметры, либо экзотику городить. Или может в новых стандартах что-то поменялось и уже не надо принудительно писать 30 дефолтных значений аргументов только для того, чтобы указать недефолтное 31-е значение ?.. :(
надо принудительно писать 30 дефолтных значений аргументов только для того, чтобы указать недефолтное 31-е значение
а вы уверены что тут проблема в отсутствии такой фичи в языке...?
/s
Вопрос был про фичу в языке... И на 10 параметрах споткнуться можно, а это уже более распространенный случай, правда ? За 20 лет эволюции кода кровавого энтерпрайза всякие монстры могут вырасти ...
Имена параметров как в C так и в C++ - это по сути своей опциональная вещь, важная только внутри самой функции, и больше нигде. При объявлении прототипов их допустимо вообще не указывать (а я встречал организации, где такое даже задано местными гайдлайнами). Далее, скажем, в std имена параметров не являются частью контракта, только типы являются, поэтому в разных реализациях std от разных компиляторов у одних и тех же функций будут разные имена параметров (а в каких-то случаях их вообще не будет). Никто не будет реализовывать фичу, которой нельзя надёжно пользоваться даже с std.
Мне бы хватило адекватного препроцессинга, когда в коде указана только пара именованных параметров, затем это (как с макросами) разворачивалось бы в нормальный вызов функции со всеми 30 дефолтными параметрами и парой указанных явно, и только потом это скармливалось бы компилятору... Это, думаю, совсем не трудно реализовать и совместимость бы никак не пострадала.
Имена параметров стираются из определений функции, ниже корректный код
// include 1
void f(int x, int y);
// include 2
void f(int y, int x);
// implementation
void f(int a, int b)
{
}
Можно использовать назначенную инициализацию, если допустимо передать один объект вместо кучи параметров - https://compiler-explorer.com/z/orM9sh9de
Сорри, только после отправки заметил что это дубль - https://habr.com/ru/articles/844988/#comment_27330472
Все представленное выше - частные случаи агрегации параметров - а это новый объект, выделение памяти, инициализация и пр... В то время как прямая передача параметров в функцию работает на регистрах.
С одной стороны, можно все эти обёртки объявить как inline
и компилятор соптимизирует. С другой стороны, если брать ваши случаи, под 10-30 параметров в функцию, они передаются через стек, и там без разницы - эти параметры лежат в стеке просто последовательно, или в той же самой памяти в стеке, но оформленные в структуру.
Да и для таких функций, даже лишнее копирование параметров не критично для производительности. Было бы критично - был бы другой интерфейс.
По тексту ошибок могу добавить, что текст зависит от компилятора. И иногда, чтобы понять о чём идёт речь, легче собрать несколькими компиляторами один модуль. Текст ошибок отличается и где-то в одном случае лучше написано у Clang, где-то у GCC
А ещё разные компиляторы по-разному выдают warnings. Некоторые из них полезны тоже по-разному, а некоторые очень даже помогают найти реальные ошибки и проблемы.
Из свежего - clang указал, что в цикле for (auto const var... делается копия большой переменной var, хотя к ней можно было бы обращаться по ссылке. Другие компиляторы этого не заметили.
Я, как обычно, придерусь к качеству перевода. Я понимаю, что это беда всех современных толмачей, которые заведомо не понимают смысла переводимого текста. Чукча не читатель, чукча переводчик. Но всё равно обидно, когда читаешь текст, и не можешь понять, откуда в нём вдруг взялась "большая аудитория". Приходится лезть в оригинал, где всё логично - аудитория превращается в "прекрасную возможность", поскольку "почти любую концепцию из C++ легко понять, если объяснить её в ключе «о, эта как та штука из Rust»."
"Ask questions later" означает совсем не "а уже потом переходили к вопросам". К вопросам тут никто и не собирается подходить. Это классические "надмозги", над которыми Гоблин так угорал в 90-е. Розетта Стоун. Причем если читать текст, а не только переводить его, то буквально парой абзацев объясняется подход "На нём можно написать код сейчас, а поправить потом", который здесь и реализуется. В виде "сделаем статическую переменную, а о недостатках этого решения подумаем когда-нибудь потом". Отложим в долгий ящик.
моя сестра была так не склонна учить C++
Шта? Мы только что читали, что она упёрлась рогом и будет учить только C++! Так склонна или нет? Оказывается склонна, а в тексте на самом деле "сообщения об ошибках были, пожалуй, самым большим затруднением в изучении С++, если не считать проблем со сборкой". Причем здесь уже задача даже не на понимание, а тупо перевод - impediment к желаниям и склонностям никакого отношения не имеет.
И так весь текст.
" она будет сидеть за отладкой ошибок сегментирования",
А может и не будет, C++ не настолько страшен, как Си, где может всё питание выгореть к х-м, вообще проблемы С++ две - 1) каждая библиотека типа stl, qt, boost епёт на свой манер, попробуй разберись, 2) избыточная лексика, для обратной совместимости, чего не стоило бы делать , на мой взгляд, к примеру для чего необходимо вводить две сущности шаблонов typename и class (object pascal даже отказался от семантики Object в пользу Class), почему надо давать описание каждой переменной в интерфейсе функции, если переменные имеют одинаковую семантику, это только самое бвдоражущее, могу накидать ещё)
boost как раз таки во многом следует принципам и стилю stl.
А qt это альтернатива stl, поэтому различия вполне оправданы
к примеру для чего необходимо вводить две сущности шаблонов typename и class
Это не две сущности, а одна, просто под разными именами, которые абсолютно взаимозаменяемы в контексте объявления шаблонов и означают совершенно одно и то же, просто этакая дань совместимости, и не более того.
object pascal даже отказался от семантики Object в пользу Class
А вот тут не тот случай, тут действительно разная семантика: object может быть размещен на стеке, а class - только в куче.
почему надо давать описание каждой переменной в интерфейсе функции, если переменные имеют одинаковую семантику
Не совсем понял, о чем вы.
Вот что происходит, если изменить сигнатуру функции в заголовке, а в файле
.cpp
этого не сделать – и наоборот.
Что случится? Qt Creator сразу подсветит.
C++ с точки зрения Rust-разработчика: достоинства и недостатки