Mr.DeleteMyMessages
Mr.DeleteMyMessages

Reputation: 381

Stream std::any without knowing the type

I want to stream std::any without actually knowing the value.

I know there is std::any.type(), but I am not sure how to use it in std::any_cast<type>(thatAny)

This is code I tried:

std::cout << std::any_cast<a.type()>(a) << std::endl;

Upvotes: 6

Views: 2749

Answers (2)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275820

You need to store how to stream it somewhere.

There are a few different approaches. If you can centrally enumerate or register all of the types you can stream, you can create a map from typeid to streaming code, then use that map to stream your specific std::any.

std::unordered_map< std::type_index, void(*)(std::any const&, std::ostream&)> streamers;

template<class T>
void add_streamer() {
  streamers[ typeid(T) ] = [](std::any const& a, std::ostream& os ) {
    os << std::any_cast<T>(a);
  };
}
template<class...Ts>
void add_streamers() {
  ( void(add_streamer<T>()), ... );
}

Now you can stream:

void stream( std::ostream& os, std::any const& a ) {
  streamers[ a.type() ]( a, os ); // maybe check errors!
}

An alternative plan is to augment your any instances.

struct streamable_any : std::any {
  void(*streamer)(std::ostream&, streamable_any const&) = nullptr;
  friend std::ostream& operator<<( std::ostream& os, streamable_any const& a ) {
    a.streamer( os, a );
    return os;
  }
  template<class T,
    std::enable_if_t<!std::is_same<std::decay_t<T>, streamable_any>{}, bool> = true
  >
  streamable_any( T&& t ):
    std::any( std::forward<T>(t) ),
    streamer([](std::ostream& os, streamable_any const& self ) {
      os << std::any_cast<std::decay_t<T>>(self);
    } )
  {}
};

now a streamable_any now only stores streamable types and store how to stream it in a function pointer with the instance.

A safer streamable_any would not publicly inherit from std::any, as modifying the std::any through conversion-to-base will break streaming. It is just a pain due to any_cast and the like.

Live example.

Upvotes: 10

MSalters
MSalters

Reputation: 180050

It's not directly possible.

However, if you have a T, don't store that directly but store {T, [](T const& t) { using std::to_string; return to_string(t); }}. I.e. store with each T also a lambda that serializes that particular T.

Alternatively, you can keep the std::any as-is, but wrap it in a custom type and store an appropriate [](std::any const& a) { return to_string(std::any_cast<T>(a));} alongside the std::any. E.g. if you insert an int, you also create and store a [](std::any const& a) { return to_string(std::any_cast<int>(a));}

Upvotes: 2

Related Questions