char = текст ASCII
chat8_t = code unit из UTF-8
int8_t, uint8_t = целые числа
std::byte = неизвестные данные (не текст и не числа)
wchar_t = системный тип символа (привет, ужасы кодировок Windows)
signed char, unsigned char = что угодно в устаревшем коде; стоило бы выпилить
Кажется странным решение, что функция может бросать или только статические исключения, или только динамические. Сравним с Java, где есть checked и unchecked исключения. Обычные динамические исключения хорошо выполняют роль unchecked исключений, когда непонятно, как исправить проблему, но где-то на верхнем уровне имеет смысл всё-таки поймать исключение и обработать. Или сравните с паниками Rust.
Но вот согласно текущему предложению, динамическое исключение, проходящее через throws-функцию, завернётся в std::error, и чтобы достать его обратно, нужно или дождаться, пока оно окажется в не-throws функции, или танцевать с бубном. Плюс, при обработке std::error надо будет помнить, что там может оказаться завёрнутое динамическое исключение, которое нельзя игнорировать с той же лёгкостью, что и коды ошибок.
По-моему, было бы логичнее сделать отдельные варианты throws(E) и throws(E) noexcept. Первый будет пропускать и статические, и динамические исключения. Второй будет пропускать только статические. Причём заворачивать динамическое исключение в std::error стоило бы только явным образом. Не уверен, что это лучшее решение, но что-то мне подсказывает, что для динамических исключений нужна «выделенная полоса».
Речь в существующих Proposal-ах о том, чтобы разделить API аллокаторов на noexcept и не noexcept, а также разделить функции, работающие с аллокаторами, на noexcept и не noexcept. Обрабатывать overcommit в стандарте никто не предлагает. Если вы хотите делать дополнительные проверки, то их надо делать в вашем аллокаторе, плюс использовать новые методы, иначе исключение, выброшенное вашим аллокатором, там же и останется (краш).
Спрашивается, зачем вообще что-то менять, если никому не станет лучше? Дело в том, что как часть плана ухода от динамических исключений, в будущем предлагается сделать максимальную часть стандартной библиотеки noexcept. (Я неточно написал: noexcept предлагается сделать все существующие функции, *которые бросают только из-за памяти*, конечно же.) Чем больше функций noexcept, тем меньше оверхед на поддержку исключений.
P.S. Пишу «уход от динамических исключений», но понятно, что пока это дело необозримого будущего. На вскидку, готово всё будет к C++26, потом ещё будет процесс внедрения новшеств в std, всякие deprecate-obsolete…
P132 предлагает добавить nothrow версии аллокаторов и nothrow версии std:: функций, использующих аллокаторы. То есть, если всё это когда-нибудь примут, то всё равно нужно будет подсовывать везде свой «кошерный» аллокатор, плюс использовать новые версии методов.
Мне нравится, как Intellij Idea подсказывает, на какой строке вызывается корутина в Kotlin:
Ничего лишнего не нужно, но всё понятно. Со старыми исключениями такого сделать нельзя, потому что большинство функций может их выбросить. С новыми статическими исключениями IDE будет точно знать, где исключения бросаются, и сможет подсказать.
Идеальная система исключений — сложно сказать, я не эксперт в этом ;) Хотя в разделе «Когда что использовать» написаны гайдлайны, эквивалентные тем, что сейчас используются в Swift. То есть для преобразования строки в число рекомендуется не пользоваться исключениями, потому что «в строке нет числа» — не ошибка. А вообще, в современных языках программирования наблюдается переход от динамических исключений к статическим. И `throws` в контексте C++ всё же нужен как подсказка программисту.
Что меня расстраивает — в текущем Proposal не предусмотрены cast-ы статических исключений. Закастить можно, но только один раз, к `std::error`, при этом от исключения остаётся один message, остальное теряется. В идеале, конечно, статические исключения должны быть такими же мощными, как динамические, но с бонусом, что можно очень быстро бросить код ошибки.
Вот уменьшение boilerplate для Си как раз не собираются завозить. В комитете сказали, что если в Си когда-нибудь будут исключения, то явные. В статье приведён пример, как это будет выглядеть. Никакого автоматического проброса, явная проверка `result.failed`. А `finally` можно было бы рассматривать для C++, но там преобладает идеология RAII, поэтому этого тоже не будет.
Edit: Наврал я! Предлагается макрос try, предназначенный для проверки failed и проброса исключения.
Прочитал заголовок, обрадовался, перечитал заголовок, посмотрел на дату, взгрустнул. Согласен с подходом Rust, когда указатели, конечно, не deprecated, но почти на все случаи жизни существуют ссылки и другие безопасные аналоги. Проблема недостатка безопасных аналогов как раз находится в процессе решения: string_view, span, observer_ptr.
Да. Насколько я вижу, по сравнению с Java, в предложенном варианте метаклассов есть, как минимум, 2 ограничения:
Метаклассы применимы только для классов. Поля и методы так аннотировать не получится. Результат — появление метаклассов вроде classx, где из-за одной небольшой фичи приходится добавлять метакласс с её поддержкой
Не более 1 метакласса на класс. Чтобы использовать несколько сразу, придётся писать код вида:
Swift 4 и последующие версии будут продолжать методично ломать ваш код :)
Под ABI совместимостью имеется в виду, что можно будет линковать фреймворки, написанные на разных версиях Swift, начиная со Swift 4.
То есть не будет такой ситуации, как с Python, когда вы не можете мигрировать на новую версию языка только потому, что все либы заточены под старую. Не компилируется под новой версией Swift? Ну и ладно, укажу в настройках, чтобы только этот модуль компилировался под старой Swift.
Если строить все структуры данных без динамического выделения памяти, то перемещение займет столько же времени, сколько копирование. То есть ситуация тут напоминает std::array: вроде, быстрее работает, но пользоваться неудобно.
Как вариант, использовать кастомный аллокатор с std::unique_ptr.
С помощью шаблонных using pool_ptr и make_pool этот вариант тоже можно прихорошить, по удобству будет во многом не хуже shared_ptr.
Когда будете делать замеры производительности, рассмотрите и этот вариант тоже ;)
Переходя в раздел «ненормальное программирование», можно предложить вообще обходиться без указателей. Иерархию наследования преобразовывать в boost::variant. На практике, конечно, я бы не советовал везде пихать такие «извращения».
На вкус и цвет… Перед тем, как отправить, я прочитал это решение. И всё же, в данном конкретном случае предпочитаю написать более короткий «однострочник».
Создание лямбды с последующей передачей в map не является идиоматическим подходом в Ruby. «Решение с lambda» привычнее переписать так, и не только потому, что лямбда может работать медленнее, чем блок:
Каюсь, перепутал.
Про проблему, можно рассмотреть 2 случая:
Объект почти сразу же присоединяется к родителю. Можно переписать через третий вариант, у наследников QObject обычно последним параметром можно передать родителя.
Объект проделывает долгий путь до прикрепления к родителю. Вначале создаём через make_unique (или через QSharedPointer, как подсказывают), потом отбираем у unique_ptr владение и сразу же прикрепляем к родителю. Этот момент можно вынести в отдельную функцию.
Если я всё правильно понимаю, проблема решается в рамках всё тех же make-функций. Я не думаю, что второй случай очень частый, но опыт работы с Qt у меня тоже небольшой. Но идею в список рассылки попробую закинуть :)
Забыл я про deleteLater, тогда действительно потребуется QSharedPointer, std::unique_ptr уже не спасёт. Тем не менее, можно реализовать функции-обёртки MakeQObject и MakeQChild так, чтобы писать код вроде:
auto obj1 = MakeQObject<MyObject>();
auto obj2 = MakeQChild<MyObject>(parent);
Собственно, один из вопросов, поднятых в статье, — можно ли все выделения памяти в куче записывать в виде:
auto obj = make<MyObject>(arguments);
Где make — подходящая функция создания умного указателя. Ответ — да, можно, причём такой код будет безопасным и унифицированным.
Такие выделения памяти также отлично сочетаются с рекомендациями Herb Sutter.
chat8_t = code unit из UTF-8
int8_t, uint8_t = целые числа
std::byte = неизвестные данные (не текст и не числа)
wchar_t = системный тип символа (привет, ужасы кодировок Windows)
signed char, unsigned char = что угодно в устаревшем коде; стоило бы выпилить
Но вот согласно текущему предложению, динамическое исключение, проходящее через throws-функцию, завернётся в std::error, и чтобы достать его обратно, нужно или дождаться, пока оно окажется в не-throws функции, или танцевать с бубном. Плюс, при обработке std::error надо будет помнить, что там может оказаться завёрнутое динамическое исключение, которое нельзя игнорировать с той же лёгкостью, что и коды ошибок.
По-моему, было бы логичнее сделать отдельные варианты throws(E) и throws(E) noexcept. Первый будет пропускать и статические, и динамические исключения. Второй будет пропускать только статические. Причём заворачивать динамическое исключение в std::error стоило бы только явным образом. Не уверен, что это лучшее решение, но что-то мне подсказывает, что для динамических исключений нужна «выделенная полоса».
Спрашивается, зачем вообще что-то менять, если никому не станет лучше? Дело в том, что как часть плана ухода от динамических исключений, в будущем предлагается сделать максимальную часть стандартной библиотеки noexcept. (Я неточно написал: noexcept предлагается сделать все существующие функции, *которые бросают только из-за памяти*, конечно же.) Чем больше функций noexcept, тем меньше оверхед на поддержку исключений.
P.S. Пишу «уход от динамических исключений», но понятно, что пока это дело необозримого будущего. На вскидку, готово всё будет к C++26, потом ещё будет процесс внедрения новшеств в std, всякие deprecate-obsolete…
Мне нравится, как Intellij Idea подсказывает, на какой строке вызывается корутина в Kotlin:
Ничего лишнего не нужно, но всё понятно. Со старыми исключениями такого сделать нельзя, потому что большинство функций может их выбросить. С новыми статическими исключениями IDE будет точно знать, где исключения бросаются, и сможет подсказать.
Идеальная система исключений — сложно сказать, я не эксперт в этом ;) Хотя в разделе «Когда что использовать» написаны гайдлайны, эквивалентные тем, что сейчас используются в Swift. То есть для преобразования строки в число рекомендуется не пользоваться исключениями, потому что «в строке нет числа» — не ошибка. А вообще, в современных языках программирования наблюдается переход от динамических исключений к статическим. И `throws` в контексте C++ всё же нужен как подсказка программисту.
Что меня расстраивает — в текущем Proposal не предусмотрены cast-ы статических исключений. Закастить можно, но только один раз, к `std::error`, при этом от исключения остаётся один message, остальное теряется. В идеале, конечно, статические исключения должны быть такими же мощными, как динамические, но с бонусом, что можно очень быстро бросить код ошибки.
Edit: Наврал я! Предлагается макрос try, предназначенный для проверки failed и проброса исключения.
Да. Насколько я вижу, по сравнению с Java, в предложенном варианте метаклассов есть, как минимум, 2 ограничения:
classx
, где из-за одной небольшой фичи приходится добавлять метакласс с её поддержкойПод ABI совместимостью имеется в виду, что можно будет линковать фреймворки, написанные на разных версиях Swift, начиная со Swift 4.
То есть не будет такой ситуации, как с Python, когда вы не можете мигрировать на новую версию языка только потому, что все либы заточены под старую. Не компилируется под новой версией Swift? Ну и ладно, укажу в настройках, чтобы только этот модуль компилировался под старой Swift.
С помощью шаблонных using pool_ptr и make_pool этот вариант тоже можно прихорошить, по удобству будет во многом не хуже shared_ptr.
Когда будете делать замеры производительности, рассмотрите и этот вариант тоже ;)
Переходя в раздел «ненормальное программирование», можно предложить вообще обходиться без указателей. Иерархию наследования преобразовывать в boost::variant. На практике, конечно, я бы не советовал везде пихать такие «извращения».
rates.map{ |r| [r['CharCode'], r['Value'].to_f / r['Nominal'].to_f] }.to_h
Про проблему, можно рассмотреть 2 случая:
Если я всё правильно понимаю, проблема решается в рамках всё тех же make-функций. Я не думаю, что второй случай очень частый, но опыт работы с Qt у меня тоже небольшой. Но идею в список рассылки попробую закинуть :)
Собственно, один из вопросов, поднятых в статье, — можно ли все выделения памяти в куче записывать в виде:
Где make — подходящая функция создания умного указателя. Ответ — да, можно, причём такой код будет безопасным и унифицированным.
Такие выделения памяти также отлично сочетаются с рекомендациями Herb Sutter.