которые позволили бы реализовать аналог макроса try! из Rust
А как там у rust с эргономикой исключений? Странно сравнивать язык, который предполагает наличие исключений с тем, кто предполагает их отсутвие.
Хотя, на самом деле, с try в расте всё очень прохо, потому как много раишных объектов взятых из С++, которые не предполагают существования без исключений. Поэтому большинство ошибок никак не обрабатываются и триггерят паники.
Аналогично try практически не работает без dyn box, не говоря уже об оверхеде на каждый вызов. Аллокатор же не особо более предсказуемый, нежели расркутка стека. Без dyn box там какая-либо эргономика отсутвует как класс.
Сам же result элементарен и зачем его тащить в язык? Чтобы что? Делать новые интерфейсы с ним? Ни на что в текущей stdlib это не повляет. Для кода вне языка есть библиотеки.
Особенно в контексте Qt/Unreal Engine это выглядит максимально нелепо. Почему библиотеки не решают создаваемые ими проблемы? Почему вы не спрашиватее с них, а требуете это от языка?
Альтенативой исключений без исключений могут быть только статические исключение. Это фича языка и её действительно можно требовать. Правда её нет ни в одном мейнтримном языке, поэтому предъявить за это нельзя.
? в язык никогда не затащат, надеюсь. Это не более чем костыль. В языке нет expr, которые влияют на поток управления. Если их вводить, то вводить нормально и это ящик пандоры. Лупхолы здесь покажуться детской игрушкой.
Превращать язык в помойку, в которую суют что угодно бессистемно лишь бы код без исключений не выглядел максимально страшно в глазах неофитов в рекламных агитках.
Мы уже имеем новый диалект, который называется C++ и то, как пишут на нём(дилекте) код. А есть другой диалект C++, который наследник си с классами. Со всеми этими virtual/this и прочим.
Поэтому те люди, которые пишут на новом диалекте абсолютно без разницы на то что там будет для тех, кто пишет на старом. И им, в с свою очередь, на тех, кто пишет на новом.
При этом. Последователи нового диалекта не мешают никак последователям старого, но последователи старого всегда мешают новым. У вас никто не забирает ваш this и никто не заставляет пользоваться новым. Почему у вас всегда своё сводится к "запрещать" и "не давать"?
А сколько лучей радости будет от разработчиков библиотек
Для тех, кому это ненужно - пусть не поддерживают. Никаких же проблем нет с теми же корутинами. Что там функция не совсем функция и return из неё не совсем reutrn.
Просто нужно пойти дальше и форкнуть функции. Если так хочется, чтобы новое не попало в старый код. Это решит множество проблем.
Но здесь есть фундаментальная проблема. Та, по которой последователи старого ведут себя подобным образом. Он хотят использовать библитеки и фишки, которые реализованы новом кодом и подходами, которым они хотят запрещать. Они очень яро блюдут совместимость, чтобы не остаться 1на1 со своим virtual. Это максимально порочная практика и рано или подхно груз легаси что-то сломает в умах последователей нового.
Темплейт здесь ненужен - уже давно есть неявные темплейты с auto, *this - так же. Указателей здесь никаких нет.
Наличие std::forward<Self> - это последствия сишной базы. С этим ничего не поделать пока C++ не перестанет быть для большинства тем на чём пишутся либы, код которых никто никогда не видет, но использует в си с классами. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1221r0.html - это и подобны начинания зарубают.
decltype(auto) value(this auto &&) { return ((decltype(this))this).value_; }
[](this auto self, int n) -> int { return (n <= 1) ? 1 : n * self(n-1); + }; - self это базовый паттерн для рекурсивных лямбд. -> int - это бесполезная мода. Множество языков и их последователей для которых не реаизован, либо не реализован вывод типа возврата, создают среду хейта вывода типов.
Так же - этот пример максимально не показательный. Это можно похакать через
std::function<int(int)> self = [](int n) -> int { return (n <= 1) ? 1 : n * self(n-1); + };
Но, если мы начнём писать настоящий С++-код, т.е. полиморфный код. Сигнатура будет всегда сводится к auto(auto ...) и никая std::function не может. Именно здесь и возникает проблемы - мы никак не можем написать это иначе. И нам нужно будет сделать обёртку и заслать туда руками этот self.
И не следует забывать, что сложность данного кода обусловлена не C++, а его семантикой. Вы не сможете показать подобный код на другом языке. Если он и будет проще, то в ущерб смыслу. А выразительность это и есть количество смысла(желательного воспринимаемого) на единицу визуального/мысленного объёма.
Допустим, практически нигде нет полиморфизма, не вдаваясь в подробности, по ссылкам/значениями. Мы можете написать либо f(int x), либо f(int & x). Когда как в C++ - это десятки разных функций, которые описаны одной. Очевидно, что всё это ненужно в случае, когда этого нет. Но когда этого нет нет и тех свойств, которым обладает код на C++. И издесь либо/либо.
Скорее даже про то, что сидеть в core сложно и заниматься его развитием тоже. Зачем, допустим, тащить те же ренжи в стандарт? Как результат мы имеем огрызок применимость которого крайне сомнительно. И ещё 10 лет его будут реализовывать. Единственное здесь преимущество - то, что мы получим новую реализацию от той же stdc++ в отличии от того ужаса, что есть сейчас. Текущую реализацию оправдать можно - она всё же PoC пытающийся работать на старых версиях языка.
Аналогично с flat_set - это элементарный адептор поверх вектора. Зачем он в стандарте? Чтобы что?
stacktrace уже более адекватно. Это всё же рантайм языка.
operator[] (int.int) - это именно кор и этим и нужно заниматься. Уж тем более не в пользу элементарных обёрток, который уже давно есть.
Решение, как мне кажется, здесь макисмально простое. Отделить язык от stdlib, в качестве stdlib оставить только то, что напрямую взаимодействует с языком. Какое-нибудь coro/initializer_list и прочее. Развивать эту stdlib вне языка. Как результат ненужно будет ждать пару 10 лет для очередной обёртки в пару строк. Привет contains.
Её же можно использовтаь в качестве референсной реализации, если кто-то заходит сделать свою. Её же можно использовать и для протипирования, чтобы эти прототипы не валялись где попало.
Какая-то очень странная логика. А вам не кажется, что у языков нет как такового потребления, производительности и всего проче, что любят им приписывать? Чем отличается тот же C++ и js? Только тем, относительно потребления памяти/производительности, какие возможности даёт язык программисту, но как и любые другие возможности - возможность никак не выражается напрямую в потребительских качествах. Нужен тот, кто ею воспользуется и это качество создаст.
В данно же случае наличие "Hello".replaceAll("Hello", "world") является тем, следствием чего является "жрущий по 2 гига". Сведя другой язык к тому же, даже если не язык, а программистов - одно без другого не работает, причину я описал выше.
Пблема с "взять либу" максимально вывсосана из пальца. 95% программирования на жс и подобных языках сводится к использованию либ. Большая часть из которых лефтпады.
"ой нуйдёт" - он уйдёт, если ему без разницы на те самые "2 гига"", а так же если если у него нет необходимых компетенций. Если что-либо иначе - для него replaceAll практически не существует.
Больше скажу - C++, наверное, самый выразительный язык из всех. Я не видел языка другого, который бы позволял упоковывать столько семантики в столь компактные/красивые языковые коснтуркции.
Но, эта выразительность требует недюжего понимания, умений и способностей. И нет, не потому, что это C++. Просто, в отличии от других, C++ действительно пытается думать о тех самых "2 гига". Создать кое как работающую абстракцию, которая игнорирует все проблемы - просто. Создать абстракцию, которая выжает минимальное количество смысла - просто.
Создать же что-то за рамками этого - сложно. C++ здесь первопроходет. Самое важное - в идеале эти абстракции не должн позволить существованию replaceAll.
В каком случае эта операция будет, как минимум, эффективна(неэффективность самой задачи выношу за скобки - её там нет)? Когда мы заменяем одну строку на другую. Нам нужны две операции. Поиск слайса и реплейс над слайсом, где уже гарантируется, что мы не изменим что-то за границами слайса. И C++ движется в эту сторону.
Конечно же, под C++ я имею ввиду некие C++ будущего в вакууме. Это не тоже самое, что обычное во многом врайт-онли си с классами. Оно всё тот же сишный подход не переродившийся в новом качестве, как C++.
Есть множество функций. Есть те функции, которые программа использует, а есть те, которые нет. Использует - это вызывает хотя бы один раз. Смысл кода - получит сайд-эффект в рантайме на факт использования. Так же туда можно передавать любые компилтайм/статические данные, уникальный для каждой функции. Они нужны, в том числе, для идентификации той функици которая стриггерила эффект.
Что далее с ним делать - абсолютно неважно. В данном примере в качестве данных передаётся указатель на функцию. Используется только для идентификации. В качестве эффекта вызывается принтф.
push назван не очень удачно - лучше это назвать effect.
Есть говорить о юзкейсах. У нас есть vulkan, там есть расширения. Использование расширений добавляет в api дополнительные функции. Мы не знаем какие расширения поддерживает, условно, видяха.
Далее мы пишем код. Нам нужно составить список всех функций, которые используем в коде. Потом узнать какие расширения их предоставляют. А далее перед стартом программы проверить, условно, видяху на наличие этих расширений.
Очевидно, что с таким подходом мы можем что-то забыть. Наш код запуститься, а далее внезапно упадёт, потому как мы забыли запросить какое-то расширение.
Используя описанный в статье подход - мы можем повесть как сайд-эффект проверку наличие в, условно, видяхе этих расширений. Это сработает до старта программы. Таким образом мы никогда ничего не забудем и не упадём внезапно.
Попытка решения описанной выше задачи и родило эту статью. Применений масса и это лишь самый базовй вариант. Постараюсь в будущем сделать более подробную статью с описанием юзкейсов практических.
А как там у rust с эргономикой исключений? Странно сравнивать язык, который предполагает наличие исключений с тем, кто предполагает их отсутвие.
Хотя, на самом деле, с try в расте всё очень прохо, потому как много раишных объектов взятых из С++, которые не предполагают существования без исключений. Поэтому большинство ошибок никак не обрабатываются и триггерят паники.
Аналогично try практически не работает без dyn box, не говоря уже об оверхеде на каждый вызов. Аллокатор же не особо более предсказуемый, нежели расркутка стека. Без dyn box там какая-либо эргономика отсутвует как класс.
Сам же result элементарен и зачем его тащить в язык? Чтобы что? Делать новые интерфейсы с ним? Ни на что в текущей stdlib это не повляет. Для кода вне языка есть библиотеки.
Особенно в контексте Qt/Unreal Engine это выглядит максимально нелепо. Почему библиотеки не решают создаваемые ими проблемы? Почему вы не спрашиватее с них, а требуете это от языка?
Альтенативой исключений без исключений могут быть только статические исключение. Это фича языка и её действительно можно требовать. Правда её нет ни в одном мейнтримном языке, поэтому предъявить за это нельзя.
? в язык никогда не затащат, надеюсь. Это не более чем костыль. В языке нет expr, которые влияют на поток управления. Если их вводить, то вводить нормально и это ящик пандоры. Лупхолы здесь покажуться детской игрушкой.
Превращать язык в помойку, в которую суют что угодно бессистемно лишь бы код без исключений не выглядел максимально страшно в глазах неофитов в рекламных агитках.
Мы уже имеем новый диалект, который называется C++ и то, как пишут на нём(дилекте) код. А есть другой диалект C++, который наследник си с классами. Со всеми этими virtual/this и прочим.
Поэтому те люди, которые пишут на новом диалекте абсолютно без разницы на то что там будет для тех, кто пишет на старом. И им, в с свою очередь, на тех, кто пишет на новом.
При этом. Последователи нового диалекта не мешают никак последователям старого, но последователи старого всегда мешают новым. У вас никто не забирает ваш this и никто не заставляет пользоваться новым. Почему у вас всегда своё сводится к "запрещать" и "не давать"?
Для тех, кому это ненужно - пусть не поддерживают. Никаких же проблем нет с теми же корутинами. Что там функция не совсем функция и return из неё не совсем reutrn.
Просто нужно пойти дальше и форкнуть функции. Если так хочется, чтобы новое не попало в старый код. Это решит множество проблем.
Но здесь есть фундаментальная проблема. Та, по которой последователи старого ведут себя подобным образом. Он хотят использовать библитеки и фишки, которые реализованы новом кодом и подходами, которым они хотят запрещать. Они очень яро блюдут совместимость, чтобы не остаться 1на1 со своим virtual. Это максимально порочная практика и рано или подхно груз легаси что-то сломает в умах последователей нового.
Темплейт здесь ненужен - уже давно есть неявные темплейты с auto, *this - так же. Указателей здесь никаких нет.
Наличие
std::forward<Self> - это последствия сишной базы. С этим ничего не поделать пока C++ не перестанет быть для большинства тем на чём пишутся либы, код которых никто никогда не видет, но использует в си с классами.http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1221r0.html - это и подобны начинания зарубают.[](this auto self, int n) -> int { return (n <= 1) ? 1 : n * self(n-1); + }; - self это базовый паттерн для рекурсивных лямбд. -> int - это бесполезная мода. Множество языков и их последователей для которых не реаизован, либо не реализован вывод типа возврата, создают среду хейта вывода типов.Так же - этот пример максимально не показательный. Это можно похакать через
Но, если мы начнём писать настоящий С++-код, т.е. полиморфный код. Сигнатура будет всегда сводится к
auto(auto ...)и никая std::function не может. Именно здесь и возникает проблемы - мы никак не можем написать это иначе. И нам нужно будет сделать обёртку и заслать туда руками этот self.И не следует забывать, что сложность данного кода обусловлена не C++, а его семантикой. Вы не сможете показать подобный код на другом языке. Если он и будет проще, то в ущерб смыслу. А выразительность это и есть количество смысла(желательного воспринимаемого) на единицу визуального/мысленного объёма.
Допустим, практически нигде нет полиморфизма, не вдаваясь в подробности, по ссылкам/значениями. Мы можете написать либо f(int x), либо f(int & x). Когда как в C++ - это десятки разных функций, которые описаны одной. Очевидно, что всё это ненужно в случае, когда этого нет. Но когда этого нет нет и тех свойств, которым обладает код на C++. И издесь либо/либо.
Скорее даже про то, что сидеть в core сложно и заниматься его развитием тоже. Зачем, допустим, тащить те же ренжи в стандарт? Как результат мы имеем огрызок применимость которого крайне сомнительно. И ещё 10 лет его будут реализовывать. Единственное здесь преимущество - то, что мы получим новую реализацию от той же stdc++ в отличии от того ужаса, что есть сейчас. Текущую реализацию оправдать можно - она всё же PoC пытающийся работать на старых версиях языка.
Аналогично с flat_set - это элементарный адептор поверх вектора. Зачем он в стандарте? Чтобы что?
stacktrace уже более адекватно. Это всё же рантайм языка.
operator[] (int.int)- это именно кор и этим и нужно заниматься. Уж тем более не в пользу элементарных обёрток, который уже давно есть.Решение, как мне кажется, здесь макисмально простое. Отделить язык от stdlib, в качестве stdlib оставить только то, что напрямую взаимодействует с языком. Какое-нибудь coro/initializer_list и прочее. Развивать эту stdlib вне языка. Как результат ненужно будет ждать пару 10 лет для очередной обёртки в пару строк. Привет contains.
Её же можно использовтаь в качестве референсной реализации, если кто-то заходит сделать свою. Её же можно использовать и для протипирования, чтобы эти прототипы не валялись где попало.
Какая-то очень странная логика. А вам не кажется, что у языков нет как такового потребления, производительности и всего проче, что любят им приписывать? Чем отличается тот же C++ и js? Только тем, относительно потребления памяти/производительности, какие возможности даёт язык программисту, но как и любые другие возможности - возможность никак не выражается напрямую в потребительских качествах. Нужен тот, кто ею воспользуется и это качество создаст.
В данно же случае наличие
"Hello".replaceAll("Hello", "world") является тем, следствием чего является "жрущий по 2 гига". Сведя другой язык к тому же, даже если не язык, а программистов - одно без другого не работает, причину я описал выше.Пблема с "взять либу" максимально вывсосана из пальца. 95% программирования на жс и подобных языках сводится к использованию либ. Большая часть из которых лефтпады.
"ой нуйдёт" - он уйдёт, если ему без разницы на те самые "2 гига"", а так же если если у него нет необходимых компетенций. Если что-либо иначе - для него
replaceAll практически не существует.Больше скажу - C++, наверное, самый выразительный язык из всех. Я не видел языка другого, который бы позволял упоковывать столько семантики в столь компактные/красивые языковые коснтуркции.
Но, эта выразительность требует недюжего понимания, умений и способностей. И нет, не потому, что это C++. Просто, в отличии от других, C++ действительно пытается думать о тех самых "2 гига". Создать кое как работающую абстракцию, которая игнорирует все проблемы - просто. Создать абстракцию, которая выжает минимальное количество смысла - просто.
Создать же что-то за рамками этого - сложно. C++ здесь первопроходет. Самое важное - в идеале эти абстракции не должн позволить существованию
replaceAll.В каком случае эта операция будет, как минимум, эффективна(неэффективность самой задачи выношу за скобки - её там нет)? Когда мы заменяем одну строку на другую. Нам нужны две операции. Поиск слайса и реплейс над слайсом, где уже гарантируется, что мы не изменим что-то за границами слайса. И C++ движется в эту сторону.
Конечно же, под C++ я имею ввиду некие C++ будущего в вакууме. Это не тоже самое, что обычное во многом врайт-онли си с классами. Оно всё тот же сишный подход не переродившийся в новом качестве, как C++.
Есть множество функций. Есть те функции, которые программа использует, а есть те, которые нет. Использует - это вызывает хотя бы один раз. Смысл кода - получит сайд-эффект в рантайме на факт использования. Так же туда можно передавать любые компилтайм/статические данные, уникальный для каждой функции. Они нужны, в том числе, для идентификации той функици которая стриггерила эффект.
Что далее с ним делать - абсолютно неважно. В данном примере в качестве данных передаётся указатель на функцию. Используется только для идентификации. В качестве эффекта вызывается принтф.
push назван не очень удачно - лучше это назвать effect.
Есть говорить о юзкейсах. У нас есть vulkan, там есть расширения. Использование расширений добавляет в api дополнительные функции. Мы не знаем какие расширения поддерживает, условно, видяха.
Далее мы пишем код. Нам нужно составить список всех функций, которые используем в коде. Потом узнать какие расширения их предоставляют. А далее перед стартом программы проверить, условно, видяху на наличие этих расширений.
Очевидно, что с таким подходом мы можем что-то забыть. Наш код запуститься, а далее внезапно упадёт, потому как мы забыли запросить какое-то расширение.
Используя описанный в статье подход - мы можем повесть как сайд-эффект проверку наличие в, условно, видяхе этих расширений. Это сработает до старта программы. Таким образом мы никогда ничего не забудем и не упадём внезапно.
Попытка решения описанной выше задачи и родило эту статью. Применений масса и это лишь самый базовй вариант. Постараюсь в будущем сделать более подробную статью с описанием юзкейсов практических.