LINUX.ORG.RU

C++ — туда и обратно, или зачем нужен Boost

 , , ,


3

5

Мой предыдущий тред в Development собрал самое большое число ответов аж с сентября, то есть за последние 9 месяцев, и это лишний раз подтверждает упадок этого форума. Полагаю, кто-то должен это изменить, и поэтому мы с тобой, ЛОР, поговорим сегодня про C++.

Начиная с C++26 вместо std::function вводится пачка новых классов: std::copyable_function, std::move_only_function (доступна с C++23) и std::function_ref. Что же не так с оригинальным std::function, ты можешь спросить? А вот что:

#include <functional>
#include <print>

struct call_me {
    int x = 0;
    void operator()() {
        std::print("x was {}\n", x++);
    }
};

int main() {
    const std::function<void()> f = call_me{};
    f();
}

Несмотря на то, что переменная f объявлена константной (люблю оксюмороны!), у неё есть внутреннее состояние и оно меняется при вызовах. Компилятор это без проблем хавает.

Так вот, ковыряясь в том, зачем и кто вообще смог так насрать себе в штаны сам, я наткнулся на статью ребят, которые подводят список подобных косяков комитета C++, когда фичи живут по 10 лет и объявляются устаревшими.

Небольшой список фич, которые были придуманы, оказались не нужны/бесполезны/вредны и выкинуты:

  • Известный vector<bool>, живущий издревле в STL и про который все говорят, что его надо избегать. Частично заменяется std::bitset.
  • std::auto_ptr. Бесполезен, ломает контейнеры, выкинут на помойку в C++17.
  • Указание исключений у функции в формате throw(X, Y). Так же выкинуто в C++17.
  • std::iterator объявили устаревшим в C++17, собираются удалить в C++26.
  • std::aligned_storage и std::aligned_union добавлены в C++11, объявлены устаревшими в C++23, скоро удалят.
  • Ключевое слово register удалено в C++17, хотя всё ещё доступно в Си.
  • std::get_temporary_buffer и std::raw_storage_iterator удалены в C++20.
  • Потрясающее по эпичности фиаско с интерфейсом для сборщиков мусора. std::declare_reachable сотоварищи были добавлены в C++11. Выяснилось, что сборщики мусора для C++ писать либо никто не умеет, либо никто не хочет, поэтому в C++23 это всё удалили и сделали вид, что ничего не было.
  • Абсолютное безумие вокруг концептов, модулей и поддержки сети. Предложения одобряли, вновь отклоняли, переделывали, и по итогу теми же модулями до сих пор никто не пользуется.
  • Сопрограммы (coroutines). В том виде, в котором они есть в C++, это просто ужас. Достаточно того, что корутины требуют выделения памяти из кучи во время работы, а значит вообще не подходят для случаях, когда требуется серьёзная производительность. Например, в любом коде, требующим работы в реальном времени и не позволяющем делать системные вызовы.

Просто лютый трешак, который никто подчищать пока не собирается:

  • std::regex – лютый тормоз, рекомендуется не использовать.
  • Мертворождённый std::simd, добавленный в C++26 и уже с ходу не нужный вообще никому, потому что код с std::simd в два-три раза тормознее чем со сторонними библиотеками, просто голыми интринсиками, и даже медленнее чем просто цикл for.
  • std::async. Дескруктор ждёт завершения асинка и поэтому может залочить весь код тебе. Наконец починили в C++26, но эта штука была сломана 15 лет.
  • Отвратительно спроектированный <iostream>. Его наконец можно выкинуть и использовать std::print, но не все про это знают.
  • Абсолютно тормозные контейнеры map, set, unordered_map. Вместо первого можно использовать flat_map из C++23. Контейнеры в стандартной библиотеке Rust (BTreeMap) и другие реализации B-Tree Map их обгоняют по производительности, но тем не менее в C++ выбирают убогий дефолт.

Решения многих из этих проблем существуют в Boost и сделаны там гораздо лучше. Но в то же время возникает важный вопрос: зачем вообще нужен настолько плохо спроектированный язык, где каждое следующее поколение инженеров, работающих над ним, отменяет решения предыдущих, а код под новые стандарты часто нужно переписывать если не с нуля, то очень близко к тому? Даже процесс разработки Rust с его поехавшими клоунами в юбках на этом фоне выглядит адекватным.

В общем, всё печально, ЛОР. Такие дела.



Последнее исправление: CrX (всего исправлений: 3)
Ответ на: комментарий от zurg

Никто, никогда не забывает ставить copy,

Напряги кочерыжку внутри кочна - зачем в новом, чудном и хорошем языке руками копипастить бойлерплейт?

бывают типы которые нельзя копировать

Частный случай, 0,00001%.

Внимание вопрос: зачем язык как дефолтную конструкцию рассматривает 0,00001%, а в остальных 99,9(9)% погромизд должен копипастить семантический мусор?

r--r--r--
()
Ответ на: комментарий от yorshka

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

В продолжающей работать программе.

В Rust же почти никто паники не перехватывает и, соответственно, последствия от паники в drop куда мягче.

