Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
988 views
in Technique[技术] by (71.8m points)

c++ - Generic function to convert boost::any to boost::variant

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

I'm looking for a generic function convert, that takes a template parameter T being a specialized boost::variant e.g. boost::variant<int, std::string> and magically converts the boost::any to one of the available types of the given boost::variant.

template<T>
T convert(const boost::any& any) {
   // Some generic conversion code here or throw exception if conversion is not possible!
}

int main(int argc, char** args) {
    typedef boost::variant<int, std::string> TVar;

    boost::any any="Hello World";
    TVar variant=convert<TVar>(any);
    // variant contains "Hello World"
    return 0;
}

I'm wondering if it is possible to write such a function or if it might be impossible for some reason?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...