С++26
#опытным
Ну что, мы все этого ждали 3 года и наконец оно случилось. Тут намедни утвердили С++26!
Подробно нововведения будем разбирать в будущих постах, но в этот раз давайте просто перечислим основные мажорные просто прикольные фичи нового стандарта. Поехали:
🔥 Триграфный оператор утверждения
С++17 триграфы уничтожил, С++26 их вернул. "Компьютерные войны. Часть С++26. Месть триграфов".
🔥 Наконец-то в стандарт добавили тредпул! Класс std::thread_pool, который принимает количество потоков, политику планирования выполнения задач и размер очереди. Политики исполнения могут быть std::thread_pool::scheduling_policy::fifo и std::thread_pool::scheduling_policy::priority, а размер задается чиселкой, но по умолчанию очередь неограничена std::thread_pool::unbounded:
🔥 Добавлена поддержка рефлексии! Еще один пункт, которого все ждали. Рефлексия позволяющей отслеживать и модифицировать элементы программы на стадии компиляции. Добавлен новый оператор (|.|) для получения метаинформации о грамматической конструкции. Для преобразования и обработки полученной в ходе инспектирования информации предложена библиотека std::meta и доступны такие возможности, как вычисления с константами.
Кстати, отпишитесь в комментах, на что похож новый оператор. Что-то напоминает, но никак не могу конкретно сказать.
🔥 Предложена реализация массива переменного размера std::inplace_vector, размещаемого в стеке. И у него вообще ничем не ограничен размер!. API близок к std::vector, но элементы массива хранятся не на куче, а внутри объекта.
И вообще никаких аллокаций!
🔥 Внесены изменения для усиления безопасности стандартной библиотеки, такие как проверки допустимых значений и выхода за границы буфера. Например, при доступе к элементу "reference operator[](size_type idx) const;" добавляется проверка условия "idx < size()". И мякотка: проверка даже рантаймовых контейнеров происходит на этапе компиляции!
🔥 Добавлен атрибут
Такой код и компилятор может не добавлять в бинарник, если докажет, что код реально не используется нигде.
🔥 В стандарт добавили поддержку линейной алгебры! Появились матрицы, их можно умножать, получить вектор из ее диагонали, посчитать определитель, изменить форму матрицы и тд. Куча всего на самом деле.
PS Надеюсь, что вы попались на первоапрельскую шутку) По этой ссылочке можно посмотреть реальные нововведения.
Don't be fooled. Stay cool
#cpp26 #fun
#опытным
Ну что, мы все этого ждали 3 года и наконец оно случилось. Тут намедни утвердили С++26!
Подробно нововведения будем разбирать в будущих постах, но в этот раз давайте просто перечислим основные мажорные просто прикольные фичи нового стандарта. Поехали:
🔥 Триграфный оператор утверждения
??! - возвращает true, если выражение может быть скомпилировано, иначе false.template<typename T, typename U>
constexpr void can_add(T&& t, U&& u) {
if constexpr (x + s ??!) {
std::cout << "Addition can be performed!\n";
} else {
std::cout << "Addition can not be performed\n";
}
}
С++17 триграфы уничтожил, С++26 их вернул. "Компьютерные войны. Часть С++26. Месть триграфов".
🔥 Наконец-то в стандарт добавили тредпул! Класс std::thread_pool, который принимает количество потоков, политику планирования выполнения задач и размер очереди. Политики исполнения могут быть std::thread_pool::scheduling_policy::fifo и std::thread_pool::scheduling_policy::priority, а размер задается чиселкой, но по умолчанию очередь неограничена std::thread_pool::unbounded:
std::thread_pool pool(4, std::thread_pool::scheduling_policy::priority);
pool.enqueue([] {
std::cout << "Critical task!\n";
}, std::thread_pool::priority::critical);
pool.enqueue([] {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
std::cout << "Normal task\n";
}, std::thread_pool::priority::normal);
🔥 Добавлена поддержка рефлексии! Еще один пункт, которого все ждали. Рефлексия позволяющей отслеживать и модифицировать элементы программы на стадии компиляции. Добавлен новый оператор (|.|) для получения метаинформации о грамматической конструкции. Для преобразования и обработки полученной в ходе инспектирования информации предложена библиотека std::meta и доступны такие возможности, как вычисления с константами.
constexpr int i = 42, j = 42;
static_assert((|.|)i == (|.|)j); // check if i and j values are equal in compile time
Кстати, отпишитесь в комментах, на что похож новый оператор. Что-то напоминает, но никак не могу конкретно сказать.
🔥 Предложена реализация массива переменного размера std::inplace_vector, размещаемого в стеке. И у него вообще ничем не ограничен размер!. API близок к std::vector, но элементы массива хранятся не на куче, а внутри объекта.
std::inplace_vector vec;
for (int i = 0; i < 10000; i++) {
vec.push_back(i);
}
И вообще никаких аллокаций!
🔥 Внесены изменения для усиления безопасности стандартной библиотеки, такие как проверки допустимых значений и выхода за границы буфера. Например, при доступе к элементу "reference operator[](size_type idx) const;" добавляется проверка условия "idx < size()". И мякотка: проверка даже рантаймовых контейнеров происходит на этапе компиляции!
🔥 Добавлен атрибут
[[maybe_unused]] для блока кода. Бывает начинаете писать какой-то сервис и тут бац! бизнес требования поменялись и допинывание сервиса до прода ушло глубоко в конец бэклога. Чтобы новые сотрудники не гадали, почему код есть, но ничего не работает, очень полезно пометить его атрибутом maybe_unused:[maybe_unused]] {
// potentially unused code
}Такой код и компилятор может не добавлять в бинарник, если докажет, что код реально не используется нигде.
🔥 В стандарт добавили поддержку линейной алгебры! Появились матрицы, их можно умножать, получить вектор из ее диагонали, посчитать определитель, изменить форму матрицы и тд. Куча всего на самом деле.
std::linalg::matrix<double> A(3, 3);
A(0,0) = 1; A(0,1) = 2; A(0,2) = 3;
A(1,0) = 4; A(1,1) = 5; A(1,2) = 6;
A(2,0) = 7; A(2,1) = 8; A(2,2) = 9;
auto A2 = std::linalg::mul(A, A);
auto diag = std::linalg::diag(A); // std::vector<double> {1, 5, 9}
double det = std::linalg::det(A); // 0
auto flat = std::linalg::reshape(A, 1, 9);
PS Надеюсь, что вы попались на первоапрельскую шутку) По этой ссылочке можно посмотреть реальные нововведения.
Don't be fooled. Stay cool
#cpp26 #fun
❤55👍19😁19🔥15🤣5👎1🤨1👀1
Такие разные векторы
#опытным
Про std::vector сказано многое, но он не перестает удивлять.
Вектор владеет регионом динамической памяти и ему надо трекать, где заканчиваются текущие элементы и где в принципе заканчивается регион.
Наивное представление, которое напишет примерно любой джун:
Понятно, что в реальности был бы еще аллокатор, тип указателя был бы зависимым типом аллокатора и скорее всего был бы
Второй вариант - 3 указателя. Начало, конец текущих данных и конец всего стораджа.
И именно последний вариант реализован например в GCC.
У каждого варианта свои плюсы и минусы:
- с помощью 3-х указателей тяжело считать size(), но легко итерироваться и вычислять end().
- указатель и 2 размера легко считают size(), но у них тяжеловато с end().
И в связи с тем, что в С++26 завезли харденинг(рантайм проверки на соблюдение контрактов стандартной библиотеки(например выход за границы массива)) ситуация приобретает интересный поворот.
Харденинг неявно увеличивает количество вызовов метода size(). Поэтому становится выгоднее использовать "наивную" реализацию через указатель и 2 числа.
Ребята из гугла проверили это на своем софте(у них харденинг проверки уже давно реализованы) и получили буст вплоть до 0.6% перфа на количество обработанных запросов в секунду! Не самого вектора, а самих сервисов.
Вот статейка, кому интересно. Крутая статья, кстати. Там в деталях расписано, как внутри std:vector функционирует и еще много всякой полезнятины.
Measure your performance. Stay cool.
#STL #optimization #cpp26
#опытным
Про std::vector сказано многое, но он не перестает удивлять.
Вектор владеет регионом динамической памяти и ему надо трекать, где заканчиваются текущие элементы и где в принципе заканчивается регион.
Наивное представление, которое напишет примерно любой джун:
template<typename T>
class Vector {
...
T * begin;
size_t size;
size_t capacity;
};
Понятно, что в реальности был бы еще аллокатор, тип указателя был бы зависимым типом аллокатора и скорее всего был бы
char *, но смысл один: есть указатель на начало и 2 размера: текущее количество элементов и максимально возможное без переаллокаций.Второй вариант - 3 указателя. Начало, конец текущих данных и конец всего стораджа.
template<typename T>
class Vector {
...
T * begin;
T * end;
T * end_of_storage;
};
И именно последний вариант реализован например в GCC.
У каждого варианта свои плюсы и минусы:
- с помощью 3-х указателей тяжело считать size(), но легко итерироваться и вычислять end().
- указатель и 2 размера легко считают size(), но у них тяжеловато с end().
И в связи с тем, что в С++26 завезли харденинг(рантайм проверки на соблюдение контрактов стандартной библиотеки(например выход за границы массива)) ситуация приобретает интересный поворот.
Харденинг неявно увеличивает количество вызовов метода size(). Поэтому становится выгоднее использовать "наивную" реализацию через указатель и 2 числа.
Ребята из гугла проверили это на своем софте(у них харденинг проверки уже давно реализованы) и получили буст вплоть до 0.6% перфа на количество обработанных запросов в секунду! Не самого вектора, а самих сервисов.
Вот статейка, кому интересно. Крутая статья, кстати. Там в деталях расписано, как внутри std:vector функционирует и еще много всякой полезнятины.
Measure your performance. Stay cool.
#STL #optimization #cpp26
❤27👍8🔥7🤔1
Hardening
#опытным
Раз уж в прошлом посте заикнулись про hardening, давайте разберем его чуть подробнее.
Неопределенное поведение (UB) в C++ - самая ужасная категория ошибок. UB может бесшумно повреждать память, вызывать сбои в местах очень отдаленных от фактической ошибки, или, что хуже всего, просто работать на вашей машине долгое время без спецэффектов. Значительная доля UB в реальных кодовых базах происходит не от экзотических языковых функций, а от базового неправильного использования стандартной библиотеки: доступа к вектору за пределами границ, вызова front() на пустом контейнере или вызова метода на пустом std::optional.
C++26 частично решает эту проблему напрямую с помощью харденинга стандартной библиотеки
Что это за зверь?
Харденинг библиотеки преобразует определённое неопределённое поведение в стандартной библиотеке в обнаруживаемые нарушения контрактов во время выполнения. Когда нарушается харденизированное предусловие, среда выполнения реагирует до того, как произойдут какие-либо другие наблюдаемые побочные эффекты.
То есть, раньше вся ответственность за корректное использование методов ложилось на плечи программистов. Допускаешь доступ за границы массива - жди беды, тебе о ней компилятор и рантайм не сообщат.
Теперь же реализации стандартной библиотеки вставлять специальные проверки, неудовлетворение которой ведет к предсказуемому завершению программы. И вроде как даже предоставляются инструменты для понимания, где произошла "паника".
Это не новая идея. Все три основные реализации стандартной библиотеки уже поставляют свои собственные режимы харденинга, зависящие от конкретного вендора. Проблема в том, что эти механизмы различны, непереносимы и не имеют единой спецификации. Теперь это дело стандартизировано.
Примеры:
Сильнейший аргумент в пользу этой включения харденинга — производственный опыт Google, на который ссылается пропоузал: применение харденизированного libc++ в «сотнях миллионов строк C++» выявило более 1000 ошибок, включая критически важные для безопасности. Средние накладные расходы на производительность оказались удивительно низкими — 0,30%(одна треть процента). Эти накладные расходы остались такими низкими благодаря способности компилятора устранять избыточные проверки во время оптимизации.
Влияние вышло за рамки безопасности: команды наблюдали 30%‑е снижение базового уровня сегментационных ошибок (segfault) в продуктивной среде, что указывает на повышение корректности кода в целом.
У gcc и msvc сейчас только частично поддержан hardening, но относительно скоро все мы сможемпотратить год на исправление всех найденных уязвимостей насладиться более безопасным кодом.
Be safe. Stay cool.
#cpp26 #compiler
#опытным
Раз уж в прошлом посте заикнулись про hardening, давайте разберем его чуть подробнее.
Неопределенное поведение (UB) в C++ - самая ужасная категория ошибок. UB может бесшумно повреждать память, вызывать сбои в местах очень отдаленных от фактической ошибки, или, что хуже всего, просто работать на вашей машине долгое время без спецэффектов. Значительная доля UB в реальных кодовых базах происходит не от экзотических языковых функций, а от базового неправильного использования стандартной библиотеки: доступа к вектору за пределами границ, вызова front() на пустом контейнере или вызова метода на пустом std::optional.
C++26 частично решает эту проблему напрямую с помощью харденинга стандартной библиотеки
Что это за зверь?
Харденинг библиотеки преобразует определённое неопределённое поведение в стандартной библиотеке в обнаруживаемые нарушения контрактов во время выполнения. Когда нарушается харденизированное предусловие, среда выполнения реагирует до того, как произойдут какие-либо другие наблюдаемые побочные эффекты.
То есть, раньше вся ответственность за корректное использование методов ложилось на плечи программистов. Допускаешь доступ за границы массива - жди беды, тебе о ней компилятор и рантайм не сообщат.
Теперь же реализации стандартной библиотеки вставлять специальные проверки, неудовлетворение которой ведет к предсказуемому завершению программы. И вроде как даже предоставляются инструменты для понимания, где произошла "паника".
Это не новая идея. Все три основные реализации стандартной библиотеки уже поставляют свои собственные режимы харденинга, зависящие от конкретного вендора. Проблема в том, что эти механизмы различны, непереносимы и не имеют единой спецификации. Теперь это дело стандартизировано.
Примеры:
std::vector<int> v = {1, 2, 3};
// нарушение контракта: 5 >= 3
int x = v[5];
v.pop_back();
v.pop_back();
v.pop_back();
// нарушение контракта: нельзя убрать элемент из пустого вектора
v.pop_back();
std::string_view sv("hello");
// нарушение контракта: 10 >= 5
char c = sv[10];
// нарушение контракта: 10 > 5
sv.remove_prefix(10);
std::optional<int> opt;
// нарушение контракта: нет реального объекта
int x = *opt;
int data[10];
// нарушение контракта: разное число элементов
// в шаблонном параметре и в аргументе конструктора
std::span<int, 5> sp(data, 3);
// нарушение контракта: 10 > size()
sp.first<10>();Сильнейший аргумент в пользу этой включения харденинга — производственный опыт Google, на который ссылается пропоузал: применение харденизированного libc++ в «сотнях миллионов строк C++» выявило более 1000 ошибок, включая критически важные для безопасности. Средние накладные расходы на производительность оказались удивительно низкими — 0,30%(одна треть процента). Эти накладные расходы остались такими низкими благодаря способности компилятора устранять избыточные проверки во время оптимизации.
Влияние вышло за рамки безопасности: команды наблюдали 30%‑е снижение базового уровня сегментационных ошибок (segfault) в продуктивной среде, что указывает на повышение корректности кода в целом.
У gcc и msvc сейчас только частично поддержан hardening, но относительно скоро все мы сможем
Be safe. Stay cool.
#cpp26 #compiler
❤30🔥11👍7