c++templatesboostboost-variantboost-any

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?


Solution

  • 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());
    }