MoustacheSpy
MoustacheSpy

Reputation: 763

How can I return a boost::variant made from a subset of the types contained in boost::varaint return type

I have 4 functions:

boost::variant<type1,type2,type3>processFile(const char* file){
     if(----expression that if true means I have to process as type 1----)
          return processType1(file);   //this just returns type1
     else if(----expression that if true means I have to process as either type 1 or type 2----)
           return processType23(file); //this returns boost::variant<type2,type3>. This also calls 2 more processing functions depending on type.
}

processType23 takes a script file which will determine which type gets sent back. I want to keep the determination of the type in this file. However I cannot return the boost::variant. I get the following error:

error: could not convert 'engine::fileManager::processLua(const char*)()' from 'boost::variant<engine::material, engine::shader>' to 'boost::variant<engine::material, engine::shader, unsigned int>'

What is the (proper) way to return the data?

Upvotes: 3

Views: 660

Answers (1)

sehe
sehe

Reputation: 393064

You should use a visitor:

Live On Coliru

template <typename R, typename A> convert_variant(A const& arg) {
    return boost::apply_visitor([](auto const& v) -> R { return R{v}; }, arg);
}

Update

In response to the observation made by @llonesmiz, you may wish to compile the conversion code even if some conversions might be illegal. In that case you will have to use some type traits to dinstinguish those cases and act accordingly:

C++03 Demo

Live On Coliru

#include <boost/variant.hpp>
#include <boost/type_traits.hpp>
#include <iostream>

template <typename R>
struct convert_variant_visitor : boost::static_visitor<R> {
    struct bad_conversion : std::runtime_error {
        bad_conversion(std::string msg) : std::runtime_error(msg) {}
    };

    template <typename T>
        typename boost::enable_if_c<boost::is_convertible<T, R>::value, R>::type
        operator()(T const& v) const 
    {
        return R(v); // or just v
    }

    template <typename T>
        typename boost::enable_if_c<not boost::is_convertible<T, R>::value, R>::type
        operator()(T const& v) const 
    {
        throw bad_conversion(std::string("Cannot convert ") + typeid(v).name() + " to " + typeid(R).name());
        //throw bad_conversion("Not convertible to variant");
    }
};

template <typename R, typename A> R convert_variant(A const& arg) {
    return boost::apply_visitor(convert_variant_visitor<R>(), arg);
}

int main() {
    typedef boost::variant<std::string, int, double> V1;
    typedef boost::variant<int, double> V2;
    V1 input = 42;
    V2 output = convert_variant<V2>(input);

    std::cout << "input:  " << input  << " (which: " << input.which()  << ")\n";
    std::cout << "output: " << output << " (which: " << output.which() << ")\n";
}

Prints

input:  42 (which: 1)
output: 42 (which: 0)

C++17 Demo

Modern C++ features are making generic code like this much simpler:

Live On Coliru

template <typename R, typename A> R convert_variant(A const& arg) {
    return boost::apply_visitor([](auto const& v) -> R {
            if constexpr (std::is_convertible_v<decltype(v), R>)
                return v;
            else
                throw std::runtime_error("bad conversion");
        } , arg);
}

Upvotes: 2

Related Questions