Polymorphic setter for Boost :: variant

I am trying to use boost :: variant with template types. For example, I have a template type Tag<T> , and boost :: variant AnyTag contains types such as Tag<double> , Tag<int> and Tag<std::string> . Each Tag<T> has members of type T. Now I would like to put these options in a container and just assign values ​​at runtime, like

 for(AnyTag & tag: AllTags) { setValue(tag, getValueFromXml()); } 

The setValue(AnyTag &tag, T &val) function setValue(AnyTag &tag, T &val) must use the execution type of the AnyTag tag to correctly assign the tag the correct value. My attempt to solve the problem can be found below, and it uses another option, which includes only the possible types of T that can be used in AnyTag (TagValueType).

 template<typename T, typename = void> class Tag {}; template <typename T> class Tag<T, EnableIf<std::is_arithmetic<T>>> { public: T value = 0; std::string address = ""; T maxValue = std::numeric_limits<T>::max(); typedef T value_type; }; template <typename T> class Tag<T, DisableIf<std::is_arithmetic<T>>> { public: T value; std::string address = ""; typedef T value_type; }; typedef boost::variant<Tag<std::string>, Tag<double>, Tag<int>, > AnyTag; typedef boost::variant<std::string, double, int> TagValueType; class tag_set_value_visitor: public boost::static_visitor<void> { const TagValueType & value; public: tag_set_value_visitor(const TagValueType & val): value(val){} template <typename T> void operator()(T & tag) const { tag.value = boost::get<typename T::value_type>(value); } }; inline void setValue(AnyTag & tag, const TagValueType & val) { assert(tag.which() == val.which()); boost::apply_visitor( tag_set_value_visitor(val), tag ); } 

Unfortunately, this approach is not what I would like, because, for example, there is no problem during compilation if I do the following:

 AnyTag a = Tag<int>(); setValue(a, double(1.3)); 

but at runtime, the acceleration library detects a type mismatch and displays a program failure message.

So my solution is a kind of type erasure that just postpones the problem.

What I would like to have is setValue (AnyTag & tag, T & val), where T is the type of the AnyTag runtime.

I get what the visitor of the option tries to try, but in this case there is a problem, because when we build the visitor, we need to know the type that we will use.

Any ideas or any thoughts on this issue?

PS: Sorry for the long post, but I could not find a way to explain my thought process with fewer words.

+5
source share
1 answer

Use a binary visitor.

Add operator() nothing but the corresponding types.

Handle inconsistencies to taste (I return logical success):

Live on coliru

 #include <boost/any.hpp> #include <boost/variant.hpp> #include <boost/mpl/vector.hpp> #include <string> using namespace boost; template <typename T> struct Tag { T value; }; using Types = mpl::vector<std::string, double, int>; using Tags = mpl::transform<Types, Tag<mpl::_1> >::type; using Variant = make_variant_over<Types>::type; using AnyTag = make_variant_over<Tags>::type; namespace mydetail { struct assign_to : boost::static_visitor<bool> { template <typename V> bool operator()(Tag<V>& tagged, V const& value) const { tagged.value = value; return true; } template <typename T, typename V> bool operator()(T&&, V&&) const { return false; } }; } bool setValue(AnyTag &tag, Variant const& val) { return boost::apply_visitor(mydetail::assign_to(), tag, val); } int main() { AnyTag t; t = Tag<std::string>(); // corresponding type assigns and returns true: assert(setValue(t, "yes works")); // mismatch: no effect and returns false: assert(!setValue(t, 42)); assert(!setValue(t, 3.1415926)); } 

ΒΉ If I understood your purpose correctly. I focused on "What I would like to have is setValue (AnyTag & tag, T & val), where T is the type of the AnyTag runtime." part of the request.

+6
source

All Articles