LINUX.ORG.RU

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

 , , ,


2

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
() автор топика

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

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

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

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

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

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

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

Несмотря на то, что переменная 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 ★★★★★
()
Ответ на: комментарий от AlexVR

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

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


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

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

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

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