LINUX.ORG.RU

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

 , , ,


3

4

Мой предыдущий тред в 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)
Ответ на: комментарий от moonmadness

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

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

Я окончательно осознал, что там наркоманы сидят, когда приняли линанал линал

moonmadness ★
()

«И это всё?» (С) Доктор Ливси на жалобы капитана Смоллета, М\ф «Остров сокровищ»

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

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

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

Что значит «приемлемый»? Практически половина добавленных в последние 10-15 лет фич косячные. При этом древние уродства типа vector<bool> живут и здравствуют, и их не торопятся исправлять.

Плюс к этому, сложность C++ обусловлена исключительно историческими причинами и попытками впихнуть невпихуемое. Тот же Rust проще и при этом покрывает все требования к C++ без проблем.

yorshka
() автор топика

со времени появления (бесконечно ругаемой) сишки ничего лучше так и не придумано, увы.

olelookoe ★★★★
()

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

Ну попробуйте поменять значение f. А то что её состояние меняется, const не виноват, не об этом он.

В том же Rust: &foo от &mut foo отличается не константностью внутреннего состояния.

объявили устаревшим …, собираются удалить …

Да и пофигу. Не зачем гнаться за «новым».

Ключевое слово … удалено …, хотя всё ещё доступно в Си.

По большей части касалось неправильно используемого. Всяких эмбедед либ за последние 10 лет от этого почистили.

Его наконец можно выкинуть и использовать std::print,

Лет 10 ещё только fmt::print

regex, simd, async

По фигу. Это пусть особо умные писатели библиотек и экспериментаторы используют.

З.Ы.: Мой подход:

  • Чем проще – тем лучше.
  • Должно компилироваться на предыдущих релизах Debian.
AlexVR ★★★★★
()
Ответ на: комментарий от yorshka

vector<bool>

О да, я тоже не понимаю зачем. Когда мне понадобилось такое, я использовал bitset

hibou ★★★★★
()
Последнее исправление: hibou (всего исправлений: 1)

Ключевое слово register удалено в C++17

Оно надолго зарезервировано.

