Reputation: 763
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
Reputation: 393064
You should use a visitor:
template <typename R, typename A> convert_variant(A const& arg) {
return boost::apply_visitor([](auto const& v) -> R { return R{v}; }, arg);
}
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:
#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)
Modern C++ features are making generic code like this much simpler:
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