Comments 61
В C++23 добавили новый интерфейс, и теперь аллокаторы могут сообщать вам, под какое количество элементов была выделена память на самом деле.а что насчет realloc'а в аллокаторах?
В C++23 провели работу над ошибками и добавили более могущественный инструмент if consteval:блин 10 лет и куча новых слов чтобы исправить то, что сделали constexpr функции вместо constexpr аргументов этих функций...
На подходе std::ranges::to, который позволит произвольный диапазон сохранять в произвольный контейнер:круто, полезно. А как решили проблему с вычислением capacity контейнера для не-sized range?
В P2186R2 убрали поддержку Grabage Collector, потому что в таком виде им всё равно никто не пользовался, он просто добавлял UB.комитет с++ вынес вердикт что GC не нужен
И наконец, приятный багфикс от Яндекса, запрещающий конструирование std::string_view и std::string от nullptr, предложение P2166R1. Ура, больше ошибок будет отлавливаться на этапе компиляции!это может сломать компиляцию многих проектов…
Эта идея прорабатывалась в github.com/cpp-ru/ideas/issues/28, но автор несколько подзатих.
> это может сломать компиляцию многих проектов…
В этом и смысл статических проверок — не допускать кода, который гарантированно не работает и роняет приложение в рантаймме
Нужен, но в том виде что был в стандарте он бессмысленен и не помогает существующим GC для C++. Там даже список сужествующих GC есть в предложении, с ссылками на статьи
круто, полезно. А как решили проблему с вычислением capacity контейнера для не-sized range?
struct count_size_fn
{
template <ranges::range Range>
auto operator ()(Range &&rng) const -> std::size_t
{
if constexpr (ranges::sized_range<Range>)
return ranges::size(std::forward<Range>(rng));
else
return ranges::count_if(std::forward<Range>(rng), [](auto const &){ return true; });
}
};
// useful for non sized ranges
constexpr count_size_fn count_size;
Понятно, что это хуже, чем O(1).
Можно еще извернуться, ввести в дополнение к sized_range еще условный bound_range, который вместо size() возвращает max_size(). Например, мы знаем что str | filter(isalnum) гарантированно вернет не более чем str.size() букв. Но и в этом случае резервировать память под тысячи элементов, когда нужны единицы, будет глуповато.
это даже не будет работать с input ranges — которые можно прочитать лишь единожды.
Естественно, просто невозможно узнать его размер, если он не сам не предоставит его.
Такой подход сработает для bidirectional rangesещё forward ranges
и то за линиювообще не понял
если там нигде мува нет
Было бы странно, если было бы по другому. Если вы написали std::move(range), то чего вы ожидаете?
bound_rangeвозможно, но, как вы и отметили, имеет ограниченное применение.
По моему опыту портирования с ranges.v2 -> ranges.v3 мне потребовалась только эта функция, поскольку концепт forward range перестал требовать size().
Я перечитал ваш исходный вопрос и понял, что мы говорим немного о разных вещах. Я никогда не слышал о «проблеме capacity контейнера для не sized range» и, мне кажется, здесь нет никакой проблемы. В общем случае случае задача получения size() не может быть решена, иначе все range были бы sized.
Выглядит интересно. Но что насчёт основных ожидаемых фич? Экзекуторы и нетворкинг всё же успевают к С++23? На рефлексию, я так понял, надеяться смысла нет.
Также не очень понятно, в чём преимущество spanstream перед обычными stringstream(может, стоило бы прочитать пропозал, но не прочитал пока).
spanstream не копирует буфер. Полезно, например, если вы получаете по сети большие объёмы данных в какой-нибудь
std::vector<std::byte>
, а потом разбираете его на части с помощью iostreams.Простите, но какая-то ерунда. Где рефлексия? Где нормальный void?
Всё в разработке.
Хорошая новость — вы можете помочь комитету:
* Если умеете разрабатывать компиляторы — помогите в реализации прототипов core language идей
* Можете взять TSы, поэкспериментировать с ними, найти проблемы и предложить способы их решения (та же рефлексия выходит в виде TS github.com/cplusplus/reflection-ts )
* Можете стать спонсором комитета isocpp.org/about/membership, чтобы комитету было проще организовывать встречи и закупать переходники/проекторы/стулья/хостинг
* Наконец, можете взяться за написание прототипов и предложений для популярных идей github.com/cpp-ru/ideas/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22
Резкую отхватить минусов, но всё же спрошу.
Подскажите хорошие курсы для упорядочивания знаний по плюсам.
После кучи видео и книг, какой-то сумбур в голове, а код всё ещё на уровне С с классами.
Если хочется попрактиковаться — есть курс от Yandex и МФТИ. Теория доступна любому зарегистрированному пользователю, практика по платной подписке.
Уверен, что есть и другие прекрасные материалы, но эти опробованы на своей шкуре :)
А что там с легковесной заменой исключений? Чем всё дело закончилось?
Прочитал ответ страуса, ну весь текст можно свести к двум предложениям. Первое, в моём мире трава зеленее, я художник у меня есть справка, мои исключения и так почти зае..., а все доказательства против подлог и провакация, и вообще у моих эксепций право первой ночи, надо просто немножко допилить напильником. Это подавляющая часть текста, которую можно игнорировать ничего не потеряв.
Но есть и вторая часть, которая хоть и очень краткая, но очень по существу, и сводиться к знаменитому вопросу - а как же нам скрестить ежа с ужом. Как новые эксепции будут уживаться со старыми, какова цена вопроса и стоит ли овчинка выделки. Видимо авторы пошли курить эти вопросы.
Факт существования раста в котором эта идея воплощена в реальности и где новые эксепции имеют право первенства показывает, что все хорошо работает вместе, паника это такая же эксепция как в цпп, ну слегка усеченная и поэтому немного более эффективная да и кого вообще волнует производительность паники?
Да, в цпп все не так просто, старый код не выкинуть и нужно как-то сосуществовать, 20 лет назад читая его книгу по цпп, я восхищался этим человеком, но по-моему, страус сильно предвзят и ему уже пара на пенсию.
* «легковесные исключения» замедляют hot path лишними if
* они используют доп регистр для флага об ошибке, что замедляет вызовы функций
В итоге — предложенные исключения кидать дёшево и они полезны при частых ошибках, но вот для ситуаций когда ошибки редки — они вредят. Кстаи, где-то была на хабре статья, где сравнивали производительность кодов возврата vs исключений, и исключения побеждали.
Вы так об этом говорите как будто это факт. Давайте я включу режим страуса и использую его же риторику только поверну стрелочку в другую сторону.
Нет ни одного настоящего научного исследования показывающее кратное превосходство эксепций над кодами для безошибочного случая. Материалы которые можно найти в интернете и якобы доказывают преимущество эксепций это все происки пятой колонны полученные для граничных случаев и необоснованно обобщенные. Преимущество в несколько процентов для некоторых сценариев это нормально и вписывается в рамки погрешности. Бранч предикторы современных процессоров творят буквально чудеса позволяя коду в 4к почти что не замечать ни единого разрыва. На Хабре есть статья по этому поводу, а на Хабре врать не будут зуб даю. И вообще бремя доказательства на том кто делает утверждение.
А ещё есть много вопросов к недополученной прибыли из за эксепций, из за них тот же копи элижен не гарантирован да и ещё много других мест где компилятор обязан не оптимизировать из за оглядки на эксепции, из за чего впрочем действительно может страдать производительность кодов в случае отсутствия ошибок по сравнению с си.
И ещё, а мужики то и не знают что оказывается то на си коды ошибок тормозят, то то они дураки все айпиай делают на кодах. Опять же в том же расте ничего почему то не тормозит. А почему тогда на си++ тормозит, не из за эксепций ли?
Кстати нашел ту статью с замерами habr.com/ru/company/ruvds/blog/533332
… что для возврата флага понадобится доп регистр — это факт.вопрос в том, какой именно регистр. Вроде как call convention использует не все x64 регистры, и передать аргумент в одном из оставшихся не должно быть проблемой?
Нельзя просто так взять и использовать регистр. Чтобы вызываемая функция могла его затереть своим флагом, вызывающая функция должна его либо не использовать вообще, либо сохранить куда-то. И то и другое может сказываться на эффективности программы, потому что у компилятора отобрали регистр, которым он мог бы свободно пользоваться.
(Впрочем, для флажков порой пользуются именно регистром с флажками, но там всё не так просто, потому что из функции надо возвращаться так, чтобы флажки случайно не затереть, восстанавливая стек, например.)
А не могли бы ссылку дать на сравнение исключений и кодов возвратов. Очень интересно, особенно в контексте встроенного ПО для микроконтроллеров.
Полагаю, что там рассматривался какой то гепотетический случай огромной вложенности и передачи кода возврата из самого низа до самого верха.
А всё увидел, ваш комментарий ниже. Спасибо.
Спасибо. Обидно что есть фрагментация сообщества — библиотеки которые хотят работать с (no-exception) и другие активно использующие их. И между ними ставят костыли. Согласен с N+1.
ps: что мешает подход opencl внедрить в c++ что бы можно было компилировать и объединять несколько ядер и пускать их на исполнение совместно (современные процессоры просто умоляют об этом).
Например, я не знаю способа ограничить объем памяти для подргужаемой библиотеке ни под виндовс, ни под линукс. Расскажете как?
Я уж молчу о том, что существуют платформы где библиотеки вообще линкуются только статически.
Например, я не знаю способа ограничить объем памяти для подргужаемой библиотеке ни под виндовс, ни под линукс. Расскажете как?
Очень просто надо попросить эту библиотеку использовать только ваш runtime которой ей и передать, что бы она выделяла память, создавала потоки, подгружала другие библиотеки, обращалась к файловой системе, запрашивала время и работала со строками и другими ресурсами, только через апи которое ей передали, а не напрямую через статические методы и прямые вызовы к ос.
Ну так это можно сделать на любом языке. Я вообще не вижу чем C++ такой особенный и почему ему для этого нужны какие-то специальные фичи.
Например, я видел такие либы для чистого C. При инициализации контекста они просят указатели на malloc, free и иже с ними. Можно передать библиотечные, можно — свои собственные.
А у C++ просто еще больше геморроя с динамическими библиотеками. Не только потому что нельзя экспортировать compile time код в динамическую библиотеку, или понять какой она runtime использует, но даже потому что так нет единого метода манглить имена с типами параметров и ограничениями концептов.
Во-первых, как я уже говорил ранее, POSIX у вас из коробки нет. Вы прекрасно можете писать на C++ под DOS или Windows, обходясь без позикса вообще. Вы случайно не путаете POSIX и стандартную библиотеку? Это разные вещи. Грубо говоря, ifstream — это STL, fopen — это libc, простой open (вы им наверное и не пользовались ни разу) — это как раз POSIX, CreateFile — это win32. Ну и в MS-DOS для открытия файла вам нужен какой-нибудь условный int 21h. Так вот, из всего этого многообразия только ifstream имеет отношение к C++. И то, не к самому языку, а к его стандартной библиотеке.
Во-вторых — я не видел ни одного языка где есть стандартный способ передачи оберток над рантаймом в библиотеки/модули. Поэтому я и говорил что было бы странно хотеть этого в C++.
В третьих — поддержка динамических библиотек, symbol name mangling и все такое — это не часть C++. Можете открыть стандарт и почитать. Там вообще ничего про это нет. Это все детали реализации. Ну и очевидно что вендоры компиляторов тут извращаются как хотят.
dl (и его виндовый аналог) был создан для того чтобы линковать программы на C (хотя, опять же, динамическая линковка не является частью стандарта С). А С++ просто "паразитирует" на этом, страдая от того что функциональность dl покрывает только задачи С. Например, манлинга имен по хорошему вообще не должно быть. Если бы dl создавался конкретно для С++, то там символы идентифицировались бы совсем по-другому.
Во-первых, как я уже говорил ранее, POSIX у вас из коробки нет
Есть стандартная библиотека, которая привязана к POSIX гвоздями.
Выделение памяти, доступ к файлам и файловой системе, создание потоков, сеть, юникод и локали, время, запуск процесов… Неужели это мене важно чем контейнеры или корутины и новые UB?
Всё то что вы написали и чего нет можно было бы добавить, при желании конечно.
Не важно как реализованы системные вызовы, но мне нужен контроль над ресурсами которые программа хочет использовать в том числе и временем исполнения.
Во-вторых — я не видел ни одного языка где есть стандартный способ передачи оберток над рантаймом в библиотеки/модули
Посмотрите maple MKernelVector, lua lua_State, java JNIEnv да просто местами называется Context.
В третьих — поддержка динамических библиотек, symbol name mangling и все такое — это не часть C++
Так что мешает дабавить функии и которые будут работать с этими именами. Если само кодирование не стандартизовано, то почему нельзя хотя бы методы для работы с ним не описать? Что бы они были из коробки.
dl (и его виндовый аналог) был создан для того чтобы линковать программы на C
Что мешает стандартизировать дополнительные функции в динамических библиотеках которые будут решать эти проблемы? cpp_init, cpp_done, cpp_query…
Есть стандартная библиотека, которая привязана к POSIX гвоздями.
Я конечно не держал свечку, но практически уверен что имплементация стандартной библиотеки C++ для MSVC вообще никаким образом не сделана поверх POSIX.
Выделение памяти
есть в STL
доступ к файлам
есть в STL
файловой системе
есть в STL начиная с C++17
создание потоков
есть в STL начиная с C++11
сеть
обещают в следующем стандарте
юникод и локали
libicu по субъективным ощущениям больше чем весь STL. Делать юникод частью стандарта C++ — ну не знаю...
время
std:time
запуск процесов
Довольно сильно зависит от платформы. У fork/execve API и поведение сильно отличается от CreateProcess.
lua lua_State, java JNIEnv
Это вроде API для работы с внешними компонентами, а не с модулями языка. Вообще я не сильно знаком с java — можно ли загрузить Java class с другим JNIEnv?
Так что мешает дабавить функии и которые будут работать с этими именами.
Что мешает стандартизировать дополнительные функции в динамических библиотеках которые будут решать эти проблемы? cpp_init, cpp_done, cpp_query…
Ну это нужно вводить динамическую загрузку кода в стандарт C++. А если ее вводить — то вводить уже правильно, чтобы потом не переделывать. Вводить же правильно — это работы комитету на несколько лет. Кстати, может это будет сделано в рамках работы модулями?
Вводить же правильно — это работы комитету на несколько лет. Кстати, может это будет сделано в рамках работы модулями?
Вместе с модулями нет шансов протащить.
В РГ21 есть идея по динамической загрузке, и есть даже proposal github.com/cpp-ru/ideas/issues/2. Комитет правда холодно воспринял идею, она пока на паузе. Попробую опять через годик, другой.
Вы не можете в STL вызвать подпрограмму и запретить её использовать динамическую память или попросить использовать свои методы доступа к файловой системе.
Я понимаю чего вы хотите. И у вас явно есть use case для этой фичи. Но я, например, зачем оно сможет мне понадобится. И судя по всему — мало кто видит потребность в такой функциональности. Поэтому делать это частью языка вряд-ли кто-то будет.
Если мне вдруг понадобится такая штука — то как вы правильно сказали — всегда можно передавать явный контекст со всеми нужными функциями (как JNIEnv, ага). Вы вроде упомянули явное следование контракту. С 3rd party компонентами конечно так не получится. Но там вообще никто не может гарантировать что они не будут вызывать mmap(MAP_ANONYMOUS) или VirtualAlloc(), например.
Вы вроде упомянули явное следование контракту. С 3rd party компонентами конечно так не получится.
Это почему же? Если 3rd party компоненты собирать с этой новой библиотекой и требованиями то всё будет. Посмотрите на android они именно туда идут.
mmap(MAP_ANONYMOUS) или VirtualAlloc(), например.
Они будут запрашивать динамические модули через только через переданное им апи и функции mmap и VirtualAlloc вы сможете этому модулю не давать или просто определить что он их вообще хотел и сколько раз и как вызывал. В общем песочница грубо говоря. Вы можете управлять доступом в рамках вам предоставленного как вам хочется.
Они будут запрашивать динамические модули через только через переданное им апи и функции mmap и VirtualAlloc вы сможете этому модулю не давать.
Ну если они будут запрашивать и вообще вести себя прилично — то конечно можно и не давать. Но вообще мы не можете запретить коду на C++ вызвать любой syscall по своему желанию.
А вообще тут возникает куча вопросов даже если все модули ведут себя прилично. Например, пусть у вас есть модули A, B и C. Вы загружаете A и B, выдавая каждому свой контекст с функциями. А затем они оба хотят загрузить C. И как тут быть? Какой контекст он должен получить? Создавать два контекста? Сможем ли мы тогда передавать объекты, который сгенерировал модуль C загруженный через A в модуль C загруженный через B? Например, может оказаться что модули A и B используют разные аллокаторы и попытка вызвать деструктор из чужого модуля приведет к интересным эффектам. Или таскать ссылку на контекст за каждым объектом? Это как-то не в стиле C++, где ты не платишь за абстракции, которыми не пользуешься.
И вообще, получится что каждый "правильный" модуль не сможет вызывать стандартную библиотеку напрямую (сейчас то добрый линковщик подставляет нужные адреса прямо в код), нужно будет делать indirect call каждый раз. Современные предсказатели переходов вроде бы справляются с этим, но все равно — лишнее случайное чтение — это не очень хорошо. Эти контексты просто не будут вылезать из кешей, мешая процессору закешировать что-то действительно важное. Короче, тут очень далеко до zero cost abstractions, которыми славится C++.
И все ради чего? Чтобы программист мог раз в жизни захукать вызовы к STL? Ну используйте LD_PRELOAD если вам жизненно важно делать хуки к стандартным либам. Немного магии с __builtin_return_address и вы даже будете знать какая конкретно библиотека пытается вызвать ту или иную функцию. А дальше — делайте любую диспетчеризацию вызовов, которые захотите. Да, непереносимо, зато не портит производительность другим юзерам.
А уж если у вас есть возможность пересобрать модули, то GCC-шная фича -finstrument-functions позволит еще и не такие чудеса творить.
А чем запуск разных потоков не «объединение и запуск нескольких ядер»?
void a(...) { a1;a2;a3;… }
void b(...) { b1;b2;b3;… }
void c(...) { c1;c2;c3;… }
это некоторые вычислительные ядра, которые выполняют вычисления в зависимости от входных индексов (как правило матриц) и могут выполняться независимо в разном порядке и на разных ядрах.
d=merge(a,b,c)
новое ядро d которое строиться из команда a,b,c порядок которых можно варьировать в зависимости от загрузки вычислителей процессора (как hyper-threading только на уровне компилятора) т.к. они совершенно независимы.
Если писать подобное вручную, то сложность такого кода быстро превысит возможности человека. Такие ядра проще сделать примерно одной длинны и сделать что бы они работали на полях схожих размеров, чем обычный линейный код.
Лучше всего это должно выглядеть на эльбрусах и итаниах.
Но Networking не примут, пока не договорятся об интерфейсе Executors.
А как вы себе это представляете? Это очень ОС-зависимый механизм.
Порой этот сахар может быть чересчур толстым.
Для потоков — в 2020 году — ещё можно выделить какой-то общий набор возможностей и предоставить сравнительно тонкую кроссплатформенную прослойку. Для событий — пока ещё не очень.
Если производительность не важна, то берите любую библиотеку для POSIX-совместимости, там есть какая-то реализация select() или poll(). Правда, она не масштабируется на тысячи сокетов и из глаз может пойти кровь, если вы её будете читать, но работает же. Пользователи приложений на Электроне может даже схавают. Только зачем такое же тащить в стандарт — не понятно.
Ведь хотелось бы не такое же, а универсально лучшее. Чтобы как WaitForMultipleEvents(), epoll(), kqueue() — только всё сразу и с одинаковой семантикой и примерно одинаковой производительностью и масштабируемостью на всех платформах.
А не планируются какие-то удобства по корутинам? В текущем виде страшновато их трогать, много всякого вокруг накрутить надо чтоб завелись
аллокатор выделит память не под 200 байт, а под 224
Почему?
С++23 WIP: онлайн-встреча международного комитета по C++