Здравствуйте, Marty, Вы писали:
S>>Может вы просто не умеете его готовить?
M>Возможно. Научи, как правильно
Может вы покажите как у вас получается через жопу и с большим количеством писанины, чтобы читатели форума могли подсказать как улучшить ситуацию? Или признать вашу правоту.
А то без кода обсуждать можно разве что ваши дурные привычки к функциям на много сотен строк.
Так это только благодаря тебе она начинается, потому что ты if-ы используешь. А так-то вообще такие штуки на перегрузках делаются. И перегрузки можно делать по-разному. Можно, лямбды использовать, можно собственные функционалы. Можно через композицию, можно через монолиты. И здесь очень востребованы оказываются концепцы и констрейнты. И код реально выглядит красиво и профессионально, не что что твои if-ы, с энумами.
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, Marty, Вы писали:
M>Здравствуйте, so5team, Вы писали:
YV>>>>здесь
M>>>Спасибо, поизучаю. Но я бы не сказал, что это всё очень просто
S>>ИМХО, вот так гораздо проще: https://godbolt.org/z/YW839hjGY S>>Но идея та же самая.
M>Спс.
M>Правда, я пока на 17ом стандарте
Для C++17 у меня получилось что-то вроде: https://godbolt.org/z/YnehMhsT9
Но не покидает ощущение, что более продвинутые в современном C++ товарищи смогут сделать проще и компактнее.
M>и ещё вопрос, насколько это смогут поддерживать сишники
Еще один вариант: перенос специализации c операторов () визитора, на специализацию свободных функций:
struct a { std::string name{"a"};};
struct b { };
struct c { std::string name{"c"};};
struct d { };
using Variant = std::variant<a, b, c, d>;
template<typename T>
auto getName(const T& v) -> decltype(v.name) { return v.name; } // можнос специализировать типы по наличию поляinline std::string getName(const d& v) { return"type d"; } // можнос специализировать конкретные типы
// Все варианты типов, что не подошли под спефиализации. Наименее приоритетный вариант.inline std::string getName(...) { return""; }
template<typename ...T>
std::string getName(const std::variant<T...>& v) // Работа с variant обязательно шаблонная. Иначе можно попасть в рекурсивный вызов для не специализированного класса.
{
return std::visit( [](const auto& vi)->std::string { return getName(vi); }, v );
}
Плюсы:
В случае, когда конкретный тип заранее известен, будет сразу вызваны функции для конкретных типов, избегая диспетчеризации в visit.
Можно писать код в "распределенном" режиме, определяя getName для новых классов рядом с классом.
с++17 — достаточно. Можно и на c++11, но специализация по признаку наличия поля — сильно многословнее.
Здравствуйте, Marty, Вы писали:
M>Здравствуйте!
M>Я тут начал довольно активно пользоваться std::variant, и часто возникает задача — узнать, что там во варианте лежит. Также возникает задача получить лежащее там значение.
std::visit решает все озвученные вопросы без необходимости изобретать велосипеды, которые я поскипал.
M>ЗЗЗЫ А как variant может стать value less? И как это можно продетектить? Это из-за исключений может получиться, или есть другие способы?
2) Type-based value accessor: If v holds the alternative T, returns a reference to the value stored in v. Otherwise, throws std::bad_variant_access. The call is ill-formed if T is not a unique element of Types....
M>Я каждый раз для каждого variant делаю перечисление enum class, если мой variant — MyVariant, то перечисление — MyVariantKind. Имена альтернатив там в одном порядке. Пишу функцию GetKind и вроде норм. Пока было нужно всего несколько раз, и руками было достаточно просто. Но вообще идея мне понравилась, и я подумал, как бы это дело сделать совместимым с std.
Даже я, будучи паталогическим велосипедостроителем в терминальной стадии, немного в шоке от вашей идеи. Да не, даже много в шоке.
Здравствуйте, Marty, Вы писали:
M>>>Я тут начал довольно активно пользоваться std::variant, и часто возникает задача — узнать, что там во варианте лежит. Также возникает задача получить лежащее там значение.
L>>std::visit решает все озвученные вопросы без необходимости изобретать велосипеды, которые я поскипал.
M>Через жопу и с кучей писанины, да
Здравствуйте, Marty, Вы писали:
M>Захотелось: M>1) поиметь функцию типа std::index (которой на самом деле вроде нет), которая для variant'а вызывает его index() и кастит к моему enum — что тут лучше придумать? Или есть что-то такое, просто я не в курсе? M>2) аналог std::get. Кстати, я что-то всегда вызываю std::get, но, по идее, тут должен работать просто get через ADL, и я могу написать свою пачку перегрузок get в том же NS, где и variant, и всё будет работать просто через вызов get, так? M>3) holds_alternative, которая принимает variantKind и variant, и возвращает bool — тут по идее, так же как с get M>4) get_if — ну это уже на базе остального делается, вроде
По-моему, во всех этих вариантах писанины будет существенно больше, чем при использовании std::visit. Для каждого типа варианта нужно написать энум, потом скастить к этому энуму индекс, потом этот энум запихнуть в какой-то switch и в конечном итоге, скорее всего, вызвать для каждого случая какую-то функцию. Ты же не будешь писать пласты кода прямо внутри switch-а.
А через std::get твоя задача (УЗНАТЬ, что лежит в вариарианте) вообще не решается. Рассчитано на то, что ты сам знаешь, что лежит в варианте и вызываешь std::get с правильным индексом или типом, в противном случае получишь исключение.
Третий и четвёртый варианты — это проверить, а не узнать.
В общем, осваивай std::variant, и твоя жизнь сразу упростится.
--
Справедливость выше закона. А человечность выше справедливости.
int func(MyVariant v)
{
return std::visit([]<typename T>(const T& t) {
if constexpr (std::same_as<T, A>)
{
doSomething(t);
}
else if constexpr (std::same_as<T, B> or std::same_as<T, C>)
{
std::cout << "Error: B or C not allowed here\n";
return -1;
}
return 0; // OK
}, v);
}
И это не единственный, и не лучший, кстати, вариант использования — тут на перегрузках можно реально по красоте всё сделать.
И я привёл законченный работающий пример, а тебе ещё нужно наколбасить энумов к своим вариантам. В итоге нифига не проще будет, а будет реально куча.
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, Chorkov, Вы писали:
C>Плюсы: C>В случае, когда конкретный тип заренее известве, буджут сразу вызваны функции для конкретных типов, избегая диспетчеризации в visit.
Ещё один плюс — подключается ADL. Это значит, что getName становится тем, что в стандартной библиотеке обозначается как customization point.
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, Marty, Вы писали:
M>Или вот вот такой пример. Есть variant с парой десятков альтернатив. У половины альтернатив есть атрибут name, у других нет. Я хочу функцию, которая возвращает name, если он есть, или пустую строку, если его нет. Чтобы выше не парится, выписывая везде std::visit для получения этого атрибута.
Я тут начал довольно активно пользоваться std::variant, и часто возникает задача — узнать, что там во варианте лежит. Также возникает задача получить лежащее там значение.
Мне не нравится то, что std::get принимает std::size_t индекс, а std::variant::index() возвращает std::size_t, а std::holds_alternative принимает тип.
Я каждый раз для каждого variant делаю перечисление enum class, если мой variant — MyVariant, то перечисление — MyVariantKind. Имена альтернатив там в одном порядке. Пишу функцию GetKind и вроде норм. Пока было нужно всего несколько раз, и руками было достаточно просто. Но вообще идея мне понравилась, и я подумал, как бы это дело сделать совместимым с std.
Захотелось:
1) поиметь функцию типа std::index (которой на самом деле вроде нет), которая для variant'а вызывает его index() и кастит к моему enum — что тут лучше придумать? Или есть что-то такое, просто я не в курсе?
2) аналог std::get. Кстати, я что-то всегда вызываю std::get, но, по идее, тут должен работать просто get через ADL, и я могу написать свою пачку перегрузок get в том же NS, где и variant, и всё будет работать просто через вызов get, так?
3) holds_alternative, которая принимает variantKind и variant, и возвращает bool — тут по идее, так же как с get
4) get_if — ну это уже на базе остального делается, вроде
Как это всё лучше по красоте сделать?
ЗЫ Думаю запилить какой-нить генератор кода, который бы мне всю красоту генерил по простецкому описанию, и мой Variant, и VariantKind
ЗЗЫ Приглашается в тему Евгений Музыченко с примером своего compile-time скрипта на его гипотетическом диалекте C++, в котором есть МАКРОСЫ. Каким ему видится решение подобной задачи? Хочу почитать код такого решения.
ЗЗЗЫ А как variant может стать value less? И как это можно продетектить? Это из-за исключений может получиться, или есть другие способы?
Здравствуйте, landerhigh, Вы писали:
M>>Я тут начал довольно активно пользоваться std::variant, и часто возникает задача — узнать, что там во варианте лежит. Также возникает задача получить лежащее там значение.
L>std::visit решает все озвученные вопросы без необходимости изобретать велосипеды, которые я поскипал.
Через жопу и с кучей писанины, да
M>>ЗЗЗЫ А как variant может стать value less? И как это можно продетектить? Это из-за исключений может получиться, или есть другие способы?
L>Ну вроде да.
Здравствуйте, so5team, Вы писали:
M>>Мне не нравится то, что std::get принимает std::size_t индекс
S>Вы точно в этом уверены?
Точно:
1) Index-based value accessor: If v.index() == I, returns a reference to the value stored in v. Otherwise, throws std::bad_variant_access. The call is ill-formed if I is not a valid index in the variant.
M>>Я каждый раз для каждого variant делаю перечисление enum class, если мой variant — MyVariant, то перечисление — MyVariantKind. Имена альтернатив там в одном порядке. Пишу функцию GetKind и вроде норм. Пока было нужно всего несколько раз, и руками было достаточно просто. Но вообще идея мне понравилась, и я подумал, как бы это дело сделать совместимым с std.
S>Даже я, будучи паталогическим велосипедостроителем в терминальной стадии, немного в шоке от вашей идеи. Да не, даже много в шоке.
Здравствуйте, so5team, Вы писали:
L>>>std::visit решает все озвученные вопросы без необходимости изобретать велосипеды, которые я поскипал.
M>>Через жопу и с кучей писанины, да
S>Может вы просто не умеете его готовить?
Здравствуйте, Marty, Вы писали:
L>>std::visit решает все озвученные вопросы без необходимости изобретать велосипеды, которые я поскипал. M>Через жопу и с кучей писанины, да
Можно и через жопу. И даже с кучей писанины. В плюсах вообще можно всё.
Но зачем?
Здравствуйте, Marty, Вы писали:
L>>std::visit решает все озвученные вопросы без необходимости изобретать велосипеды, которые я поскипал.
M>Через жопу и с кучей писанины, да
R> std::visit([](auto&& t) {
R> std::cout << "Тут лежит " << t << std::endl;
R> }, v);
R>
R>А как бы ты хотел чтоб это выглядело? Можешь примерчик набросать?
Как-то так:
int func(MyVariant v)
{
auto kind = getKind(v);
if (kind==Kind::A)
{
doSomething(get<Kind::A>(v));
// doSomething(get(kind, v)); // или даже так
}
else if (kind==Kind::B || kind==Kind::C)
{
log << "Error: " << enum_serialize(kind) << " not allowed here\n";
return -1; // error - B or C not allowed here
}
else
{
return 0; // OK
}
//...
}
Здравствуйте, rg45, Вы писали:
R>По-моему, во всех этих вариантах писанины будет существенно больше, чем при использовании std::visit. Для каждого типа варианта нужно написать энум, потом скастить к этому энуму индекс,
Генератор кода
R>потом этот энум запихнуть в какой-то switch и в конечном итоге, скорее всего, вызвать для каждого случая какую-то функцию. Ты же не будешь писать пласты кода прямо внутри switch-а.
Или не вызвать, а выйти из текущей
R>А через std::get твоя задача (УЗНАТЬ, что лежит в вариарианте) вообще не решается. Рассчитано на то, что ты сам знаешь, что лежит в варианте и вызываешь std::get с правильным индексом или типом, в противном случае получишь исключение.
ну да, для узнать это get_if или holds_alternative
R>Третий и четвёртый варианты — это проверить, а не узнать.
R>В общем, осваивай std::variant, и твоя жизнь сразу упростится.
Я как-то осваивал std::variant, пиша (пися? писая?) тулзу с использованием LLVM. Мягко говоря, осталось впечатление, что это полный п надо бы как-то попроще всё это
А как мне выйти из функции, которая вызывает std::visit, по какой-нибудь альтернативе?
Ещё бесит, что в visit значение варианта передаётся после обработчика. Почему нельзя было его первым аргументом? Оно за портянкой лямбды теряется совсем
Здравствуйте, rg45, Вы писали:
R>В общем, осваивай std::variant, и твоя жизнь сразу упростится.
Или вот вот такой пример. Есть variant с парой десятков альтернатив. У половины альтернатив есть атрибут name, у других нет. Я хочу функцию, которая возвращает name, если он есть, или пустую строку, если его нет. Чтобы выше не парится, выписывая везде std::visit для получения этого атрибута.
auto kind = getKind(v);
name = getName(kind, v);
Как мне такое сделать?
Я пока портяночно написал, но вот думаю, как бы можно было бы это лучше сделать.
R>int func(MyVariant v)
R>{
R> return std::visit([]<typename T>(const T& t) {
R> if constexpr (std::same_as<T, A>)
R> {
R> doSomething(t);
R> }
R> else if constexpr (std::same_as<T, B> or std::same_as<T, C>)
R> {
R> std::cout << "Error: B or C not allowed here\n";
R> return -1;
R> }
R> return 0; // OK
R> }, v);
R>}
R>
в ветке doSomething(t); я не хочу выходить из func. Надо было там написать после if продолжение кода — //.... Поправлю для остальных
R>И это не единственный, и не лучший, кстати, вариант использования — тут на перегрузках можно реально по красоте всё сделать.
R>И я привёл законченный работающий пример, а тебе ещё нужно наколбасить энумов к своим вариантам. В итоге нифига не проще будет, а будет реально куча.
Генератор
А можно в рантайме, в какой-нибудь другой подсистеме, сформировать список альтернатив, которые нужно обработать, и передать в обработчик вместе с variant'ом?
Здравствуйте, rg45, Вы писали:
M>>у нас намечается простыня
R>Так это только благодаря тебе она начинается, потому что ты if-ы используешь. А так-то вообще такие штуки на перегрузках делаются. И перегрузки можно делать по-разному. Можно, лямбды использовать, можно собственные функционалы. Можно через композицию, можно через монолиты. И здесь очень востребованы оказываются концепцы и констрейнты. И код реально выглядит красиво и профессионально, не что что твои if-ы, с энумами.
Нет под рукой ссылки, где можно почитать такой прекрасный код?
О, шаблонная лямбда? Не встречал такого. Через жопу писал (auto a) и затем тип выводил через declspec/decay
M>>в ветке doSomething(t); я не хочу выходить из func. Надо было там написать после if продолжение кода — //.... Поправлю для остальных
R>Так просто убери else после if-а и пиши, что хочешь. Я же опирался на твой прототип.
Ну, может ты и прав.
А где бы всё же полюбоваться на божественный код с variant'ами? Для общего развития?
И как списки альтернатив передавать между подсистемами в рантайме?
Рабочие примеры, я уже задолбался писать, честно говоря. Но поверь, для того, чтобы добиться такого, требуется всего пол экрана вспомогательного повторно используемого кода.
--
Справедливость выше закона. А человечность выше справедливости.
на плюсах, разбираю сишечную метадату через CastXML. В принципе, уже сделал на говне, решил разобраться, как можно было бы сделать лучше. И да, мои if/else/switch для поддержки были бы гораздо проще кому-то из команды кроме меня.
ЗЫ А в частном порядке нельзя с тобой связаться, если ты не против? А то код проекта не могу показывать на публику, но в частном порядке думаю можно. А ты бы мог без труда найти дерьмовые решения, и показать на форуме только их, чтобы всем было полезно. Наверное, нахрен тебе не надо
Здравствуйте, Marty, Вы писали:
M>ЗЫ А в частном порядке нельзя с тобой связаться, если ты не против? А то код проекта не могу показывать на публику, но в частном порядке думаю можно. А ты бы мог без труда найти дерьмовые решения, и показать на форуме только их, чтобы всем было полезно. M>Наверное, нахрен тебе не надо
Можно. Только не сегодня, ладно?
Я сделаю все необходимые вспомогательные утилиты (благо их не так уж много), а ты придумай какой-нибудь синтетический, не очень сложный, но более-менее осмысленный пример, на котором можно будет обкатать. И выкладывай пример прямо сюда. Я на днях по свободке запилю.
--
Справедливость выше закона. А человечность выше справедливости.
Да выброси ты свои kind-ы, это вчерашний день. Ты ж пойми, эти кайнды тебе нужны только для того, чтобы сделать диспетченизацию и полиморфизм. Больше ни зачем.
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, rg45, Вы писали:
M>>Как мне такое сделать?
R>Да выброси ты свои kind-ы, это вчерашний день. Ты ж пойми, эти кайнды тебе нужны только для того, чтобы сделать диспетченизацию и полиморфизм. Больше ни зачем.
В целом, я понимаю, но современный сипипи много шума даёт, пытаюсь найти вариант, который бы использовал современный язык, но был бы прост для поддержки
Простите, а вот этот вот список {Kind::A, Kind::B, ...} -- он откуда возьмется?
Его программист должен ручками написать? Типа "я помню, что в структуре A есть атрибут name, поэтому Kind::A нужно включить в этот список". Правильно?
M>>Ну или как-то так
S>Простите, а вот этот вот список {Kind::A, Kind::B, ...} -- он откуда возьмется? S>Его программист должен ручками написать? Типа "я помню, что в структуре A есть атрибут name, поэтому Kind::A нужно включить в этот список". Правильно?
Типа того. Но в моей задаче я разбирал XML, можно было оттуда выцепить и сгенерить код
Здравствуйте, Marty, Вы писали:
S>>Простите, а вот этот вот список {Kind::A, Kind::B, ...} -- он откуда возьмется? S>>Его программист должен ручками написать? Типа "я помню, что в структуре A есть атрибут name, поэтому Kind::A нужно включить в этот список". Правильно?
M>Типа того. Но в моей задаче я разбирал XML, можно было оттуда выцепить и сгенерить код
Ну так говно же.
Современный C++ позволяет сделать это проще. Выше тов.YuriV уже показал набросок.
Здравствуйте, Marty, Вы писали:
M>у нас намечается простыня
Это у вас намечается
if constexpr(std::is_same_v(t, typeA) || std::is_same_v(t, typeB))
{
}
M>А как мне выйти из функции, которая вызывает std::visit, по какой-нибудь альтернативе?
Не распарсил.
M>Ещё бесит, что в visit значение варианта передаётся после обработчика. Почему нельзя было его первым аргументом? Оно за портянкой лямбды теряется совсем
Ну, у меня это единственная претензия к std::visit.
Здравствуйте, landerhigh, Вы писали:
M>>Ещё бесит, что в visit значение варианта передаётся после обработчика. Почему нельзя было его первым аргументом? Оно за портянкой лямбды теряется совсем
L>Ну, у меня это единственная претензия к std::visit.
Ну, почему в std::visit так сделано, понятно — потому что там это не единичное значение, а вариадик. А для одного значения действительно удобнее, когда сначала идет вариант, а за ним обработчики. Вот поэтому я и делаю обычно несложную обертку вокруг std::visit, типа как показано здесь
. А заодно это обертка включает в себя и overloaded, и использующий код получается аккуратным и очищенным от лишних технических подробностей, типа как здесь
Здравствуйте, Marty, Вы писали:
M>ЗЫ А в частном порядке нельзя с тобой связаться, если ты не против? А то код проекта не могу показывать на публику, но в частном порядке думаю можно. А ты бы мог без труда найти дерьмовые решения, и показать на форуме только их, чтобы всем было полезно.
Я смотрю, здесь уже накидали рабочих примеров, так что мне и добавить особо нечего. Дальше уже эту идею можно развивать в прикладной области с использованием концептов/констрейнтов и с добавлением синтаксического сахара
Здравствуйте, so5team, Вы писали:
YV>>>здесь
M>>Спасибо, поизучаю. Но я бы не сказал, что это всё очень просто
S>ИМХО, вот так гораздо проще: https://godbolt.org/z/YW839hjGY S>Но идея та же самая.
Спс.
Правда, я пока на 17ом стандарте, и ещё вопрос, насколько это смогут поддерживать сишники
Здравствуйте, so5team, Вы писали:
S>Для C++17 у меня получилось что-то вроде: https://godbolt.org/z/YnehMhsT9 S>Но не покидает ощущение, что более продвинутые в современном C++ товарищи смогут сделать проще и компактнее.
Не претендую на звание более продвинутого, но свой вариант предложу, всё-таки: