Search code examples
c++boost

Return underlying value of boost::variant with boost::apply_visitor


Is it possible to code the write the boost:static_visitor so that it could return the underlying value of a boost::variant in a type-safe way? My current efforts to do that look as follows.

using EventData = boost::variant<boost::blank, int, std::string, boost::system::error_code>;

struct EventDataVisitor : public boost::static_visitor<> 
{
  int operator()(int number) const { return number; }
  SstErrors operator()(SstErrors err) const { return err; }
  boost::blank operator()(boost::blank) const { return boost::blank{}; }
  decltype(auto) operator()(const boost::system::error_code& ec) const { return ec; }
};

Next, I want to use the EventData variant as follows:

EventData data {"Hello world!"};
// ... Some code here ...

// Retrieve data of specific type later. 
// Should safely return the underlying value of a type it was initialized with.
std::string& eventDataString = boost::apply_visitor(EventDataVisitor{}, data)

If this is not possible, what is another safe way to retrieve the current value of the EventData? (With or better without enabling RTTI).


Solution

  • You can't. If that were possible you wouldn't need variant.

    Another way of putting it: you can but it requires you to return (another) variant, e.g.:

    struct EventDataVisitor : public boost::static_visitor<boost::variant<boost::blank, int, SstErrors, boost::system::error_code> >
    {
      int operator()(int number) const { return number; }
      SstErrors operator()(SstErrors err) const { return err; }
      boost::blank operator()(boost::blank) const { return boost::blank{}; }
      decltype(auto) operator()(const boost::system::error_code& ec) const { return ec; }
    };
    

    The usual approach is to pass a callback that is invoked with the active element type. This is exactly what the visitor interface is, but sometimes it might make sense to have a more specific interface.

    If this is not possible, what is another safe way to retrieve the current value of the EventData? (With or better without enabling RTTI).

     EventData data {"Hello world!"};
     std::string& eventDataString = boost::get<std::string>(data);
    

    That's safe. If there's more between the assignment and use, you might use the pointer versions of get<>:

     if (auto* p = boost::get<std::string>(&data)) {
          // contains a valid std::string
          std::string& eventDataString = *p;
     }