2

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.

Community
  • 1
  • 1
Aleph0
  • 5,816
  • 4
  • 29
  • 80
  • So, just to be clear, the goal of the macro is to generate the definition of the template specialization, *not* convert a `boost::any` on the spot? – Quentin Jan 27 '17 at 09:25
  • You got it. The macro should just generate the code above. – Aleph0 Jan 27 '17 at 09:26

1 Answers1

2

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

Quentin
  • 62,093
  • 7
  • 131
  • 191
  • Many thanks. I'll check your solution. Seems I really have to deal with `boost-preprocessor` a little bit more. The solution was way shorter that I would expect. – Aleph0 Jan 27 '17 at 11:06
  • I was not even aware of BOOST_PP_REMOVE_PARENS! This was already the point I'm failing to made progress. – Aleph0 Jan 27 '17 at 11:09
  • @FrankSimon I think you could also use `BOOST_PP_SEQ_ENUM(typeSeq)`. – llonesmiz Jan 27 '17 at 12:08