tag:blogger.com,1999:blog-654279083390275842.post7110162856266743546..comments2024-03-19T12:22:43.654+03:00Comments on Размышлизмы eao197: [prog.c++] Что-то я неслабо затупил из-за отсутствия fold expression в C++14eao197http://www.blogger.com/profile/17283739752119445290noreply@blogger.comBlogger16125tag:blogger.com,1999:blog-654279083390275842.post-6681848495010161842019-10-12T18:37:47.124+03:002019-10-12T18:37:47.124+03:00если try_parse вернула falseесли try_parse вернула falseAlexanderhttps://www.blogger.com/profile/08595415230413011053noreply@blogger.comtag:blogger.com,1999:blog-654279083390275842.post-73029111127879223962019-10-12T18:37:03.744+03:002019-10-12T18:37:03.744+03:00Да, тут остаётся надеяться только на компилятор. C...Да, тут остаётся надеяться только на компилятор. C -O0 он, похоже, проходится по всем элементам, а вот уже с -O1 идёт сразу на выход функции, если она вернула false: <a href="https://godbolt.org/z/Zo19Qp" rel="nofollow">https://godbolt.org/z/Zo19Qp</a>Alexanderhttps://www.blogger.com/profile/08595415230413011053noreply@blogger.comtag:blogger.com,1999:blog-654279083390275842.post-70370186522432321932019-10-12T17:32:19.977+03:002019-10-12T17:32:19.977+03:00@Alexander
Ага, спасибо за ссылки. Действительно,...@Alexander<br /><br />Ага, спасибо за ссылки. Действительно, пункты 6 и 10 показывают, что должно быть все нормально.<br /><br />Но подумалось вот о чем: если в этот initializer-list попадает 20 выражений, из которых на практике надо бы вычислить только 3, то вычисляться-то будут всегда 20. Но 3 полноценно, с вызовом try_parse, а у остальных 17 будет вычисляться только левый операнд в &&. Но все равно будет.<br /><br />На практике эти расходы вряд ли будут иметь значение. Но сам факт...eao197https://www.blogger.com/profile/17283739752119445290noreply@blogger.comtag:blogger.com,1999:blog-654279083390275842.post-21250946778518128192019-10-12T17:16:39.301+03:002019-10-12T17:16:39.301+03:00Насколько я понимаю написанное здесь, a &&...Насколько я понимаю написанное <a href="https://en.cppreference.com/w/cpp/language/eval_order#Rules" rel="nofollow">здесь</a>, a && b и {a, b} вычисляются так, что все side-эффекты вычисления a заканчиваются до начала вычисления b (пункты 6 и 10 соответственно)Alexanderhttps://www.blogger.com/profile/08595415230413011053noreply@blogger.comtag:blogger.com,1999:blog-654279083390275842.post-47471842880010243152019-10-12T09:04:13.783+03:002019-10-12T09:04:13.783+03:00@Alexander
> Ещё вариант: https://pastebin.com...@Alexander<br /><br />> Ещё вариант: https://pastebin.com/Lyya4fDy<br /><br />Очень прикольно, спасибо большое!<br /><br />Я, правда, озадачился другим вопросом: а есть ли здесь гарантии того, что все будет выполняться в нужном порядке. Ведь, по сути, там происходит следующее:<br /><br />initializer_list&lf;bool> a = {<br /> result && (result = get<0>(parsers).try_parse(source)),<br /> result && (result = get<1>(parsers).try_parse(source)),<br /> result && (result = get<2>(parsers).try_parse(source)),<br /> ...<br />};<br /><br />Внутри каждого выражения вычисления должны идти слева направо (хотя нет уверенности в том, что здесь нет UB, т.к. result и читается, и пишется в одном выражении). Но вот то, что все эти выражения должны вычисляться от индекса 0 к индексу N, а не наоборот, в этом у меня уверенности нет.eao197https://www.blogger.com/profile/17283739752119445290noreply@blogger.comtag:blogger.com,1999:blog-654279083390275842.post-36068740697983402282019-10-12T05:30:25.114+03:002019-10-12T05:30:25.114+03:00Ещё вариант: https://pastebin.com/Lyya4fDyЕщё вариант: https://pastebin.com/Lyya4fDyAlexanderhttps://www.blogger.com/profile/08595415230413011053noreply@blogger.comtag:blogger.com,1999:blog-654279083390275842.post-54079899141005052122019-10-11T08:55:02.503+03:002019-10-11T08:55:02.503+03:00Да, код не очень понятным получился, лень было раз...Да, код не очень понятным получился, лень было разбираться с типами, использовал везде auto. <br /><br />Продублирую здесь с явными типами. Идея та же - переносим обработку элементов кортежа в runtime. Для этого создаем initializer_list с указателями на функции try_parser_at для доступа к каждому элементу кортежа, и ищем первую функцию, вернувшую false, с помощью стандартной std::all_of.<br /><br />Не знаю только, возможно ли в принципе избавиться от вспомогательной try_parse_impl() с пачкой индексов в шаблоне.<br /><a href="https://wandbox.org/permlink/98CEhYjVEsvijy6P" rel="nofollow">Wandbox</a><br /><br />template<br />bool try_parser_at(const std::string & source, Tuple & parsers)<br />{<br /> return std::get(parsers).try_parse(source);<br />}<br /><br />template<br />bool try_parse_impl(const std::string & source, Tuple & parsers, std::index_sequence)<br />{ <br /> using try_parser_func_ptr = bool(*)(const std::string & source, Tuple & parsers);<br /><br /> const std::initializer_list try_parsers =<br /> {<br /> &try_parser_at...<br /> };<br /> <br /> return std::all_of(try_parsers.begin(), try_parsers.end(),<br /> [&source, &parsers](try_parser_func_ptr try_parser)<br /> {<br /> return try_parser(source, parsers);<br /> });<br />}<br /><br />template< typename... F ><br />bool try_parse(const std::string & source, std::tuple & parsers)<br />{<br /> using Indexes = std::make_index_sequence;<br /> return try_parse_impl(source, parsers, Indexes{});<br />}<br />Pavelhttps://www.blogger.com/profile/13419990047910988897noreply@blogger.comtag:blogger.com,1999:blog-654279083390275842.post-37983899459171876932019-10-11T08:14:44.796+03:002019-10-11T08:14:44.796+03:00@Pavel
Приветствую!
Прикольное решение, спасибо!...@Pavel<br /><br />Приветствую!<br /><br />Прикольное решение, спасибо! Заставило напрячь мозги чтобы понять, что именно здесь происходит.eao197https://www.blogger.com/profile/17283739752119445290noreply@blogger.comtag:blogger.com,1999:blog-654279083390275842.post-42408783402618551082019-10-10T22:00:32.711+03:002019-10-10T22:00:32.711+03:00Привет, Евгений
Можно воспользоваться идей tuple ...Привет, Евгений<br /><br />Можно воспользоваться идей <a href="https://foonathan.net/2017/03/tuple-iterator/" rel="nofollow">tuple iterator</a> и применить std::all_of() для всех указателей на функции try_parse, сгенерированных для каждого из элементов кортежа в initializer_list.<br /><br />К сожалению, применить constexpr к получающемуся initializer_list не получается, так что остается только надеяться, что компилятор сможет это как-то соптимизировать. Зато без рекурсивных воплощений шаблонов :)<br /><br />https://wandbox.org/permlink/LLAAzsDPXXl0yHSNPavelhttps://www.blogger.com/profile/13419990047910988897noreply@blogger.comtag:blogger.com,1999:blog-654279083390275842.post-39906344172434838842019-10-09T23:13:12.918+03:002019-10-09T23:13:12.918+03:00> нет, как и accumulate, код работает с любым п...<br />> нет, как и accumulate, код работает с любым предикатом, в примере это лямбда.<br /><br />Это я неправильно выразился.<br /><br />> А в остальных местах, где используются функции, код компактный и лаконичый, и не надо вспоминать как там раскрывать эти tuple-ы каждый раз.<br /><br />Это я понял. Но тут проблема в том, что нужно как-то выражать логику работы с аккумулятором. В данном конкретном примере она оказалась не очень тривиальной для меня, например.<br />eao197https://www.blogger.com/profile/17283739752119445290noreply@blogger.comtag:blogger.com,1999:blog-654279083390275842.post-78779858991433921062019-10-09T22:54:09.479+03:002019-10-09T22:54:09.479+03:00> Насколько я понял, ваш код -- это что-то типа...> Насколько я понял, ваш код -- это что-то типа std::accumulate, <br /><br />да<br /><br />> но только для частного случая с парсерами. <br /><br />нет, как и accumulate, код работает с любым предикатом, в примере это лямбда.<br /><br />>И, опять же насколько я понял, у вас вызов try_parse будет выполняться для всех парсеров в тупле. Т.к. я не увидел прерывания итерации, если какой-то из try_parse вернет false.<br /><br />Вы правы, единственно что, вызывается несколько раз лямбда, а не try_parse; try_parse больше не вызовется, после того как предыдуший вернул false. Чтобы сделать, как Вы хотите, мне надо было бы написать tuple_all, который бы работал как std::all_of(). <br /><br />А вообще моё предложение в улущение кода заключается как раз в написании небольшого набора таких вот функций типа tuple_reduce, tuple_all, и использовать только их. Таким образом неважно какой страшности там код, ибо он в одном месте, и его легко поменять. А в остальных местах, где используются функции, код компактный и лаконичый, и не надо вспоминать как там раскрывать эти tuple-ы каждый раз.Stanislav Mischenkohttps://www.blogger.com/profile/06713554207558018133noreply@blogger.comtag:blogger.com,1999:blog-654279083390275842.post-30299154514853717792019-10-09T20:43:48.381+03:002019-10-09T20:43:48.381+03:00@Unknown
Насколько я понял, ваш код -- это что-то...@Unknown<br /><br />Насколько я понял, ваш код -- это что-то типа std::accumulate, но только для частного случая с парсерами. И, опять же насколько я понял, у вас вызов try_parse будет выполняться для всех парсеров в тупле. Т.к. я не увидел прерывания итерации, если какой-то из try_parse вернет false.<br /><br />Что почитать не подскажу, т.к. где-то года с 2014-го в основном читаю блоги, ссылки на которые время от времени проскакивают на reddit-е. Отсюда и какое-то представление о возможностях современного C++.eao197https://www.blogger.com/profile/17283739752119445290noreply@blogger.comtag:blogger.com,1999:blog-654279083390275842.post-4886794775865081942019-10-09T20:36:20.542+03:002019-10-09T20:36:20.542+03:00@Grigory Demchenko
Интересно было бы посмотреть н...@Grigory Demchenko<br /><br />Интересно было бы посмотреть на то, как проход по туплу с объектами неизвестного типа реализуется в:<br /><br />a) других статически типизированных языках;<br />b) других статически типизированных языках без GC.eao197https://www.blogger.com/profile/17283739752119445290noreply@blogger.comtag:blogger.com,1999:blog-654279083390275842.post-86490284050726199452019-10-09T20:23:21.545+03:002019-10-09T20:23:21.545+03:00Потролю немного: это все из-за того, что C++ позво...Потролю немного: это все из-за того, что C++ позволяет легко реализовывать простые вещи и ты не задумываешься по нескольку часов и не спрашиваешь коллег о том, как сделать что-то настолько элементарное. Это говорит о мощи и элегантности языка.Grigory Demchenkohttps://www.blogger.com/profile/00767146690798788624noreply@blogger.comtag:blogger.com,1999:blog-654279083390275842.post-8800279987488959792019-10-09T20:10:48.367+03:002019-10-09T20:10:48.367+03:00Как только появилась возможность использовать выра...Как только появилась возможность использовать выражения свёртки весь этот ужасный код был выброшен :)<br />Кстати, лучше использовать шаблон вместо прямого std::tuple, тогда и классы вроде std::pair можно будет обработать.<br /><br />template class Tuple, typename... Types ><br />bool try_parse(const std::string & source, Tuple & parsers)<br />NNhttps://www.blogger.com/profile/16823052781877891385noreply@blogger.comtag:blogger.com,1999:blog-654279083390275842.post-70449688384388003672019-10-09T19:42:01.415+03:002019-10-09T19:42:01.415+03:00Позволю предложить свой вариант. Код ещё времён C+...Позволю предложить свой вариант. Код ещё времён C++11 (gcc 4.4). Если не скомпилируется, то заранее прошу прощения, на плюсах с тех пор не прогал. Кстати, если, конечно несложно, подскажите, что бы прочитать, дабы наверстать упущенное?<br />https://pastebin.com/AVDEFNu4Stanislav Mischenkohttps://www.blogger.com/profile/06713554207558018133noreply@blogger.com