name.cpp:1:11: error: expected identifier before ‘register’
 namespace register{
           ^~~~~~~~
name.cpp:1:19: error: expected unqualified-id before ‘{’ token
 namespace register{
                   ^
vM ★★★
()
Ответ на: комментарий от AlexVR

Должно компилироваться на предыдущих релизах Debian.

Как-то уныло поддерживать долгоживущий софт, когда сидишь, и добавляешь фичи из n-2 стандарта в программу на n-3, а потом идёшь и читаешь, что в n+1 это всё нахрен задепрекейтят в пользу седьмого способа сделать то же самое, только лучше.

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

сидишь, и добавляешь фичи

тарапицъ нинад!

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

Не зачем гнаться за «новым».

Незачем этот кал в виде всяческих std::function использовать.


Сейчас ещё рефлексии в код понатаскают, мало им шаблонных гирлянд( Даст бог мало кто разберется с корутинами, не так популярно будет.

BruteForce ★★★★★
()
Последнее исправление: BruteForce (всего исправлений: 1)
Ответ на: комментарий от AlexVR

Чем проще – тем лучше.

Тогда тебе вообще к C++ близко нельзя подходит. Ничего простого в C++ нет изначально.

yorshka
() автор топика

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

А зачем переписывать код под новые стандарты? Почему нельзя использовать какой-то старый стандарт лет 20, пока идет жизненный цикл проекта.

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

А зачем переписывать код под новые стандарты?

Потому что в них может появиться фича, которая сильно облегчит жизнь. Лямбды из C++11, например, радикально упростили жизнь и позволили выкинуть примерно 80% лапши из визитеров и подобного рака.

Почему нельзя использовать какой-то старый стандарт лет 20, пока идет жизненный цикл проекта.

Это неверный вопрос. Правильный вопрос выглядит так: почему нельзя спроектировать язык сразу нормально, а не четыре раза и через разные жопы?

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

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

zurg ★★
()
Последнее исправление: zurg (всего исправлений: 1)

Отвратительно спроектированный . Его наконец можно выкинуть и использовать std::print, но не все про это знают

наивное летнее дитя.

$ cat main.cpp      
#include <iostream>

int main() { 
  std::cout << "hello\n";
}
$ time g++ -std=c++23 main.cpp -static-libstdc++ -o main
g++ -std=c++23 main.cpp -static-libstdc++ -o main  0.42s user 0.07s system 99% cpu 0.496 total
$ strip ./main
$ du -h ./main                          
1.0M	./main
$ cat main.cpp      
#include <print>

int main() { 
  std::print("hello");
}
$ time g++ -std=c++23 main.cpp -static-libstdc++ -o main 
g++ -std=c++23 main.cpp -static-libstdc++ -o main  0.70s user 0.11s system 98% cpu 0.823 total
$ strip ./main
$ du -h ./main                                            
1.2M	./main

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

Чисто для сравнения поделка какого-то китайского студента, в которой в пару раз больше фич и которая работает в пару раз быстрее:

$ gh repo clone cppfastio/fast_io 
$ cat main.cpp
#include "fast_io/include/fast_io.h"

int main() { 
  fast_io::io::print("hello\n");
}
$ time g++ -std=c++23 main.cpp -static-libstdc++ -o main
g++ -std=c++23 main.cpp -static-libstdc++ -o main  0.28s user 0.04s system 99% cpu 0.324 total
$ strip main
$ du -h main 
100K	main
Lrrr ★★★★★
()
Ответ на: комментарий от Lrrr

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

И чтобы менять формат вывода, тебе нужно ставить мютекс на доступ к std::out, а не то формат поедет сразу во всех потоках. Хочешь распечатать число в шестнадцатеричном формате? Все будут печатать в шестнадцатеричном!

time g++

Ей богу, в типичном проекте на C++ время сборки от std::print просто ничтожно. Там стопудов на шаблонах нахреначено куда больше.

-static-libstdc++

Ахахахах! Что ты делаешь? Прекрати!

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

почему нельзя спроектировать язык сразу нормально

Потому что мы не можем залезать в голову другим людям и делать там нормально. А в свою - можем. Это надо и использовать.

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

Ей богу, в типичном проекте на C++ время сборки от std::print просто ничтожно. Там стопудов на шаблонах нахреначено куда больше.

немного говнокода там, немного говнокода сям, и в результате получается очередной хупрланд, компиляющийся 15 минут (правда или нет не знаю, сам не проверял, но цифра похожа на правду)

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

Тогда тебе вообще к C++ близко нельзя подходит.

Полностью с тобой согласен. Меня нельзя. Я вообще любитель структурок. А из всего Modern C++ признаю только move-семантику (и немного по-мелочи плюшечек).

Ничего простого в C++ нет изначально.

На любом ЯП можно программировать без наворотов.

AlexVR ★★★★★
()

Тот случай, когда даже не успел узнать о том, что добавили и уже выпилили.

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

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

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

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

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

Как-то уныло поддерживать долгоживущий софт

По большей степени сдерживает не версия компилятора. В последние годы gcc стал намного удобнее для статической линковки своего рантайма. А вот когда надо поддерживать старые и «специальные» дистрибутивы, то версии библиотек сносят башню.

AlexVR ★★★★★
()

Достаточно того, что корутины требуют выделения памяти из кучи во время работы, а значит вообще не подходят для случаях, когда требуется серьёзная производительность.

Выделяй в своей куче..

dynamic_cast ★
()

std::regex – лютый тормоз, рекомендуется не использовать.

Регулярки сами по себе тозмознутое УГ. Если можно обойтись О(n) реплейсом, надо использовать реплейс..

dynamic_cast ★
()

Известный vector, живущий издревле в STL и про который все говорят, что его надо избегать. Частично заменяется std::bitset

Здесь согласен. Специализацию vector добавили наркоманы..

dynamic_cast ★
()

std::regex – лютый тормоз, рекомендуется не использовать.

Это относится к регекспам в целом или именно к конкретному движку? На PCRE быстрее будет? Я просто всегда тестирую скорость, потому что если писать не думая об этом, да ещё вставить это в цикл, то конечно получится медленно.

AZJIO ★
()
Последнее исправление: AZJIO (всего исправлений: 1)
Ответ на: комментарий от yorshka

Умные указатели норм. С ними очень просто работать, особенно после указателей С, где это не число, не адрес, а невыразимое хрен пойми что.

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

это ненадол

у профессора карабин, то целому стаду крокодилу пипец без вариантов.

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

что в приведённом примере не так? патроны однажды закончатся. и сколько останется профессоров? на интервале лет 20?

приблизительно ни одного.

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

Цинк 700 патронов, в ящике 2 цинка, у профессора 150 ящиков. Столько крокодилов в районе не наберется.

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

формат поедет сразу во всех потоках

если ты выводишь в системный out из нескольких потоков одновременно, то кто тут ССЗБ?

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

Это же тебе не раст. Нужно несколько лет, чтобы каждую фичу обкатать на промышленном коды, потом уже сделать выводы.

Lusine
()
Ответ на: комментарий от dynamic_cast

её добавили в стандарт, в расчёте на то, что писатели компиляторов реализуют битовый вектор более эффективно. пейсатели нешмогли и забили. чем тут виноват стандарт?

и вообще половина всего этого высера ТСа — критика стандарта за кривые реализации. ну, сядь и реализуй не криво, или слабо́?

anonymous
()

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

Собственно не вижу причин почему через 27 лет что-то примерно похожее не будет написано про rust, хотя на самом деле я могу и сейчас написать такой же список, и про БЧ который ругается на валидные кейсы, и на дыры в системе типов, и на то что стабилизируется годами, и на хэшмапу которая тоже тормозная (ради стойкости к атакам). И про другие языки, включая go, kotlin и скалу, я от коллег таких же списков наслушался.

Короче, вырасти, и прими что ничего идеального не бывает, что если что-то не нравится то иди и почини (но сначала разберись почему оно именно так, иначе с большой вероятностью тебе твои починки вернут с подробными объяснениями), и что код не получится написать один раз и не трогать 10 лет.

Ну и это, иногда инструменты правда устаревают, и имеет смысл периодически мигрировать на актуальные, а не ныть как всё плохо.

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

если ты выводишь в системный out из нескольких потоков одновременно, то кто тут ССЗБ?

Ты предлагаешь логи писать в отдельный поток из каждого треда? Или предлагаешь делать отдельный поток под логирование исключительно ради синхронизации, которая уже есть в системе, но C++ её ломает?

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

Что значит «приемлемый»? Практически половина добавленных в последние 10-15 лет фич косячные. При этом древние уродства типа vector живут и здравствуют, и их не торопятся исправлять.

Чтобы их исправить нужно сломать совместимость, а на слом совместимости в плюсах на твоё место сотня недовольных прибежит. Да ты и сам жалуешься что

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

это противоречие, кстати, в тебе неопытность и выдаёт - легаси тебе плохо, нестабильность тебе плохо, тебе нужно просто чтобы было з**сь. А, ну иди сделай.

Плюс к этому, сложность C++ обусловлена исключительно историческими причинами и попытками впихнуть невпихуемое. Тот же Rust проще и при этом покрывает все требования к C++ без проблем

Так и есть, и в чём проблема писать на Rust?

anonymous
()

Я только-только начал входить в эти ваши «кресты», поэтому сильно хз. :⁠-⁠!

sparkie ★★★★★
()

Boost

Всё, туши свет, бросай гранату. Если подключаешь Boost, ты проиграл, в какую бы игру ты ни играл.

shdown ★★
()

Так это что, бросить C++ надо? Или не надо? Или не C++? Или не бросить, но надо? Памагитя, я запутался.

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

Никакой синхронизации в системе нет, если ты записал атрибут который меняет цвет, то покрасится всё и из этого потока, и из другого, и из вообще не связанного процесса который давно в фоне запущен и тут вдруг решил вякнуть. Исходя из этой данности, вывод в любом случае нужно как-то синхронизировать. На самом деле, ЕМНИП, гарантий нет в принципе, потому что write(2) в tty не гарантирует атомарности в общем случае, но для простоты ограничимся одним нашим процессом.

Так вот, вывод нужно синхронизировать, это можно делать из коробки или руками, у каждого варианта свои трейдоффы, потому что синхронизация не бесплатна, поэтому я бы не стал ставить выбор любого из этих путей в упрек языку, главное чтобы была возможность сделать так как надо в конкретном случае. В Rust stdio синхронизирован из коробки, в плюсах есть явный std::osyncstream для этого, если ты не знал.

anonymous
()

vector - ну, надо же было что-то делать. Не помню, когда bitset появился, если позже, то наркомания вполне объяснима. auto_ptr - херню спороли, согласен, но быстро исправились. Корутины - да, пиздец. Как я понимаю, вызванный тем, что работать оно должно на куче архитектур и вменяемо это не сделать. Концепты рулят. Тут претензия, какого хрена раньше не сделали. Но при создании темплейтов никто в здравом уме не мог представить, что их додумаются использовать для SFINAE.

Про неподчищенный треш разговаривать не о чем, там проблемы с реализацией, а не языком.

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

Собственно не вижу причин почему через 27 лет что-то примерно похожее не будет написано про rust

Анон, прекрати. Тут не про 27 лет, тут про то, что многие добавленные в C++ штуки являются говном примерно сразу. std::simd убог уже сегодня. std::async был сломан сразу, и это всем было ясно. Натянуть сборку мусора на C++ является тупой идеей изначально.

её добавили в стандарт, в расчёте на то, что писатели компиляторов реализуют битовый вектор более эффективно. пейсатели нешмогли и забили. чем тут виноват стандарт?

Я так предполагаю, что ты тот же анон. vector<bool> критикуют не за эффективность, а за то, что это не vector. Например, следующая функция будет работать для всех vector кроме vector<bool>:

template <typename T>
void find_and_modify(const T elem, std::function<void(T &)> f, std::vector<T> &v) {
    for(T& e : v)
        if(e == elem) f(e);
}

пейсатели нешмогли и забили. чем тут виноват стандарт?

Вообще, сейчас в стандарт без существующей хотя бы сколь-нибудь эффективной реализации тупо не принимают. Так что да, виноват стандарт.

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

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

stdout буферизируется построчно. В принципе, можно расчитывать, что всё до очередного ‘\n’ (или до fflush(stdout) / std::endl) будет выведено за раз.

в плюсах есть явный std::osyncstream для этого, если ты не знал.

(since C++20)

Ну ты понял. Можно добавить в список в оригинальном посте. Двадцать с лишним лет ушло у чуваков чтобы допереть.

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

Анон, прекрати. Тут не про 27 лет, тут про то, что многие добавленные в C++ штуки являются говном примерно сразу. std::simd убог уже сегодня. std::async был сломан сразу, и это всем было ясно. Натянуть сборку мусора на C++ является тупой идеей изначально.

Допустим что они действительно являются «говном» (но с аргументацией «убог уже сегодня», «сломан сразу», и «является тупой идеей» понятно что ты на них даже не смотрел, не говоря уже о почитать пропозалы на тему того почему было сделано именно так), ты думаешь в какой-то другой язык никогда не примут такое же «говно»?

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

понятно что ты на них даже не смотрел

лол

Про std::simd можешь почитать вот тут и посмотреть лулзовые тесты тут (github.com). Вкратце, библиотеку придумали больше 10 лет назад и больше 10 лет дрочили. За это время компиляторы сильно улучшили автовекторизацию и прочие ништяки, и библиотека оказалась тупо ненужной. Плюс, реализации SIMD типа как в ARM на эту библиотеку просто плохо ложатся.

В принципе, с остальными штуками аналогично: комитетчики дрочат годами, делают мало пригодное говно, а потом все страдают.

ты думаешь в какой-то другой язык никогда не примут такое же «говно»?

Ага. Нет комитета – нет проблем.

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

Никакой синхронизации в системе нет, если ты записал атрибут который меняет цвет, то покрасится всё и из этого потока, и из другого, и из вообще не связанного процесса который давно в фоне запущен и тут вдруг решил вякнуть. Исходя из этой данности, вывод в любом случае нужно как-то синхронизировать. На самом деле, ЕМНИП, гарантий нет в принципе, потому что write(2) в tty не гарантирует атомарности в общем случае, но для простоты ограничимся одним нашим процессом.

Ребят, вы про что вообще? В stdio на каждом FILE* мютекс, все операции (кроме *_unlocked) захватывают мютекс, делают что-то, потом отпускают его. Этого требует POSIX.

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

Ребят, вы про что вообще? В stdio на каждом FILE* мютекс, все операции (кроме *_unlocked) захватывают мютекс, делают что-то, потом отпускают его. Этого требует POSIX.

Мы про C++. iostream не является обёрткой вокруг FILE*.

yorshka
() автор топика
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.