[MP11] Initialize the Nth alternative of an std::variant based on a runtime index

Hi. I'm modernizing some code, to use std::variant instead of ad-hoc peudo-variant structs. These structs need to be encoded/decoded via avro::codec_traits specializations, but given that std::variant is variadic, I'm struggling a bit. I've done my research and found peter's [1] and also looked at [2], which I thought might work, but does not, and I don't understand the errors I'm getting with MSVC 2019 in C++17 mode. The code is below, with also a subset of the compiler errors I'm getting. [1] https://www.reddit.com/r/cpp/comments/f8cbzs/creating_stdvariant_based_on_index_at_runtime/filw8g7?utm_source=share&utm_medium=web2x&context=3 [2] https://www.boost.org/doc/libs/develop/libs/mp11/doc/html/mp11.html#mp_with_... template <> struct codec_traits<std::monostate> { static void encode(Encoder& e, const std::monostate&) { e.encodeNull(); } static void decode(Decoder& d, std::monostate&) { d.decodeNull(); } }; template <typename... Ts> struct codec_traits<std::variant<Ts...>> { static void encode(Encoder& e, const std::variant<Ts...>& var) { e.encodeUnionIndex(var.index()); std::visit( [&e](const auto& alternative) { avro::encode(e, alternative); }, var ); } static void decode(Decoder& d, std::variant<Ts...>& var) { const size_t index = d.decodeUnionIndex(); if (index >= sizeof...(Ts)) { throw avro::Exception("Invalid Union index"); } auto& alternative = boost::mp11::mp_with_index<sizeof...(Ts)>( index, [&var](auto I) { return var.emplace<I>(); // I is mp_size_t<index>{} here } ) ; avro::decode(d, alternative); } }; 1>Messages.cpp 1>D:\pdgm\trunk\psc3\SharedComponents\src\lib\pdgm\eml\etp12\Messages.hpp(70,1): error C2220: the following warning is treated as an error 1>D:\pdgm\trunk\psc3\SharedComponents\src\lib\pdgm\eml\etp12\Messages.hpp(70,1): warning C4239: nonstandard extension used: 'initializing': conversion from 'std::monostate' to 'std::monostate &' 1>D:\pdgm\trunk\psc3\SharedComponents\src\lib\pdgm\eml\etp12\Messages.hpp(70,1): message : A non-const reference may only be bound to an lvalue 1>D:\pdgm\trunk\psc3\SharedComponents\src\lib\pdgm\eml\etp12\Messages.hpp(70): message : while compiling class template member function 'void avro::codec_traits<T>::decode(avro::Decoder &,std::variant<std::monostate,int64_t,double> &)' 1> with 1> [ 1> T=pdgm::eml::etp12::Datatypes::IndexValue_item_v 1> ] 1>D:\pdgm\kits\trunk\Avrocpp\1.9.2-Boost-1.74.0\Win_x64_10_v16_debug\include\avro/Specific.hh(341): message : see reference to function template instantiation 'void avro::codec_traits<T>::decode(avro::Decoder &,std::variant<std::monostate,int64_t,double> &)' being compiled 1> with 1> [ 1> T=pdgm::eml::etp12::Datatypes::IndexValue_item_v 1> ] 1>D:\pdgm\kits\trunk\Avrocpp\1.9.2-Boost-1.74.0\Win_x64_10_v16_debug\include\avro/Specific.hh(333): message : see reference to class template instantiation 'avro::codec_traits<T>' being compiled 1> with 1> [ 1> T=pdgm::eml::etp12::Datatypes::IndexValue_item_v 1> ] 1>D:\pdgm\trunk\psc3\SharedComponents\src\lib\pdgm\eml\etp12\codegen/Messages.i.ipp(1527): message : see reference to function template instantiation 'void avro::encode<pdgm::eml::etp12::Datatypes::IndexValue_item_v>(avro::Encoder &,const T &)' being compiled 1> with 1> [ 1> T=pdgm::eml::etp12::Datatypes::IndexValue_item_v 1> ] 1>D:\pdgm\kits\trunk\boost\1.74.0\Win_x64_10_v16_debug\include\boost/mp11/detail/mp_with_index.hpp(87,1): error C2440: 'return': cannot convert from '__int64' to 'std::monostate' 1>D:\pdgm\kits\trunk\boost\1.74.0\Win_x64_10_v16_debug\include\boost/mp11/detail/mp_with_index.hpp(87,1): message : No constructor could take the source type, or constructor overload resolution was ambiguous 1>D:\pdgm\kits\trunk\boost\1.74.0\Win_x64_10_v16_debug\include\boost/mp11/detail/mp_with_index.hpp(371): message : see reference to function template instantiation 'std::monostate boost::mp11::detail::mp_with_index_impl_<3>::call<0,_Ty>(size_t,F &&)' being compiled 1> with 1> [ 1> _Ty=avro::codec_traits<pdgm::eml::etp12::Datatypes::IndexValue_item_v>::decode::<lambda_7e593df011d98e42859c6ecb357b3e3f>, 1> F=avro::codec_traits<pdgm::eml::etp12::Datatypes::IndexValue_item_v>::decode::<lambda_7e593df011d98e42859c6ecb357b3e3f> 1> ] 1>D:\pdgm\trunk\psc3\SharedComponents\src\lib\pdgm\eml\etp12\Messages.hpp(70): message : see reference to function template instantiation 'std::monostate boost::mp11::mp_with_index<3,avro::codec_traits<T>::decode::<lambda_7e593df011d98e42859c6ecb357b3e3f>>(size_t,F &&)' being compiled 1> with 1> [ 1> T=pdgm::eml::etp12::Datatypes::IndexValue_item_v, 1> F=avro::codec_traits<pdgm::eml::etp12::Datatypes::IndexValue_item_v>::decode::<lambda_7e593df011d98e42859c6ecb357b3e3f> 1> ] 1>D:\pdgm\trunk\psc3\SharedComponents\src\lib\pdgm\eml\etp12\Messages.hpp(70): message : while compiling class template member function 'void avro::codec_traits<T>::decode(avro::Decoder &,std::variant<std::monostate,int64_t,double> &)' 1> with 1> [ 1> T=pdgm::eml::etp12::Datatypes::IndexValue_item_v 1> ] 1>D:\pdgm\kits\trunk\Avrocpp\1.9.2-Boost-1.74.0\Win_x64_10_v16_debug\include\avro/Specific.hh(341): message : see reference to function template instantiation 'void avro::codec_traits<T>::decode(avro::Decoder &,std::variant<std::monostate,int64_t,double> &)' being compiled 1> with 1> [ 1> T=pdgm::eml::etp12::Datatypes::IndexValue_item_v 1> ]