Программа тупо падает, да.

r--r--r--
()
Ответ на: комментарий от eao197

В C++ использование try{} catch() {} является чуть ли не повсеместным

Похоже, все зависит от того, кто куда и как смотрит.

Если ты работаешь на гугель и весь код собирается с -fno-exceptions, вопросов конечно нет. Но это редкость. В среднем, в проектах, с которыми я работал, исключения достаточно часто использовались как часть control flow, особенно в более старом коде (до C++11).

yorshka
() автор топика
Ответ на: комментарий от yorshka

В среднем, в проектах, с которыми я работал, исключения достаточно часто использовались как часть control flow,

Я специально поискал catch в своем проекте. Всего лишь несколько десятков вхождений на 37KLOC кода.

eao197 ★★★★★
()
Ответ на: комментарий от yorshka

в проектах, с которыми я работал, исключения достаточно часто использовались как часть control flow

Я даже на java такого не видел.

r--r--r--
()
Ответ на: комментарий от eao197

Весь этот набор банальностей не отвечает на заданные мной вопросы.

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

Возможно, это сообщение тебе тоже покажется банальным.

Darfin
()
Ответ на: комментарий от eao197

В среднем, в проектах, с которыми я работал, исключения достаточно часто использовались как часть control flow,

Я специально поискал catch в своем проекте. Всего лишь несколько десятков вхождений на 37KLOC кода.

Всё, что это значит, это что мы работаем над разными проектами. Только и всего.

Тем не менее, в Rust при написании реализации drop программисту не нужно думать что какая-то из вызванных функций запаникует, если это явно не является частью API (например, .unwrap(), за который надо по рукам бить), потому что паника является аварийной ситуацией. В случае с C++, стоит рассчитывать, что любая посторонняя функция может кинуть исключение.

TL;DR не надо сравнивать панику и исключения в C++. Это разные механизмы для разных целей.

yorshka
() автор топика
Ответ на: комментарий от Darfin

Возможно, это сообщение тебе тоже покажется банальным.

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

… нет смыла ее как-то обрабатывать, приложение уже находится в непонятном состоянии, все что можно сделать — побыстрее завершить работу.

Паники обрабатывают разве что ради какого-то логирования о причинах падения приложения, после чего приложение все равно завершает работу.

И поэтому буквально каждый веб-фреймворк имеет готовое middleware для перехвата и обработки паник, ага.

r--r--r--
()
Ответ на: комментарий от yorshka

Всё, что это значит, это что мы работаем над разными проектами. Только и всего.

Я, собственно, сразу об этом и сказал:

все зависит от того, кто куда и как смотрит.


TL;DR не надо сравнивать панику и исключения в C++.

Я не сравниваю панику с исключениями. Я сравниваю количество знаний, которые нужны в C++ для написания деструктора, с количеством знаний, которые нужны в Rust-е для написания Drop::drop.

eao197 ★★★★★
()
Ответ на: комментарий от r--r--r--

И в чём же фундаментальная разница?

Документацию почитай, хотя бы:

https://doc.rust-lang.org/reference/panic.html

Например, в том, что бесконечный цикл при панике – вполне валидная реализация.

yorshka
() автор топика
Ответ на: комментарий от eao197

Я сравниваю количество знаний, которые нужны в C++ для написания деструктора, с количеством знаний, которые нужны в Rust-е для написания Drop::drop.

Окей. В C++ этих знаний требуется больше.

yorshka
() автор топика
Ответ на: комментарий от yorshka

бесконечный цикл при панике – вполне валидная реализация.

Но это не разница между throw и panic!(). Это разница между крестами и растом.

Давай я переформулирую вопрос.

Дано:

Пишем обработчик http-запроса, в ходе которого фетчим предзагруженные данные в бд. В ходе обработки, у нас возможны три сценария ошибок:

  1. Ошибка на вводе / ввыводе с БД
  2. Ошибка структуры БД
  3. В БД нет pre seed данных

API на все операции корректно возвращает Result / Option.

Вопрос:

Будешь ли ты делать unwrap()?

r--r--r--
()
Ответ на: комментарий от r--r--r--

https://godbolt.org/z/j1rc31h8d я буду использовать С++, потомучто в нём есть наследование, а Раст не серьезный язык, даже с такой тулой, факт заключается в том, что С просуществовал в еще более худшем состоянии, когда Раст не осилел наследование, но в тот же момент осилил борова

anonymous
()
Ответ на: комментарий от eao197

получается одна из нюансов С++ и Раста

https://godbolt.org/z/abdY6MTKz С++

https://godbolt.org/z/ersj5hdT4 Раст

как по мне проблема очевиднее между двумя языками чем может показаться, когда начнём проектировать на Расте, когда на С++ проектирование удобнее получается нагляднее чтоли, дело не в методе дроп или деструктор, но С с деструктором было бы вау и классно сегодня )

anonymous
()
Ответ на: комментарий от zurg

