Новости РГ21 — группы по стандартизации C++
Всем добрый день!
Надеюсь, вы хорошо покушали.
Меня зовут Антон Полухин и я люблю C++.
Занимаю стандартными библиотеками, автор многих библиотек-буста.
Представляю Россию в Международном комитете по стандартизации этого замечательного языка программирования.
Кстати о стандартизации.
Недавно прошла встреча международного комитета и там добавили в «Черновик C++26» новые фишеньки.
О них-то мы и поговорим сегодня.
И начнем с вот этой штуки.
C++26 он будет одним из самых надежных C++, что был для этого, потому что комитет активно занялся убиранием undefined behavior неопределенного поведения и стандарта.
И на недавнем
собрание и комитета, как раз еще один DefineBehavior убрали.
Например, до C++26 можно было вызвать delete на forward за декларенном типе, delete incomplete type.
Компиляторы при этом, конечно, давали в ворнинге, пожалуйста, не делайте так, это страшно, вы, скорее всего, делаете что-то не то, но код можно было скомпилировать.
C++26 компиляторы говорят, нет, это ошибка, так делать нельзя, а если вы раньше на такое поведение полагались, так делали, не делаете так больше.
В итоге код становится чуть более надежным.
Также добавили и симптоксического сахара.
Вот такая вот креативная конструкция.
IF, а внутри нее Structured Binding.
Как эта конструкция работает?
Рассмотрим на примере TuChars.
Написали вот эту страшную конструкцию.
Что произойдет в начале?
В начале будет вызов 2chars.
2chars вернет результат.
Он вернет 2chars result.
Объект типа 2chars result.
У этого объекта есть оператор pull.
Он может к pull'у превратиться.
Этот оператор зовется внутри IFA, и если этот оператор был вернул true, тогда уже происходит Structured Binding.
Результат разделяется на части и заходит внутрь IFA.
Если оператор вернул false, тогда Structured Binding вообще не выполняется, внутрь IFA не заходит, красота.
Продолжаем необычные новинки.
std optional теперь контейнер на 0 или 1 элемент.
В STD Optional добавили методы begin and end и aliасы для итераторов.
Зачем, казалось бы, так делать?
А ответ следующий.
Для range.
Было обсуждение, и в range очень хотели добавить STD Views Maybe.
в юхо, которое либо возвращает одно значение, либо ничего не возвращает.
Обсуждали эту идею.
Обсуждали.
И в какой-то момент люди в комитете заметили, что как-то это maybe очень похож на optional.
Нужно только к нему begin and добавить.
Добавили.
И теперь можно optional-ом пользоваться следующим образом.
Вот есть у нас какой-то контейнер с мобильными телефонами.
на им наваньями мобильных телефонов.
У этого мобильного телефона есть метод GetVendorOption.
Его для каждого элемента из фонс и зовём.
GetVendorOption возвращает из туда optional от строки.
То есть после вызова вот этого трансформ у нас будут выдаваться Optionals один за другим.
Если общину трактовать как контейнер, как диапазон с нулем или одним значением, то можно после этого позвать Views и вот этот вот массив из диапазонов склеить в один диапазон.
После этого, если мы возьмем это и запихнем в STD Anorder Set, то мы получим Anorder Set с уникальными именами всех вендеров.
Поехали дальше.
Переходим к более практичным вещам, а именно, наконец-таки, добавили в Standard Container In-Place Vector.
Это контейнер, который очень похож на STD-вектор, но он не аллоцирует динамический память под свои нужды.
Вместо этого вторым шаблонным параметром передается максимальное количество элементов, который может этот in-place-вектор хранить.
А дальше у него интерфейс аналогичный STD-вектор.
Можно вызывать push-back, pop-back, push-front.
Все вот это вот и рейзы.
Но есть нюанс.
вектор очень популярный контейнер для имбеда разработки и для разработки внутри различных там кядер операционной системы.
А в этих
окружениях не очень принято пользоваться исключениями.
Поэтому inPlaceVector расширили дополнительными методами try что-то, которые сообщают об ошибке не через выкидывание исключения, а через возврат null pointer.
То есть если вы вызовете try pushback на контейнере, который уже полностью заполнен и у него больше нет места, чтобы вставить новый элемент, то этот try pushback вернет вам назад nullptr.
Также для любителей выжить максимум производительности из приложений, ну то есть для нас, добавили методы Unchecked.
Они при вставке не проверяют, что в контейнере есть еще свободное место.
Соответственно, нам самим разработчикам надо проверить, что контейнеры действительно есть свободное место и то, что туда можно вставлять.
Далее продолжим про ускорение.
Ускорили STD-принт.
А кто знает, что делает STD-принт?
Слышали уже о нем?
Поднимайте руки.
Хорошо, смотрите.
STD-принт – это штука, которая под капотом фактически вызывает STD-формат, создает строчку, и после чего эту строчку куда-нибудь в поток или в файл звездочка запихивает.
И автор библиотеки fmt-формат, который принял стандарт std-формат, предложил следующую оптимизацию.
Добавить переменную enable non-locking-форматор optimization.
Если эту шаблонную переменную специализировать в true,
для своего типа, в стандартной библиотеке, она для всех типов уже специализирована в тру.
Тогда Std-принд вместо того, чтобы звать формат, который создаст отдельную строчку, вместо этого принд начинает работать чуть-чуть по-другому.
Он берет этот файл звездочка, который ему передали на вход.
И внутри него уже есть буфер, из которого файл читает и выводит куда-то на консоль либо файл.
Соответственно, принт берет, залочивает этот буфер, делает то же самое, что делает файл, когда в него что-то пишет.
И формат начинает форматировать сразу в этот буфер внутри стрима.
Дальше нужный буфер разлочивается и происходит обычная магия вывода куда-то на экран, в консоль, в файл.
Соответственно, такая оптимизация позволяет ускорить работу STD-принта на 20-200% в зависимости от операционной системы и платформы.
Кто знает, почему нужна отдельная шаблонная переменная?
Можно ли такую оптимизацию делать или нет?
Кого есть идеи?
Да?
Я до конца не услышал, но расскажу тогда правильный ответ.
Файл в нем есть мьютекс, который защищает буфер и некоторые внутренние части.
И этот мьютекс не рекурсивный.
То есть если вы сделали свой форматор, внутри которого опять вызываете STD-формат, у вас получится deadlock при попытке вызвать принты.
И все сломается и будет плохо.
И вот эта шаблонная переменная как раз нужна для того, чтобы вы сообщили стандартные библиотеки, что вы знаете, что делаете, и формат внутри формата звать не будете, и можно спокойно один раз взять и залочить этот мьютекс.
Далее.
Как известно, в стандартной библиотеке достаточно много генераторов псевдослучайных чисел.
Очень много.
На любой вкус.
Ну вот засада.
Тей генераторы все случайных чисел, что есть стандартные библиотеки, не всех устраивают.
В основном они не очень хорошо подходят для математического метода «Монте Карло», то есть то, чем пользуются математики, физики, химики.
Достаточно простой.
У вас есть генератор тех, кто случайно чисел.
Он генерирует чиселку.
Вы эту чиселку пропускаете через какую-то модель, получаете результат, делаете это 100 тысяч миллиардов раз, и в итоге на полученных результатах можете делать какие-то выводы.
В чем тут засада?
Если мы берем простой и быстрый генератор всех случайных чисел, очень быстро диапазон его выдаваемых значений заканчивается.
Монта-карло зачастую вызывается на огромных кластерах, где там тысячи сотни тысяч машин с сотними ядер.
И если этот метод раскидать, генератор один и тот же использовать, у вас быстро начинает повторяться числа и точность падает, все становится плохо.
Если берем
крутой, сложный генератор псевдослучайных чисел, какой-нибудь мерсен-твистер, тогда да, тогда диапазон не повторяется.
Ну, во-первых, он долго считается, долго производит новое случайное число, а во-вторых, его весьма сложно инициализировать и сложно распоролеливать.
То есть, вот у вас миллион кластеров,
Как бы так Монте Карло проиненциализировать, чтобы последовательности на разных кластерах точно не повторялись?
Сложно.
Именно поэтому стандарт добавили новый генератор псевдот случайных чисел, который называется Филокс, и у него есть пара прикольных моментов.
Во-первых, у него достаточно дешевый конструктор.
Можно его брать, создавать внутри цикла, каким-нибудь сидом инициализировать.
А во-вторых,
Все его состояние можно задать буквально четырьмя интеджерами.
И в стандарте есть гарантия, что вот если вы проиненциализировали там первые два интеджера, то число, которое выдаёт этот генератор псевдослучайных чисел, вот это последовательность, она не будет повторяться два там в сорок какой-то степени.
В стандарте точно написано формула.
Если вы занимаетесь методом Монта Карлова, вам точно стоит посмотреть.
И соответственно,
Что мы получаем?
Генератор псевослучайных чисел, который быстро можно создать, состояние у которого достаточно маленькое, его легко проиненциализировать, и есть гарантия на то, что он выдает не пересекающиеся диапазоны при правильном взведении этих счетчиков.
Все, идеально.
Теперь его легко можно раскинуть на тысячи кластеров.
Есть гарантия, что он не повторяется и то, что он работает быстро.
И все, и поехали.
А теперь большие фишки.
C++26 приняли экзекьютеры.
Но есть нюанс.
О нем чуть позже.
Для начала, как выглядит работа с экзекьютерами?
Вот у вас есть какой-то третпул, он может предоставлять шедулир.
Что-то, на чем можно выполнять запросы.
Берем этот шедуляр, а далее нам нужно построить цепочку каких-то вычислений.
Пока мы ее строим, цепочку вычислений не запускается.
Вот мы говорим то, что начинаем строить нашу цепочку вычислений.
Первым шагом нужно взять и в консольку вывести Hello World Havenint и вернуть чиселку 13.
Следующим шагом нужно к циферке с прошлого шага добавить 42 и вернуть эту циферку.
построили цепочку вычислений, а уже дальше говорим то, что все, давай машина выполняй ее, и цепочку вычислений начинает выполняться.
Казалось бы, все хорошо, но есть нюанс.
Во-первых, все, что возвращается методами из STD execution, это суровые шаблонные классы, у которых нету базового
классы, нет виртуальных функций, что хорошо, потому что оно работает быстро.
С другой стороны, это плохо, потому что все, что есть в описании этих объектов, это концепты, которые они удовлетворяют.
А плохо это, потому что если вы разрабатываете какую-то библиотеку, какой-то фреймворк, вот вы сделали свою базу данных, хотите клиентскую библиотеку дать людям, типа пользуетесь, клиентская библиотека у вас написана круто, эффективно, синхронно, ей нужен какой-то шедулер, экзекьютор, на котором выполнять эти колбеки синхронные.
А единого класса для этого нету.
То есть нельзя сейчас взять и всем библиотекам выдать единый класс экзекьютора, чтобы его использовать, и один экзекьютор между всеми библиотеками шарить.
Неудобно.
Другой минус суровия.
Экзекьюторы, которые есть в стандарте, это очень низкоуровневая вещь.
Вот так, например, выглядит получение файла по сети, где мы вначале читаем размер файла из сети, а потом полученный размер пытаемся опять-таки считать сети.
Здесь все проблемы о синхронного программирования.
Колбеки, которые что-то захватывают, соответственно, нужно внимательно следить за лайфтаймами, нужно эффективно передавать эти захваты, где-то по ссылке, где-то следить, чтобы объект, на который ссылаемся, был жив в течение всего этого времени.
Партянка из колбеков поддерживать это не очень просто и легко.
Соответственно,
Мой вам совет, если вы хотите попробовать экзекьюторы,
Пожалуйста, пробуйте.
Это весело.
С другой стороны, если вы хотите писать какой-то продакшн-код, вряд ли ребята по команде и девчата по команде будут рады вот такому вот страшному синтаксису и коду.
Так что если хотите более готовое бизнес-решение, уже более высокоурованное, то у меня есть для вас предложение использовать у серого точка тех.
Там это все есть из коробки и попроще.
Ну а далее, что у нас есть что интересного?
Reflection для C++26 прошла этап Design Review и сейчас остается этап, когда финальные слова уже выверяются и подключаются к стандарту.
Все шансы на то, что Reflection будет в C++26.
Также.
Моя любимая фишечка, возможность агрегат или картеж разбить на составные части, не зная на самом деле сколько внутри него полей.
Вот это дошло до финального голосования.
Все сказали, да, клевая штука, но примем стандарт, когда будет прототип в каком-нибудь из компиляторов, желательно не в одном.
Так что, скорее всего, на следующем заседании или через него возьмут и примут стандарт и это.
А еще.
Есть контракты.
Не то, чтобы они уже готовы к принятию в СПЛЦ 26, но шансы увеличиваются.
То есть на последнем заседании вопросов по дизайну контрактов стало меньше.
Людей, которых контракты устраивают, стало больше.
Конценсус увеличивается.
контрактом люди привыкают.
Они начинают понимать, что и как с помощью них можно сделать, что получить.
И есть шансы увидеть и контракты в C++26.
А на этом у меня, пожалуй, всё самое время для вопросов.
Так, Антон, спасибо.
Значит, вижу руки.
Давайте так.
Один вопрос оттуда, один вопрос отсюда.
Сюда сразу, пожалуйста, микрофон принесите.
Так.
Нет, вот в этому человеку.
Да.
А если у нас есть второй микрофон, его, пожалуйста, вот этому сразу, чтобы мы время не теряли.
Давайте, человек с микрофоном.
Антон, большое спасибо за доклад.
Активно их слушаю.
Слушал предыдущий на C++ Rasha.
Все, как всегда круто.
Но вот не хватает все-таки чего-то про Тулинг в стандарте.
Как с этим сейчас дела идут?
С Тулингом в стандарте неожиданно пошло неплохо.
Подгруппа Тулинга пришла к идее, что давайте мы не будем стандартизировать какой-то конкретный инструмент сборки или конкретный там пакетный менеджер.
Вместо этого стандартизируем описание зависимости и описание сборки.
И сейчас у них идет работа фактически по
стандартизации по принятию этого единого способа описания зависимости и сборки.
То есть там сейчас хорошо пошло, задвигалось.
Но в C++26 мы это точно не увидим.
Есть шансы на C++29.
Живем, увидим.
Спасибо.
Так, хорошо, спасибо.
А, да, вот, отлично.
Антон, спасибо большое за доклад, Владимир.
Ты говорил в том числе про In-Place Vector, по-моему.
То есть вот, как пришли плюсы, мы ушли от сишных массивов, сделали SD Vector, потом решили еще и SDRA сделать, чтобы более, так, лучше по сравнению с сишными массивами работать.
Зачем вот этот третий, это рация, третий подход к снаряду?
Спасибо.
STD Ray он сразу инициализирует все элементы и как бы вот.
Они все проинциализированы, а возможно вам это и не нужно.
Inplace Vector это отчасти Ray, но он не сразу все инициализирует, не для всего вызывает конструкторы.
И он помнит сколько элементов он хранит.
То есть бывают такие случаи, когда вы знаете верхнюю границу, сколько элементов может быть в вашем векторе максимально.
Но это не знает, что там всегда столько.
Там может быть меньше, там может быть 0 элементов.
И для этих случаев как раз подходит in-place vector.
То есть вы знаете, что у вас тут 16 элементов максимум, сделали in-place vector.
Посчитали что-то, что вам нужно, запихнули туда значение, еще что-то посчитали, выкинули In-Place Vector.
Избежали динамической аллокации и избежали вызовов конструкторов для тех элементов, которые вам не нужны.
Спасибо.
Кстати, он в доле поперек весь концтекспор.
Я прям рад.
Так, сейчас два вопроса из онлайна.
И если успеем, еще вот, человек там руку тянет, мы после онлайна.
Не-не-не, вон там вот вот чек в красном, кажется.
Так, пока ему несут микрофон.
Вопрос был про то же in-place-вектор, про try pushback и вот подобные штуки.
Да, почему он возвращает сестайл null.ptr, а не std-expected?
Мммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммммм
только одна, то либо возвращать optional, либо указатель, то есть pointer.
Нет, достаточно логичное решение на мой взгляд.
Отлично.
И еще один вопрос из онлайна про генератор случайных чисел.
А что с повторяемостью РНГ генератора?
Тем, кому нужна повторяемость результатов, например, геймдева, все еще нужно либо лочить версию toolchain, либо использовать
какой-то сторонний генератор.
Не было ли предложений стандартизировать условной, могу неправильно прочитать, хоро-широ, как есть?
Стандартизировали филокс, он повторяемый, то есть если вы задаете сит и каунтеры.
На одной машине и на другой машине он будет выдавать одинаковые последовательности на обоих машинах.
То есть он повторяемый.
Здесь все хорошо.
Формулы жестко описаны.
Более того, там в стандарте написано достаточно интересно что-то типа...
Должен работать вот по такой-то формуле.
В качестве проверки смотрите.
Тысячный элемент, который выдаёт этот генератор на таких-то значениях должен быть такой-то.
То есть это гарантированно зашито повторяемость.
Супер.
Так, и у нас остался как раз последний вопрос.
Вот там должен быть человек с микрофоном.
Здравствуйте, Антон.
А вы не подскажете, есть ли вообще какое-нибудь будущее у предложений типа Herb-ception?
То есть от Herb-satter было предложение по... Ну, вы знаете, да.
Фактически, предложение satter это expected просто чуть-чуть затащенный на уровень языка и обмазанный синтаксическим сахаром.
Проблема там началась в имплементации.
Несмотря на то, что говорили, что это более быстрое исключение, выяснилось, что нет, что это как коды возврата.
И даже если сильно подсказывать компилятору и оптимизировать в этом месте, то все те же проблемы кодов возврата остаются.
Если вы используете такие эксцепшены на горячем пути, они тормозят сильнее, чем обычный эксцепшен.
если не кидать, ни те, ни другие.
Если их кидать, то, разумеется, что стандартные имеющиеся эксцепшен, они там и память элацируют, и по стеку ходят, и стек-трейс могут собирать.
Они медленнее, но для...
Комитет не очень хочет делать вот два инструмента сообщения об ошибок на уровне языка, которые называются exception и очень похожа, так что пока что предложение на паузе и все переключились на рефлексию, а дальше метоклассы.
Понятно, спасибо.
Давайте поблагодарим Антона.
Спасибо большое.