According to its author @hkaiser, here boost::spirit::hold_any
is a more performant alternative to boost::any
and can, for the most part, be used as a drop-in replacement for the latter. I'm interested in it since it purports to allow for easy output streaming, a feature that boost::any
lacks.
While I'm able to input simple types like int
into hold_any
, I can't seem to get it to hold a container such as std::vector<int>
for example. Here'e the code
#include <iostream>
#include <boost/spirit/home/support/detail/hold_any.hpp> // v1.79
#include <vector>
using any = boost::spirit::hold_any;
int main() {
int a1 = 1;
any v1(a1); // OK
std::cout << v1 << std::endl; // OK
std::vector<int> w2 = {5,6};
any v2(w2); // compilation error - invalid operands to binary expression ('std::basic_istream<char>' and 'std::vector<int>')
std::cout << v2 << std::endl;
}
which fails to compile with
hold_any.hpp:155:23: error: invalid operands to binary expression ('std::basic_istream<char>' and 'std::vector<int>')
i >> *static_cast<T*>(*obj);
Presumably, the std::vector<int>
needs to be made istream streamable though I'm not sure how to proceed. How does one make this work?
Firstly, that advice is 12 years old. Since then std::any
was even standardized. I would not assume that hold_any
is still the better choice (on the contrary).
Also, note that the answer you implied contains the exact explanation:
This class has two differences if compared to boost::any:
- it utilizes the small object optimization idiom and a couple of other optimization tricks, making
spirit::hold_any
smaller and faster thanboost::any
- it has the streaming operators (
operator<<()
andoperator>>()
) defined, allowing to input and output a spirit::hold_any seemlessly.
(emphasis mine)
Incidentally, the whole question was about streaming any
in the first place, so the answer was on-point there.
The code further drives home the assumption:
// these functions have been added in the assumption that the embedded
// type has a corresponding operator defined, which is completely safe
// because spirit::hold_any is used only in contexts where these operators
// do exist
template <typename Char_>
friend inline std::basic_istream<Char_>&
operator>> (std::basic_istream<Char_>& i, basic_hold_any<Char_>& obj)
{
return obj.table->stream_in(i, &obj.object);
}
template <typename Char_>
friend inline std::basic_ostream<Char_>&
operator<< (std::basic_ostream<Char_>& o, basic_hold_any<Char_> const& obj)
{
return obj.table->stream_out(o, &obj.object);
}
So, indeed that explains the requirement. It's a bit unfortunate that the implementation is not SFINAE-ed so that you'd only run into the limitation if you used the stream_in
/stream_out
operations, but here we are.