Generic function to convert boost::any to boost::variant

前端 未结 2 1874
轮回少年
轮回少年 2020-12-21 05:19

Assume that you have a boost::any object and a boost::variant object.

I\'m looking for a generic function convert, that takes

相关标签:
2条回答
  • 2020-12-21 06:06

    Let's enclose all code in struct templated by variant type

    template<class VAR>
    struct ExtractorGenerator
    {
        using Extractor = std::function<boost::optional<VAR>(boost::any const &)>;
        std::vector<Extractor> extractors;
    
        template<class T> 
        static boost::optional<VAR> tryCast(boost::any const & arg);
    
        template<class T> 
        void operator()(T);
    };
    

    You can easily write a function that for given type tries to convert boost::any to variant of this type

    template<class VAR>
    template<class T> 
    boost::optional<VAR> ExtractorGenerator<VAR>::tryCast(boost::any const & arg)
    { 
        T const * val = boost::any_cast<T>(&arg);
        return val == nullptr ? boost::none : boost::make_optional(VAR{*val});
    }
    

    Now using boost::mpl you can iterate through all variant types to generate function for each variant's type

    template<class VAR>
    template<class T> void ExtractorGenerator<VAR>::operator()(T)
    {
        extractors.push_back(Extractor::tryCast<T>);
    }
    
    typedef boost::variant<int, std::string, char> MyVariant;
    ExtractorGenerator<MyVariant> generator;
    boost::mpl::for_each<MyVariant::types>(boost::ref(generator));
    

    And now you just apply all created functions:

    std::vector<MyVariant> extractedVals;
    for (auto fun : extractor.extractors)
    {
        boost::optional<MyVariant> extracted = fun(val);
        if (extracted)
            extractedVals.push_back(extracted.get());
    }
    
    0 讨论(0)
  • 2020-12-21 06:17

    You can call boost::any_cast for each of the types within boost::variant and stop when the first cast succeeded:

    #include <iostream>
    #include <utility>
    #include <stdexcept>
    #include <sstream>
    
    #include <boost/any.hpp>
    #include <boost/variant.hpp>
    #include <boost/type_index.hpp>
    #include <boost/mpl/size.hpp>
    #include <boost/mpl/at.hpp>
    
    template <typename Sequence>
    struct mpl_sequence_to_std_tuple
    {
    template <std::size_t... Is>
    static auto x(std::index_sequence<Is...>) -> std::tuple<typename boost::mpl::at_c<Sequence, Is>::type...>;
    
    using type = decltype(x(std::make_index_sequence<boost::mpl::size<Sequence>::type::value>{}));
    };
    
    struct signal_conversion_success{};
    
    template <typename T, typename Variant>
    void try_convert(const boost::any& any, Variant& var)
    {
        try
        {
            var = boost::any_cast<T>(any);
            throw signal_conversion_success{};
        }
        catch(const boost::bad_any_cast &)
        {
        }
    }
    
    template <typename T, typename... Ts>
    std::string parameter_pack_to_string(const std::string& separator = ", ")
    {
        std::stringstream ss;
        ss << boost::typeindex::type_id<T>().pretty_name();
        auto l = {0, (void(ss << separator << boost::typeindex::type_id<Ts>().pretty_name()),0)...};
        std::ignore = l;
        return ss.str();
    }
    
    template <typename Variant, typename...Ts>
    void do_convert(const boost::any& any, Variant& var, std::tuple<Ts...>)
    {
        bool success = false;
    
        try {
            auto l = {0, (void(try_convert<Ts>(any, var)), 0)... };
            std::ignore = l;
        }
        catch(const signal_conversion_success&)
        {
            success = true;
        }
    
        if (!success)
        {
            std::stringstream ss;
            ss << "cannot convert this boost::any instance to any of the following types: ";
            ss << parameter_pack_to_string<Ts...>();
            throw std::invalid_argument(ss.str());
        }
    }
    
    template<typename Variant>
    void convert(const boost::any& any, Variant& var)
    {
      using Tuple = typename mpl_sequence_to_std_tuple<typename Variant::types>::type;
      do_convert(any, var, Tuple{});
    }
    
    struct print_visitor : public boost::static_visitor<void>
    {
        template <typename T>
        void operator()(T&& t) const
        {
            std::cout << boost::typeindex::type_id<T>().pretty_name() << ": " << std::forward<T>(t) << std::endl;
        }
    };
    
    int main()
    {
        using Variant = boost::variant<int, std::string>;
        boost::any any = std::string("Hello World");
        Variant var;
        convert(any, var);
        boost::apply_visitor(print_visitor(), var);
    }
    

    live example

    In case none of the casts succeeded, an exception is thrown, see the following live example.

    0 讨论(0)
提交回复
热议问题