Sorry, email went out before intended... Maybe easier to troubleshoot than the partial code I posted earlier, I've modifed Peter's Godbolt sample from [1] to replicate (I think) what I need. Short and long links below. I'm not familiar with Godbolt, hopefully those links are correct. I'd appreciate some insights on this issue. Thanks, --DD https://godbolt.org/z/3xhjnMn5G https://godbolt.org/#z:OYLghAFBqd5QCxAYwPYBMCmBRdBLAF1QCcAaPECAM1QDsCBlZAQwB... Just in case, here's the code (I modified to add init_variant and the last two lines in main, and this does not compile) inline: #include <variant> #include <iostream> #include <boost/mp11.hpp> using namespace boost::mp11; template<class... T, class... A> std::variant<T...> make_variant( std::size_t index, A&&... a ) { return mp_with_index<sizeof...(T)>( index, [&](auto I){ return std::variant<T...>( std::in_place_index<I>, std::forward<A>( a )... ); }); } template<class... T> auto init_variant( std::size_t index, std::variant<T...>& v) { return mp_with_index<sizeof...(T)>( index, [&v](auto I){ return v.emplace<I>(); }); } //////////////////////////////////////////////////////////////////////////////// // test struct A { void info() { std::cout << "A"; } }; struct B { void info() { std::cout << "B"; } }; int main() { std::size_t index = 1; auto v = make_variant<A, B>(index); std::visit([](auto&& o) { o.info(); }, v); auto& alt0 = init_variant(0, v); alt0.info(); auto& alt1 = init_variant(1, v); alt1.info(); } On Wed, Jun 30, 2021 at 3:27 PM Dominique Devienne <ddevienne@gmail.com> wrote:
Hi. I'm modernizing some code, to use std::variant instead of ad-hoc peudo-variant structs. These structs need to be encoded/decoded via avro::codec_traits specializations, but given that std::variant is variadic, I'm struggling a bit. I've done my research and found peter's [1] and also looked at [2], which I thought might work, but does not, and I don't understand the errors I'm getting with MSVC 2019 in C++17 mode.
The code is below, with also a subset of the compiler errors I'm getting.
[1] https://www.reddit.com/r/cpp/comments/f8cbzs/creating_stdvariant_based_on_index_at_runtime/filw8g7?utm_source=share&utm_medium=web2x&context=3 [2] https://www.boost.org/doc/libs/develop/libs/mp11/doc/html/mp11.html#mp_with_...
template <> struct codec_traits<std::monostate> { static void encode(Encoder& e, const std::monostate&) { e.encodeNull(); }
static void decode(Decoder& d, std::monostate&) { d.decodeNull(); } };
template <typename... Ts> struct codec_traits<std::variant<Ts...>> { static void encode(Encoder& e, const std::variant<Ts...>& var) { e.encodeUnionIndex(var.index()); std::visit( [&e](const auto& alternative) { avro::encode(e, alternative); }, var ); }
static void decode(Decoder& d, std::variant<Ts...>& var) { const size_t index = d.decodeUnionIndex(); if (index >= sizeof...(Ts)) { throw avro::Exception("Invalid Union index"); } auto& alternative = boost::mp11::mp_with_index<sizeof...(Ts)>( index, [&var](auto I) { return var.emplace<I>(); // I is mp_size_t<index>{} here } ) ; avro::decode(d, alternative); } };
1>Messages.cpp 1>D:\pdgm\trunk\psc3\SharedComponents\src\lib\pdgm\eml\etp12\Messages.hpp(70,1): error C2220: the following warning is treated as an error 1>D:\pdgm\trunk\psc3\SharedComponents\src\lib\pdgm\eml\etp12\Messages.hpp(70,1): warning C4239: nonstandard extension used: 'initializing': conversion from 'std::monostate' to 'std::monostate &' 1>D:\pdgm\trunk\psc3\SharedComponents\src\lib\pdgm\eml\etp12\Messages.hpp(70,1): message : A non-const reference may only be bound to an lvalue 1>D:\pdgm\trunk\psc3\SharedComponents\src\lib\pdgm\eml\etp12\Messages.hpp(70): message : while compiling class template member function 'void avro::codec_traits<T>::decode(avro::Decoder &,std::variant<std::monostate,int64_t,double> &)' 1> with 1> [ 1> T=pdgm::eml::etp12::Datatypes::IndexValue_item_v 1> ] 1>D:\pdgm\kits\trunk\Avrocpp\1.9.2-Boost-1.74.0\Win_x64_10_v16_debug\include\avro/Specific.hh(341): message : see reference to function template instantiation 'void avro::codec_traits<T>::decode(avro::Decoder &,std::variant<std::monostate,int64_t,double> &)' being compiled 1> with 1> [ 1> T=pdgm::eml::etp12::Datatypes::IndexValue_item_v 1> ] 1>D:\pdgm\kits\trunk\Avrocpp\1.9.2-Boost-1.74.0\Win_x64_10_v16_debug\include\avro/Specific.hh(333): message : see reference to class template instantiation 'avro::codec_traits<T>' being compiled 1> with 1> [ 1> T=pdgm::eml::etp12::Datatypes::IndexValue_item_v 1> ] 1>D:\pdgm\trunk\psc3\SharedComponents\src\lib\pdgm\eml\etp12\codegen/Messages.i.ipp(1527): message : see reference to function template instantiation 'void avro::encode<pdgm::eml::etp12::Datatypes::IndexValue_item_v>(avro::Encoder &,const T &)' being compiled 1> with 1> [ 1> T=pdgm::eml::etp12::Datatypes::IndexValue_item_v 1> ]
1>D:\pdgm\kits\trunk\boost\1.74.0\Win_x64_10_v16_debug\include\boost/mp11/detail/mp_with_index.hpp(87,1): error C2440: 'return': cannot convert from '__int64' to 'std::monostate' 1>D:\pdgm\kits\trunk\boost\1.74.0\Win_x64_10_v16_debug\include\boost/mp11/detail/mp_with_index.hpp(87,1): message : No constructor could take the source type, or constructor overload resolution was ambiguous 1>D:\pdgm\kits\trunk\boost\1.74.0\Win_x64_10_v16_debug\include\boost/mp11/detail/mp_with_index.hpp(371): message : see reference to function template instantiation 'std::monostate boost::mp11::detail::mp_with_index_impl_<3>::call<0,_Ty>(size_t,F &&)' being compiled 1> with 1> [ 1> _Ty=avro::codec_traits<pdgm::eml::etp12::Datatypes::IndexValue_item_v>::decode::<lambda_7e593df011d98e42859c6ecb357b3e3f>, 1> F=avro::codec_traits<pdgm::eml::etp12::Datatypes::IndexValue_item_v>::decode::<lambda_7e593df011d98e42859c6ecb357b3e3f> 1> ] 1>D:\pdgm\trunk\psc3\SharedComponents\src\lib\pdgm\eml\etp12\Messages.hpp(70): message : see reference to function template instantiation 'std::monostate boost::mp11::mp_with_index<3,avro::codec_traits<T>::decode::<lambda_7e593df011d98e42859c6ecb357b3e3f>>(size_t,F &&)' being compiled 1> with 1> [ 1> T=pdgm::eml::etp12::Datatypes::IndexValue_item_v, 1> F=avro::codec_traits<pdgm::eml::etp12::Datatypes::IndexValue_item_v>::decode::<lambda_7e593df011d98e42859c6ecb357b3e3f> 1> ] 1>D:\pdgm\trunk\psc3\SharedComponents\src\lib\pdgm\eml\etp12\Messages.hpp(70): message : while compiling class template member function 'void avro::codec_traits<T>::decode(avro::Decoder &,std::variant<std::monostate,int64_t,double> &)' 1> with 1> [ 1> T=pdgm::eml::etp12::Datatypes::IndexValue_item_v 1> ] 1>D:\pdgm\kits\trunk\Avrocpp\1.9.2-Boost-1.74.0\Win_x64_10_v16_debug\include\avro/Specific.hh(341): message : see reference to function template instantiation 'void avro::codec_traits<T>::decode(avro::Decoder &,std::variant<std::monostate,int64_t,double> &)' being compiled 1> with 1> [ 1> T=pdgm::eml::etp12::Datatypes::IndexValue_item_v 1> ]

Solved, by moving the decode() inside the lambda, and adding the `.template ` for GCC (MSVC was happy w/o it...) A miraculous inspiration I guess. Unusual the ML is silent like this. Must have been a question asked the wrong way. --DD static void decode(Decoder& d, std::variant<Ts...>& var) { const size_t index = d.decodeUnionIndex(); if (index >= sizeof...(Ts)) { throw avro::Exception("Invalid Union index"); } boost::mp11::mp_with_index<sizeof...(Ts)>( index, [&](auto Idx) { constexpr size_t alternative_index = Idx; avro::decode(d, var.template emplace<alternative_index>()); } ); } On Wed, Jun 30, 2021 at 3:27 PM Dominique Devienne <ddevienne@gmail.com>
wrote:
Hi. I'm modernizing some code, to use std::variant instead of ad-hoc peudo-variant structs. These structs need to be encoded/decoded via avro::codec_traits specializations, but given that std::variant is variadic, I'm struggling a bit. I've done my research and found peter's [1] and also looked at [2],
which I thought might work, but does not, and I don't understand the
errors I'm getting with MSVC 2019 in C++17 mode.
participants (1)
-
Dominique Devienne