I'd like to investigate boost::spirit::karma::generate
as a replacement for std::stringstream
for a boost::variant
containing, apart from familiar types such as int
and double
, one or more custom classes (e.g. A
). However I'm unable to even compile the code once I include one or more custom classes in the variant.
#include <iostream>
#include <boost/spirit/include/karma.hpp>
#include <boost/variant.hpp>
struct A {
double d;
explicit A(const double d) : d(d) {}
friend std::ostream& operator<<(std::ostream& os, A const& m) {
return os << "{" << m.d << "}";
}
};
BOOST_FUSION_ADAPT_STRUCT(A, d)
using Variant = boost::variant<int, double, A>;
void test_stringstream(const Variant &v) {
std::stringstream os;
os << v;
std::cout << os.str() << std::endl;
}
void test_karma(const Variant &v) {
std::string str;
boost::spirit::karma::generate(std::back_inserter(str), v);
std::cout << str << std::endl;
}
int main() {
A a(double(1.0));
std::cout << a << std::endl;
test_stringstream(Variant(a));
test_karma(Variant(a));
}
I'd expect the output:
{1}
{1}
{1}
The error output begins with
mpl_iterator.hpp:45:24: error: no matching constructor for initialization of 'boost::fusion::mpl_iterator<boost::mpl::l_iter<boost::mpl::l_item<mpl_::long_<1>, A, boost::mpl::l_end>>>::deref<boost::fusion::mpl_iterator<boost::mpl::l_iter<boost::mpl::l_item<mpl_::long_<1>, A, boost::mpl::l_end>>>>::type' (aka 'A')
return type();
Apart from custom classes, I'd also like to enquire how one could make Enums 'streamable' as it were via boost::spirit::karma::generate
.
CodePudding user response:
The error message informs you that A is not default-constructible. It's a long type expression, but helpfully summarized it for you: (aka 'A')
.
Adding a default value for d
fixes it:
explicit A(double d = {}) : d(d) {}
Now with
std::cout << "iostream: " << a << std::endl;
std::cout << "stringstream: "; test_stringstream(Variant(a));
std::cout << "karma: "; test_karma(Variant(a));
You'd see (Live)
iostream: {1}
stringstream: {1}
karma:
You didn't pass any karma expression to generate
. Let's add:
void test_karma(const Variant& v) {
namespace k = boost::spirit::karma;
std::string str;
k::generate(std::back_inserter(str), k::stream, v);
std::cout << str << std::endl;
}
Now you get the same output. Note that the Fusion adaptation is completely unnecessary here, don't actually ever deal with A
in your karma expression. It will effectively amount to the stringstream
implementation, but with extra steps, so it will be slower.
Demo
Prints
iostream: {1}
stringstream: {1}
karma: {1}
Suggestions
I'd suggest using Boost lexical_cast
here. It's almost guaranteed to be faster and certainly less ... clumsy:
#include <boost/lexical_cast.hpp>
#include <boost/variant.hpp>
#include <iostream>
struct A {
explicit A(double d = {}) : d(d) {}
private:
double d;
friend std::ostream& operator<<(std::ostream& os, A const& m) {
return os << "{" << m.d << "}";
}
};
using Variant = boost::variant<int, double, A>;
int main() {
Variant vv[]{42, 3.14, A(1.0)};
for (Variant a : vv)
std::cout << boost::lexical_cast<std::string>(a) << "\n";
}
Prints
42
3.14
{1}