понимаете в чем дело, вот мы начинаем путь - изучаем кодинх, вот например С/С++, вдруг появляется Раст, по началу прикольно, но попользовавшись такими удобствами начинаешь понимать, что Раст крут да? конечно, никто не спорит, но во первых Раст нельзя отключить и включить своё видение, на С++ есть больше опций для реализации своего видения, прибавив сюда тонкости того что зачерпнули в Расте, возвращаемся на С++ с уже осознанными запросами к языку и более менее становится понятно, что то что предлагает Раст не панацея, а взгляд одного из. Получается С++ просто шире, наследование покрывает нужные задачи где он просто обязателен и он не костыльный как весь ООП в С++ после кодинга на Расте, а в Расте это портянки и компизиции бесконечные. А ну еще нюансы FFI. Ну да многопоточка ваще улётная, но после Раста реально на С++ смотришь не так как до захода в Раст.

а по тому что мол костылинг, есть же вроде std::optional

anonymous
()
Ответ на: комментарий от r--r--r--

Но это не разница между throw и panic!(). Это разница между крестами и растом.

Я даже не знаю, как вот эту строчку прокомментировать.

Будешь ли ты делать unwrap()?

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

yorshka
() автор топика
Ответ на: комментарий от anonymous

если я правильно понимаю, алгебраические типы в С++ можно просто создать свои выделить их в группу математических, тоесть отделив от строк и буквенных значений, и просто вывести нужное поведение, в Расте да удобно в этом плане тыкнул в инпут нужного вывода библиотеки и провел тип и поведение, но опять же реализовывая BVH на обоих языках с минимальными возможностями в 3д код работает абсолютно индентично по моим ощущениям, да для стриминга и многопотока придётся вникнуть в мутексы и локи возможно, но я считаю это обоснованная плата входа, потомучто всё до стриминга вполне реализуемо, а это самое начало технологий только, там что не рассматривай на старте хоть бвх хоть бсп, они почти одинаковы по реализациям будут, но реализация УИ на композиции - извините, пока не попробуешь дерево, конечно интересно, но после ООП на УИ глядя как оно работает, компизиции писать вообще нету желания.

anonymous
()
Ответ на: комментарий от r--r--r--

Будешь ли ты делать unwrap()?

Конечно же нет.

А что ты будешь делать? Вот вернул тебе сиквель запрос ошибку. Твои действия?

Очень странный вопрос, чувак.

Встречный вопрос: вернул тебе read() в сишной проге ошибку. Что ты будешь делать? Падать в кору?

yorshka
() автор топика
Ответ на: комментарий от yorshka

а тут нюансы есть, предположим байты не потерялись от С, которые пришли в Раст, где-то читал, что байты могут потеряться, типо С кидает строку, а Раст на приёме потерял что-то. А в событийной модели красивее, кд, события и прочее, наверно это и есть control flow.

anonymous
()
Ответ на: комментарий от yorshka

вернул тебе read() в сишной проге ошибку. Что ты будешь делать?

Разверну стэк вызовов обратно с кодом ошибки до точки, где ошибку имеет смысл обработать.

Теперь к моему "очень странному" вопросу, который, как я догадываюсь, описывает что-то очень странное и вообще не возможное:

Ввернул тебе сиквель запрос ошибку. Твои действия?

r--r--r--
()
Ответ на: комментарий от r--r--r--

Теперь к моему «очень странному» вопросу, который, как я догадываюсь, описывает что-то очень странное и вообще не возможное:

Ввернул тебе сиквель запрос ошибку. Твои действия?

Ну вот ровно так же и сделаю:

Разверну стэк вызовов обратно с кодом ошибки до точки, где ошибку имеет смысл обработать.

Только вместо ручного сишного if(err != 0) return err; в Rust есть Result и оператор ?. А в остальном суть та же.

yorshka
() автор топика
Ответ на: комментарий от yorshka

Ну вот ровно так же и сделаю:

Что ты сделаешь-то, я не понял нихера. Ты пишешь код обработчика. Никакого контроля над кодом по стеку выше у тебя нет. API обработчика требует вернуть из функции объект с ответом. Как вариант, можешь вернуть экземлпяр impl Responder / Writable. Твой err клиенту в браузере нахер не упёрся.

в Rust есть Result

Тебе ещё Option обрабатывать надо.

и оператор ?.

Который чем тебе поможет?

Ты, я так чувствую, с растом только по срачам на лоре знаком?

r--r--r--
()
Ответ на: комментарий от r--r--r--

Никакого контроля над кодом по стеку выше у тебя нет. API обработчика требует вернуть из функции объект с ответом. Как вариант, можешь вернуть экземлпяр impl Responder / Writable. Твой err клиенту в браузере нахер не упёрся.

Ну, значит единственный вариант тут – записать в лог и вернуть 500.

Тебе ещё Option обрабатывать надо.

И в чём проблема?

Ты, я так чувствую, с растом только по срачам на лоре знаком?

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

yorshka
() автор топика
  • Markdown
Пустая строка (два раза Enter) начинает новый абзац. Знак '>' в начале абзаца выделяет абзац курсивом цитирования.
Внимание: прочитайте описание разметки Markdown.
Используйте Ctrl-Enter для размещения комментария