c++templatesboost-variantboost-preprocessorboost-any

Boost Any to Boost Variant using Boost Preprocessor


In my projects I'm using boost::any and boost::variant exhaustively. For this a general conversion routine from boost::any to boost::variant was devised in my previous question Generic function to convert boost::any to boost::variant. Many thanks to the people helping me out.

The found solution worked for me without any problems, but had some serious drawback. The code bloat produced by the fully templatized solution could be prohibitive and sometimes unnecessary for simple conversion. (I didn't mentioned the compilation times.)

Now I got the idea to let template specialization work for the easy (non-general) conversions, but I found the necessary code tedious and error-prone to type. Especially, if one has to do this task over and over again.

The following code snippet illustrates the problem. Image that in a typical application one might have up 20 types or more!

#include <boost/preprocessor.hpp>
#include <boost/variant.hpp>
#include <boost/any.hpp>
#include <boost/optional.hpp>
#include <iostream>

template<typename VARIANT> 
boost::optional<VARIANT> anyToVariant(const boost::any& any) {
    boost::optional<VARIANT> ret;
    // General implementation omitted. 
    // The implementation is lengthy and produces an enormous code bloat. In some cases it is the best solution.
    return ret;
}

// Specialized Template reduces code bloat. But is error-prone to type write for every new variant type.
template<>
boost::optional <boost::variant<int, double, std::string>> anyToVariant(const boost::any& any) {
    boost::optional<boost::variant<int, double, std::string>> ret;
    if (any.type() == typeid(int)) {
        ret = boost::any_cast<int>(any);
    }
    if (any.type() == typeid(double)) {
        ret = boost::any_cast<double>(any);
    }
    if (any.type() == typeid(std::string)) {
        ret = boost::any_cast<std::string>(any);
    }
    return ret;
}

// Should be implemented with boost preprocessor
#define BOOST_ANY_TO_VARIANT(TypeList) 

// Better would be a macro like this
BOOST_ANY_TO_VARIANT(int, double, std::string); // Create the above template specialization

int main() {
    boost::variant<int, double, std::string> v;
    boost::any x = 4;
    v=*anyToVariant<boost::variant<int, double, std::string>>(x);
}

A better solution would be of course a macro, but unfortunately I was not able to implement this macro with boost-preprocessor. I'm still lacking the skills, but I'm sure it could be done.

Can anyone with experience in boost-preprocessor help me out with my problem?

The best solution I could imagine would be a macro like the following:

#define BOOST_ANY_TO_VARIANT(VariantType) \ 
// Magic?

typedef boost::variant<int, std::string, double> MyVariant;

BOOST_ANY_TO_VARIANT(MyVariant)

But I doubt that this solution is achievable.


Solution

  • Here you go:

    #define ANY_TO_VARIANT_OP_VARIANT(typeSeq) \
        boost::optional<boost::variant<BOOST_PP_SEQ_ENUM(typeSeq)>>
    
    #define ANY_TO_VARIANT_CONVERT_AND_RETURN(r, data, elem) \
        if (any.type() == typeid(elem)) { \
            return Ret{boost::any_cast<elem>(any)}; \
        }
    
    #define SPECIALIZE_BOOST_ANY_TO_VARIANT(typeSeq) \
        template<> \
        ANY_TO_VARIANT_OPT_VARIANT(typeSeq) anyToVariant(const boost::any& any) { \
            using Ret = ANY_TO_VARIANT_OPT_VARIANT(typeSeq); \
            BOOST_PP_SEQ_FOR_EACH(ANY_TO_VARIANT_CONVERT_AND_RETURN, ~, typeSeq) \
            return Ret{}; \
        }
    

    Usage:

    SPECIALIZE_BOOST_ANY_TO_VARIANT((int)(double)(std::string))
    

    See it live on Coliru