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 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?
CodePudding user response:
I was able to make the above code work by making a generic vector std::vector<T>
both output and input streamable.
#include <iostream>
#include <boost/spirit/home/support/detail/hold_any.hpp>
#include <vector>
using any = boost::spirit::hold_any;
namespace std {
// input stream
template<typename T>
std::istream& operator >> ( std::istream& ins, std::vector<T>& p ) {
size_t sz;
ins >> sz;
for ( size_t i = 0; i < sz; i ) {
T tmp;
ins >> tmp;
p.push_back( tmp );
}
return ins;
}
// output stream
template<typename T>
std::ostream& operator << ( std::ostream& outs, const std::vector<T>& p ) {
outs << "[";
for ( size_t i = 0; i < p.size(); i ) {
outs << p[i];
if ( i != p.size() - 1 )
outs << " ";
else
outs << "]";
}
return outs;
}
}
I'd appreciate any comments on how to make the above more performant. boost::iostreams
perhaps?
CodePudding user response:
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)
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.