2

Can you please explain why this construct works:

typedef boost::make_recursive_variant<
    boost::blank,
    std::string,
    std::vector< std::string >,
    std::vector< int32_t >,
    std::vector< int64_t >,
    std::vector< double >,
    std::vector< std::complex<double> >,
    std::map< std::string, boost::recursive_variant_ > 
>::type Variant; 

And this doesn't:

typedef boost::make_recursive_variant<
    boost::blank,
    std::string,
    std::vector< std::string >,
    std::vector< int32_t >,
    std::vector< int64_t >,
    std::vector< double >,
    std::vector< std::complex<double> >,
    std::unordered_map< std::string, boost::recursive_variant_ > 
>::type Variant;

Unfortunately, I can't comprehend error message. It says something about

  static int initialize(void* dest, param_T operand)
                        ^
/usr/include/boost/variant/detail/initializer.hpp:104:24: note:   no known conversion for argument 2 from ‘const std::unordered_map<std::__cxx11::basic_string<char>, boost::variant<boost::detail::variant::recursive_flag<boost::blank>,

//Skipped 

to ‘boost::detail::variant::make_initializer_node::apply<boost::mpl::pair<boost::detail::variant::make_initializer_node::apply<boost::mpl::pair<boost::detail::variant::make_initializer_node::apply<boost::mpl::pair<boost::detail::variant::make_initializer_node::apply<boost::mpl::pair<boost::detail::variant::make_initializer_node::apply<boost::mpl::pair<boost::detail::variant::make_initializer_node::apply<boost::mpl::pair<boost::detail::variant::make_initializer_node::apply<boost::mpl::pair<boost::detail::variant::initializer_root, mpl_::int_<0> >, boost::mpl::l_iter<boost::mpl::l_item<mpl_::long_<8l>,

Ubuntu 16.04, gcc 5.4.0 20160609, boost 1.58.

Minor Threat
  • 2,025
  • 1
  • 18
  • 32
  • Do you still get the error if you use a boost::unordered_map? Also, which boost version are you using? I remember some trouble with boost variants and unordered_map before boost 1.64 (although I think it was hash related and would only affect argument 1 of the unordered_map). – Tim Smit Mar 15 '18 at 11:23
  • I think your problem might be due to a bad initializer. If so, make a **SSCCE** (http://sscce.org) and save us all some time – sehe Mar 15 '18 at 12:47
  • 1
    This code is being instantiated in some hairy SWIG output. Yes, I'll try to isolate the case. – Minor Threat Mar 15 '18 at 13:09
  • Good luck! The bad news is, it's work. The good news is (a) it will solve your problem (b) it will probably show you that you were capable of solving it all along :) – sehe Mar 15 '18 at 13:48

2 Answers2

2

The problem seems to be related to your particular setup. I couldn't reproduce it with all the below configurations.

Also, sampling tests across the board that include

Variant v = std::unordered_map<std::string, Variant> { {"hello", Variant{"world"} } };

inside main all also compiled without a hitch.

GCC with libstdc++




Clang with libc++



sehe
  • 374,641
  • 47
  • 450
  • 633
2

Hah. Your comment casually mentioning SWIG made a lightbulb come on in my head.

I wouldn't trust SWIG on anything involving boost::[make_recursive_]variant. Now that you mentioned that, it is 99% clear to me that SWIG fails to understand the wrapper types involved. Mystery solved.

The good thing is, you don't need it if you can use boost::unordered_map. This is because due to implementation details std::unordered_map may require the mapped-type to be complete at time of (shallow) instantiation: How to have an unordered_map where the value type is the class it's in?.

Live On Coliru

#include <boost/variant.hpp>
#include <complex>
#include <string>
#include <vector>
#include <boost/unordered_map.hpp>

using boost::unordered_map;

struct Variant : boost::variant<
                            boost::blank,
                            std::string,
                            std::vector< std::string >,
                            std::vector< int32_t >,
                            std::vector< int64_t >,
                            std::vector< double >,
                            std::vector< std::complex<double> >,
                            unordered_map< std::string, Variant >
                         > 
{
    using base_type = boost::variant<
            boost::blank,
            std::string,
            std::vector< std::string >,
            std::vector< int32_t >,
            std::vector< int64_t >,
            std::vector< double >,
            std::vector< std::complex<double> >,
            unordered_map< std::string, Variant >
         >;

    using base_type::base_type;
    using base_type::operator=;
};

int main() {
    Variant v = unordered_map<std::string, Variant> { {"hello", Variant{"world"} } };
}

The reason this works is because the map is a node-based container: it already decouples the allocation of the mapped type from its physical layout, which is the chief reason you'd need the recursive type wrapper.

See also here for another situation where this is explained in more detail: Recursive using declaration with boost variant

sehe
  • 374,641
  • 47
  • 450
  • 633