I've the following class diagram:
There's some unused class like BinaryOperator, but my real code needs them so I want to keep them also in the example.
I want to use boost::karma in order to obtain a JSON representation of this. The JSON should be like the following one:
{
"name": "Plus",
"type": "Function",
"arguments": [
{
"name": "IntegerValue",
"type": "Value",
"value": "4"
},
{
"name": "Plus",
"type": "Function",
"arguments": [
{
"name": "IntegerValue",
"type": "Value",
"value": "5"
},
{
"name": "IntegerValue",
"type": "Value",
"value": "6"
}
]
}
]
}
Since it's a simple example, I'd like to use BOOST_FUSION_ADAPT_ADT macro for my classes in order to modularize the generator.
I'm new to Karma, I've read the tutorial on boost site but I don't understand how to attack my problem. I can't find some good tutorial about that macro.
I don't want to use existing libraries for JSON because at first I want to learn Karma, and in second place JSON is only an example, I need to export my expression in many formats, and I can do it by simply changing generators while the code that uses BOOST_FUSION_ADAPT_ADT for my classes should be the same.
You can find the code for creating a sample expression. Where I need to start in order to solve my problem?
#include <boost/lexical_cast.hpp>
#include <iostream>
#include <vector>
class Expression {
public:
virtual std::string getName() const = 0;
};
class Value : public Expression {
public:
virtual std::string getValue() const = 0;
};
class IntegerValue : public Value {
public:
IntegerValue(int value) : m_value(value) {}
virtual std::string getName() const override { return "IntegerValue"; }
virtual std::string getValue() const override { return boost::lexical_cast<std::string>(m_value); }
private:
int m_value;
};
class Function : public Expression {
public:
void addArgument(Expression* expression) { m_arguments.push_back(expression); }
virtual std::string getName() const override { return m_name; }
protected:
std::vector<Expression*> m_arguments;
std::string m_name;
};
class Plus : public Function {
public:
Plus() : Function() { m_name = "Plus"; }
};
///////////////////////////////////////////////////////////////////////////////
int main(int argc, char **argv) {
// Build expression 4 + 5 + 6 as 4 + (5 + 6)
Function* plus1 = new Plus();
Function* plus2 = new Plus();
Value* iv4 = new IntegerValue(4);
Value* iv5 = new IntegerValue(5);
Value* iv6 = new IntegerValue(6);
plus2->addArgument(iv5);
plus2->addArgument(iv6);
plus1->addArgument(iv4);
plus1->addArgument(plus2);
// Generate json string here, but how?
return 0;
}
I'd advise against using Karma to generate JSON. I'd advise strongly against ADAPT_ADT (it's prone to very subtle UB bugs and it means you're trying to adapt something that wasn't designed for it. Just say no).
Here's my take on it. Let's take the high road and be as unintrusive as possible. That means
We can't just overload operator<< to print json (because you may want to naturally print the expressions instead)
It also means that what ever function is responsible for generating the JSON doesn't
have to bother with json implementation details
have to bother with pretty formatting
Finally, I wouldn't want to intrude on the expression tree with anything JSON specific. The most that could be acceptable is an opaque friend declaration.
A simple JSON facility:
This might well be the most simplistic JSON representation, but it does the required subset and makes a number of smart choices (supporting duplicate properties, retaining property order for example):
#include <boost/variant.hpp>
namespace json {
// adhoc JSON rep
struct Null {};
using String = std::string;
using Value = boost::make_recursive_variant<
Null,
String,
std::vector<boost::recursive_variant_>,
std::vector<std::pair<String, boost::recursive_variant_> >
>::type;
using Property = std::pair<String, Value>;
using Object = std::vector<Property>;
using Array = std::vector<Value>;
}
That's all. This is fully functional. Let's prove it
Pretty Printing JSON
Like with the Expression tree itself, let's not hardwire this, but instead create a pretty-printing IO manipulator:
#include <iomanip>
namespace json {
// pretty print it
struct pretty_io {
using result_type = void;
template <typename Ref>
struct manip {
Ref ref;
friend std::ostream& operator<<(std::ostream& os, manip const& m) {
pretty_io{os,""}(m.ref);
return os;
}
};
std::ostream& _os;
std::string _indent;
void operator()(Value const& v) const {
boost::apply_visitor(*this, v);
}
void operator()(Null) const {
_os << "null";
}
void operator()(String const& s) const {
_os << std::quoted(s);
}
void operator()(Property const& p) const {
_os << '\n' << _indent; operator()(p.first);
_os << ": "; operator()(p.second);
}
void operator()(Object const& o) const {
pretty_io nested{_os, _indent+" "};
_os << "{";
bool first = true;
for (auto& p : o) { first||_os << ","; nested(p); first = false; }
_os << "\n" << _indent << "}";
}
void operator()(Array const& o) const {
pretty_io nested{_os, _indent+" "};
_os << "[\n" << _indent << " ";
bool first = true;
for (auto& p : o) { first||_os << ",\n" << _indent << " "; nested(p); first = false; }
_os << "\n" << _indent << "]";
}
};
Value to_json(Value const& v) { return v; }
template <typename T, typename V = decltype(to_json(std::declval<T const&>()))>
pretty_io::manip<V> pretty(T const& v) { return {to_json(v)}; }
}
The to_json thing dubs as a handy ADL-enabled extension point, you can already us it now:
std::cout << json::pretty("hello world"); // prints as a JSON String
Connecting it up
To make the following work:
std::cout << json::pretty(plus1);
All we need is the appropriate to_json overload. We could jot it all in there, but we might end up needing to "friend" a function named to_json, worse still, forward declare types from the json namespace (json::Value at the very least). That's too intrusive. So, let's add anothe tiny indirection:
auto to_json(Expression const* expression) {
return serialization::call(expression);
}
The trick is to hide the JSON stuff inside an opaque struct that we can then befriend: struct serialization. The rest is straightforward:
struct serialization {
static json::Value call(Expression const* e) {
if (auto* f = dynamic_cast<Function const*>(e)) {
json::Array args;
for (auto& a : f->m_arguments)
args.push_back(call(a));
return json::Object {
{ "name", f->getName() },
{ "type", "Function" },
{ "arguments", args },
};
}
if (auto* v = dynamic_cast<Value const*>(e)) {
return json::Object {
{ "name", v->getName() },
{ "type", "Value" },
{ "value", v->getValue() },
};
}
return {}; // Null in case we didn't implement a node type
}
};
Full Demo
See it Live On Coliru
#include <boost/lexical_cast.hpp>
#include <iostream>
#include <iomanip>
#include <vector>
struct Expression {
virtual std::string getName() const = 0;
};
struct Value : Expression {
virtual std::string getValue() const = 0;
};
struct IntegerValue : Value {
IntegerValue(int value) : m_value(value) {}
virtual std::string getName() const override { return "IntegerValue"; }
virtual std::string getValue() const override { return boost::lexical_cast<std::string>(m_value); }
private:
int m_value;
};
struct Function : Expression {
void addArgument(Expression *expression) { m_arguments.push_back(expression); }
virtual std::string getName() const override { return m_name; }
protected:
std::vector<Expression *> m_arguments;
std::string m_name;
friend struct serialization;
};
struct Plus : Function {
Plus() : Function() { m_name = "Plus"; }
};
///////////////////////////////////////////////////////////////////////////////
// A simple JSON facility
#include <boost/variant.hpp>
namespace json {
// adhoc JSON rep
struct Null {};
using String = std::string;
using Value = boost::make_recursive_variant<
Null,
String,
std::vector<boost::recursive_variant_>,
std::vector<std::pair<String, boost::recursive_variant_> >
>::type;
using Property = std::pair<String, Value>;
using Object = std::vector<Property>;
using Array = std::vector<Value>;
}
///////////////////////////////////////////////////////////////////////////////
// Pretty Print manipulator
#include <iomanip>
namespace json {
// pretty print it
struct pretty_io {
using result_type = void;
template <typename Ref>
struct manip {
Ref ref;
friend std::ostream& operator<<(std::ostream& os, manip const& m) {
pretty_io{os,""}(m.ref);
return os;
}
};
std::ostream& _os;
std::string _indent;
void operator()(Value const& v) const {
boost::apply_visitor(*this, v);
}
void operator()(Null) const {
_os << "null";
}
void operator()(String const& s) const {
_os << std::quoted(s);
}
void operator()(Property const& p) const {
_os << '\n' << _indent; operator()(p.first);
_os << ": "; operator()(p.second);
}
void operator()(Object const& o) const {
pretty_io nested{_os, _indent+" "};
_os << "{";
bool first = true;
for (auto& p : o) { first||_os << ","; nested(p); first = false; }
_os << "\n" << _indent << "}";
}
void operator()(Array const& o) const {
pretty_io nested{_os, _indent+" "};
_os << "[\n" << _indent << " ";
bool first = true;
for (auto& p : o) { first||_os << ",\n" << _indent << " "; nested(p); first = false; }
_os << "\n" << _indent << "]";
}
};
Value to_json(Value const& v) { return v; }
template <typename T, typename V = decltype(to_json(std::declval<T const&>()))>
pretty_io::manip<V> pretty(T const& v) { return {to_json(v)}; }
}
///////////////////////////////////////////////////////////////////////////////
// Expression -> JSON
struct serialization {
static json::Value call(Expression const* e) {
if (auto* f = dynamic_cast<Function const*>(e)) {
json::Array args;
for (auto& a : f->m_arguments)
args.push_back(call(a));
return json::Object {
{ "name", f->getName() },
{ "type", "Function" },
{ "arguments", args },
};
}
if (auto* v = dynamic_cast<Value const*>(e)) {
return json::Object {
{ "name", v->getName() },
{ "type", "Value" },
{ "value", v->getValue() },
};
}
return {};
}
};
auto to_json(Expression const* expression) {
return serialization::call(expression);
}
int main() {
// Build expression 4 + 5 + 6 as 4 + (5 + 6)
Function *plus1 = new Plus();
Function *plus2 = new Plus();
Value *iv4 = new IntegerValue(4);
Value *iv5 = new IntegerValue(5);
Value *iv6 = new IntegerValue(6);
plus2->addArgument(iv5);
plus2->addArgument(iv6);
plus1->addArgument(iv4);
plus1->addArgument(plus2);
// Generate json string here, but how?
std::cout << json::pretty(plus1);
}
Output is picture-perfect from your question:
{
"name": "Plus",
"type": "Function",
"arguments": [
{
"name": "IntegerValue",
"type": "Value",
"value": "4"
},
{
"name": "Plus",
"type": "Function",
"arguments": [
{
"name": "IntegerValue",
"type": "Value",
"value": "5"
},
{
"name": "IntegerValue",
"type": "Value",
"value": "6"
}
]
}
]
}
Thanks, the fact is that json is only one of many formats that I must to use, some format is proprietary and there are no libraries, so I want to use an uniform way for all. I've decided to use json for the question because is known to community more than, for example, asciimath or other formats created by us – Jepessen 9 hours ago
This changes nothing about my recommendation. If anything, it really emphasizes that you don't want arbitrary restrictions imposed.
The problems with Karma
Karma is an "inline" DSL for statically generated generators. They work well for statically typed things. Your AST uses dynamic polymorphism.
That removes any chance of writing a succinct generator barring the use of many, complicated semantic actions. I don't remember writing many explicit answers related to Karma, but the problems with both dynamic polymorphism and semantic actions are much the same on the Qi side:
How can I use polymorphic attributes with boost::spirit::qi parsers?
Boost Spirit: "Semantic actions are evil"?
The key draw backs all apply, except obviously that AST creation is not happening, so the performance effect of allocations is less severe than with Qi parsers.
However, the same logic still stands: Karma generators are statically combined for efficiency. However your dynamic type hierarchy precludes most of that efficiency. In other words, you are not the target audience for Karma.
Karma has another structural limitation that will bite here, regardless of the way your AST is designed: it's (very) hard to make use of stateful rules to do pretty printing.
This is, for me, a key reason to practically never use Karma. Even if pretty printing isn't a goal you can still get similar mileage just generating output visiting the AST using Boost Fusion directly (we used this in our project to generate different versions of OData XML and JSON representations of API types for use in restful APIs).
Granted, there are some stateful generating tasks that have custom directives builtin to Karma, and sometimes they hit the sweet spot for rapid prototyping, e.g.
Writing a Boost ublas matrix to a text file
Though it gets wieldy quickly Inconsistent Generator directive column behavior in boost karma. Many quirks arise due to the way sub-generators are "counted" w.r.t. the columns[] directive
Let's Do It Anyways
Because I'm not a masochist, I'll do borrow a concept from the other answer: creating an intermediate representation that facilitates Karma a lot better.
In this sample the intermediate representation can be exceedingly simple, but I suspect your other requirements like "for example, asciimath or other formats created by us" will require a more detailed design.
///////////////////////////////////////////////////////////////////////////////
// A simple intermediate representation
#include <boost/variant.hpp>
namespace output_ast {
struct Function;
struct Value;
using Expression = boost::variant<Function, Value>;
using Arguments = std::vector<Expression>;
struct Value { std::string name, value; };
struct Function { std::string name; Arguments args; };
}
Firstly, because we're going to use Karma, we do need to actually adapt the intermediate representation:
#include <boost/fusion/include/struct.hpp>
BOOST_FUSION_ADAPT_STRUCT(output_ast::Value, name, value)
BOOST_FUSION_ADAPT_STRUCT(output_ast::Function, name, args)
A generator
Here's the simplest generator I can think of, give and take 2 things:
I have tweaked it for considerable time to get some "readable" format. It gets simpler if you remove all insignificant whitespace.
I opted to not store redundant information (such as the static "type" representation in the intermediate representation). Doing so would slightly uncomplicate, mostly by making the type rule more similar to name and value.
namespace karma_json {
namespace ka = boost::spirit::karma;
template <typename It>
struct Generator : ka::grammar<It, output_ast::Expression()> {
Generator() : Generator::base_type(expression) {
expression = function|value;
function
= "{\n " << ka::delimit(",\n ")
[name << type(+"Function") ]
<< arguments
<< "\n}"
;
arguments = "\"arguments\": [" << -(("\n " << expression) % ",") << ']';
value
= "{\n " << ka::delimit(",\n ")
[name << type(+"Value") ]
<< value_
<< "\n}"
;
type = "\"type\":\"" << ka::string(ka::_r1) << "\"";
string = '"' << *('\\' << ka::char_("\\\"") | ka::char_) << '"';
name = "\"name\":" << string;
value_ = "\"value\":" << string;
}
private:
ka::rule<It, output_ast::Expression()> expression;
ka::rule<It, output_ast::Function()> function;
ka::rule<It, output_ast::Arguments()> arguments;
ka::rule<It, output_ast::Value()> value;
ka::rule<It, std::string()> string, name, value_;
ka::rule<It, void(std::string)> type;
};
}
Post Scriptum
I was making the simplified take for completeness. And ran into this excellent demonstration of completely unobvious attribute handling quirks. The following (just stripping whitespace handling) does not work:
function = '{' << ka::delimit(',') [name << type] << arguments << '}';
value = '{' << ka::delimit(',') [name << type] << value_ << '}' ;
You can read the error novel here in case you like drama. The problem is that the delimit[] block magically consolidates the attributes into a single string (huh). The error message reflects that the string attribute has not been consumed when e.g. starting the arguments generator.
The most direct way to treat the symptom would be to break up the attribute, but there's no real way:
function = '{' << ka::delimit(',') [name << ka::eps << type] << arguments << '}';
value = '{' << ka::delimit(',') [name << ka::eps << type] << value_ << '}' ;
No difference
function = '{' << ka::delimit(',') [ka::as_string[name] << ka::as_string[type]] << arguments << '}';
value = '{' << ka::delimit(',') [ka::as_string[name] << ka::as_string[type]] << value_ << '}' ;
Would be nice if it actually worked. No amount of adding includes or replacing with incantations like ka::as<std::string>()[...] made the compilation error go away.²
So, to just end this sob-story, we'll stoop to the mind-numbingly tedious:
function = '{' << name << ',' << type << ',' << arguments << '}';
arguments = "\"arguments\":[" << -(expression % ',') << ']';
See the section labeled "Simplified Version" below for the live demo.
Using it
The shortest way to generate using that grammar is to create the intermediate representation:
///////////////////////////////////////////////////////////////////////////////
// Expression -> output_ast
struct serialization {
static output_ast::Expression call(Expression const* e) {
if (auto* f = dynamic_cast<Function const*>(e)) {
output_ast::Arguments args;
for (auto& a : f->m_arguments) args.push_back(call(a));
return output_ast::Function { f->getName(), args };
}
if (auto* v = dynamic_cast<Value const*>(e)) {
return output_ast::Value { v->getName(), v->getValue() };
}
return {};
}
};
auto to_output(Expression const* expression) {
return serialization::call(expression);
}
And use that:
using It = boost::spirit::ostream_iterator;
std::cout << format(karma_json::Generator<It>{}, to_output(plus1));
Full Demo
Live On Wandbox¹
#include <boost/lexical_cast.hpp>
#include <iostream>
#include <vector>
struct Expression {
virtual std::string getName() const = 0;
};
struct Value : Expression {
virtual std::string getValue() const = 0;
};
struct IntegerValue : Value {
IntegerValue(int value) : m_value(value) {}
virtual std::string getName() const override { return "IntegerValue"; }
virtual std::string getValue() const override { return boost::lexical_cast<std::string>(m_value); }
private:
int m_value;
};
struct Function : Expression {
void addArgument(Expression *expression) { m_arguments.push_back(expression); }
virtual std::string getName() const override { return m_name; }
protected:
std::vector<Expression *> m_arguments;
std::string m_name;
friend struct serialization;
};
struct Plus : Function {
Plus() : Function() { m_name = "Plus"; }
};
///////////////////////////////////////////////////////////////////////////////
// A simple intermediate representation
#include <boost/variant.hpp>
namespace output_ast {
struct Function;
struct Value;
using Expression = boost::variant<Function, Value>;
using Arguments = std::vector<Expression>;
struct Value { std::string name, value; };
struct Function { std::string name; Arguments args; };
}
#include <boost/fusion/include/struct.hpp>
BOOST_FUSION_ADAPT_STRUCT(output_ast::Value, name, value)
BOOST_FUSION_ADAPT_STRUCT(output_ast::Function, name, args)
#include <boost/spirit/include/karma.hpp>
namespace karma_json {
namespace ka = boost::spirit::karma;
template <typename It>
struct Generator : ka::grammar<It, output_ast::Expression()> {
Generator() : Generator::base_type(expression) {
expression = function|value;
function
= "{\n " << ka::delimit(",\n ")
[name << type(+"Function") ]
<< arguments
<< "\n}"
;
arguments = "\"arguments\": [" << -(("\n " << expression) % ",") << ']';
value
= "{\n " << ka::delimit(",\n ")
[name << type(+"Value") ]
<< value_
<< "\n}"
;
type = "\"type\":\"" << ka::string(ka::_r1) << "\"";
string = '"' << *('\\' << ka::char_("\\\"") | ka::char_) << '"';
name = "\"name\":" << string;
value_ = "\"value\":" << string;
}
private:
ka::rule<It, output_ast::Expression()> expression;
ka::rule<It, output_ast::Function()> function;
ka::rule<It, output_ast::Arguments()> arguments;
ka::rule<It, output_ast::Value()> value;
ka::rule<It, std::string()> string, name, value_;
ka::rule<It, void(std::string)> type;
};
}
///////////////////////////////////////////////////////////////////////////////
// Expression -> output_ast
struct serialization {
static output_ast::Expression call(Expression const* e) {
if (auto* f = dynamic_cast<Function const*>(e)) {
output_ast::Arguments args;
for (auto& a : f->m_arguments) args.push_back(call(a));
return output_ast::Function { f->getName(), args };
}
if (auto* v = dynamic_cast<Value const*>(e)) {
return output_ast::Value { v->getName(), v->getValue() };
}
return {};
}
};
auto to_output(Expression const* expression) {
return serialization::call(expression);
}
int main() {
// Build expression 4 + 5 + 6 as 4 + (5 + 6)
Function *plus1 = new Plus();
Function *plus2 = new Plus();
Value *iv4 = new IntegerValue(4);
Value *iv5 = new IntegerValue(5);
Value *iv6 = new IntegerValue(6);
plus2->addArgument(iv5);
plus2->addArgument(iv6);
plus1->addArgument(iv4);
plus1->addArgument(plus2);
// Generate json string here, but how?
using It = boost::spirit::ostream_iterator;
std::cout << format(karma_json::Generator<It>{}, to_output(plus1));
}
The Output
The generator is being as as readable/robust/functional as I'd like (there are quirks related to delimiters, there are issues when type contain characters that would need to be quoted, there's no stateful indentation).
The result doesn't look as expected, though it's valid JSON:
{
"name":"Plus",
"type":"Function",
"arguments": [
{
"name":"IntegerValue",
"type":"Value",
"value":"4"
},
{
"name":"Plus",
"type":"Function",
"arguments": [
{
"name":"IntegerValue",
"type":"Value",
"value":"5"
},
{
"name":"IntegerValue",
"type":"Value",
"value":"6"
}]
}]
}
Fixing it is... a nice challenge if you want to try it.
The Simplified Version
The simplified version, complete with attribute-handling workaround documented above:
Live On Coliru
namespace karma_json {
namespace ka = boost::spirit::karma;
template <typename It>
struct Generator : ka::grammar<It, output_ast::Expression()> {
Generator() : Generator::base_type(expression) {
expression = function|value;
function = '{' << name << ',' << type << ',' << arguments << '}';
arguments = "\"arguments\":[" << -(expression % ',') << ']';
value = '{' << name << ',' << type << ',' << value_ << '}' ;
string = '"' << *('\\' << ka::char_("\\\"") | ka::char_) << '"';
type = "\"type\":" << string;
name = "\"name\":" << string;
value_ = "\"value\":" << string;
}
private:
ka::rule<It, output_ast::Expression()> expression;
ka::rule<It, output_ast::Function()> function;
ka::rule<It, output_ast::Arguments()> arguments;
ka::rule<It, output_ast::Value()> value;
ka::rule<It, std::string()> string, name, type, value_;
};
}
Yields the following output:
{"name":"Plus","type":"Function","arguments":[{"name":"IntegerValue","type":"Value","value":"4"},{"name":"Plus","type":"Function","arguments":[{"name":"IntegerValue","type":"Value","value":"5"},{"name":"IntegerValue","type":"Value","value":"6"}]}]}
I'm inclined to think this is a much better cost/benefit ratio than the failed attempt at "pretty" formatting. But the real story here is that the maintenance cost is through the roof anyways.
¹ Interestingly, Coliru exceeds the compilation time... This too could be an argument guiding your design descisions
² makes you wonder how many people actually use Karma day-to-day
I want to share structured data between C++ and Python languages using MessagePack like this one:
{
"t" : [ [t00,...,t0N], ... , [tM0,...,tMN] ],
"x" : [ x0,..,xN],
"P" : [ [P00, ..., P0N], ..., [PM0,...,PMN] ]
}
The number of variables is optional so in some cases I will have for example only:
{
"t" : [ [t00,...,t0N], ... , [tM0,...,tMN] ]
}
Decoding this in Python is pretty simple, my problem is to figure out
how to decode this in C++ if I don't know in advance the structure of
the data ? or the exact number of variables that I would have; is it
possible to iterate the structure in these cases?
I managed to handle a "fixed" data structure ( always with the same
number of variables ) defining a struct for example:
struct variables
{
std::vector< std::vector<double> > t;
std::vector< double > x;
std::vector< std::vector<double> > P;
MSPACK_DEFINE_MAP( t, x, P );
};
std::stringstream inBuffer;
.... (read data )
std::string str( inBuffer.str() );
msgpack::object_handle oh = msgpack::unpack( str.data(), str.size() );
msgpack::object deserialized = oh.get();
variables var;
deserialized.convert( var );
Is there a better way to accomplish this ?, how could manage optional
variables that could not appear in the structure ?; I repeat the
previous question: could I iterate an unknown data structure in C++?,
how ?
Thanks in advance!
Regards, Ernesto
There are two ways to treat unknown data structure.
The first way is using parse/visitor mechanism.
Here is an example:
#include <msgpack.hpp>
#include <sstream>
#include <iostream>
// This is a simple print example visitor.
// You can do any processing in your visitor.
struct my_visitor : msgpack::null_visitor {
bool start_map_key() {
processing_map_key = true;
return true;
}
bool end_map_key() {
processing_map_key = false;
return true;
}
bool start_array(uint32_t size) {
std::cout << "array (size:" << size << ")[" << std::endl;
return true;
}
bool end_array() {
std::cout << "]" << std::endl;
return true;
}
bool visit_str(const char* v, uint32_t size) {
if (processing_map_key) {
std::cout << "map key:" << std::string(v, size) << std::endl;
}
return true;
}
bool visit_positive_integer(uint64_t v) {
std::cout << "found value:" << v << std::endl;
return true;
}
bool processing_map_key = false;
std::string indent;
};
int main() {
// create test data
std::stringstream ss;
msgpack::packer<std::stringstream> pk(ss);
pk.pack_map(1);
pk.pack("t");
pk.pack_array(2);
pk.pack_array(3);
pk.pack(1);
pk.pack(2);
pk.pack(3);
pk.pack_array(3);
pk.pack(4);
pk.pack(5);
pk.pack(6);
// print data (for debug)
{
auto oh = msgpack::unpack(ss.str().data(), ss.str().size());
std::cout << oh.get() << std::endl;
}
// apply visitor
{
my_visitor mv;
msgpack::parse(ss.str().data(), ss.str().size(), mv);
}
}
Running demo: https://wandbox.org/permlink/3NrR4IMDIuLTk9e9
See https://github.com/msgpack/msgpack-c/wiki/v2_0_cpp_visitor.
The other way is using msgpack::type::variant or `msgpack::type::variant_ref.
The former copies data, you can update it. The latter doesn't copy data. You cannot update it.
This approach requires boost. So you need to define MSGPACK_USE_BOOST. I recommend defining as a compiler option.
// Boost is required
#define MSGPACK_USE_BOOST
#include <msgpack.hpp>
#include <sstream>
#include <iostream>
struct my_visitor:boost::static_visitor<void> {
void operator()(uint64_t v) const {
std::cout << "positive insteger:" << v << std::endl;
}
// const is required for map key because std::multimap's key (first) is const.
void operator()(std::string const& v) const {
std::cout << "string:" << v << std::endl;
}
void operator()(std::vector<msgpack::type::variant>& v) const {
std::cout << "array found" << std::endl;
for (auto& e : v) {
boost::apply_visitor(*this, e);
}
}
void operator()(std::multimap<msgpack::type::variant, msgpack::type::variant>& v) const {
std::cout << "map found" << std::endl;
for (auto& e : v) {
std::cout << "key:" << std::endl;
boost::apply_visitor(*this, e.first);
std::cout << "value:" << std::endl;
boost::apply_visitor(*this, e.second);
}
}
template <typename T>
void operator()(T const&) const {
std::cout << " match others" << std::endl;
}
};
int main() {
// create test data
std::stringstream ss;
msgpack::packer<std::stringstream> pk(ss);
pk.pack_map(1);
pk.pack("t");
pk.pack_array(2);
pk.pack_array(3);
pk.pack(1);
pk.pack(2);
pk.pack(3);
pk.pack_array(3);
pk.pack(4);
pk.pack(5);
pk.pack(6);
auto oh = msgpack::unpack(ss.str().data(), ss.str().size());
std::cout << oh.get() << std::endl;
msgpack::type::variant v = oh.get().as<msgpack::type::variant>();
boost::apply_visitor(my_visitor(), v);
}
Running demo: https://wandbox.org/permlink/HQwJjfwW8rLEMi0d
See https://github.com/msgpack/msgpack-c/wiki/v2_0_cpp_variant
Here are exampless:
https://github.com/msgpack/msgpack-c/blob/master/example/boost/msgpack_variant_capitalize.cpp
https://github.com/msgpack/msgpack-c/blob/master/example/boost/msgpack_variant_mapbased.cpp
Both ways can treat unpredictable data structure. You need to do some visitor processing. If the data structure is predictable some extent, your original approach is also good way.
Actually there is a simpler way, if you are dealing with maps (like stated in the question), not arrays.
msgpack::object_handle oh = msgpack::unpack(/* some data */);
std::map<std::string,msgpack::type::variant> map = obj.convert();
This way you will get a map with all the data, no need for a visitor or boost.
Let's say I want to print the number args if they are > 1 or "none" if they are <= 1. The only way I know how to do this is:
cout << "number of args: ";
if (argc > 1)
cout << argc - 1;
else
cout << "none";
cout << endl;
But then I can't chain the << operator. Ideally, I would like to be able to do something like:
cout << "number of args: "
<< argc > 1 ? argc - 1 : "none"
<< endl;
But this isn't possible because the types from a ternary if are different.
Ideas?
The simple way. Handle with a string.
std::cout << "number of args: "
<< (argc > 1 ? std::to_string(argc - 1) : "none")
<< std::endl;
It has some superfluous cost to change the string. But it is easy to read and maintain if it is used once. And the reason the parentheses are required is that the shift operator(<<) has higher precedence than ternary conditional operator(?:) or relational operators (>).
You can make your own class to do the limit printing:
class limit {
int lim;
int k;
public:
limit(int lim_, int k_) : lim(lim_), k(k_) {}
friend ostream& operator<<(ostream& ostr, const limit& lim);
};
ostream& operator<<(ostream& ostr, const limit& lim) {
if (lim.k < lim.lim) {
ostr << "none";
} else {
ostr << lim.k - lim.lim;
}
return ostr;
}
int main() {
cout << "Hello " << limit(1, 15) << endl;
cout << "World " << limit(1, -1) << endl;
return 0;
}
Demo.
The limit class holds two numbers - the limit (1 in the demo) and the number to be printed. It provides an operator << for printing, which uses your conditional statement to print either the value over the limit, or "none". This lets you write cout << limit(1, argc), and produce the desired printout.
If you place the << within the ternary expressions, it unifies the types for both the true and false cases.
argc > 1 ? std::cout << argc - 1 : std::cout << "none";
You can use a helper template function to make things a bit more seamless:
template <typename T>
std::ostream & argp (const T &x) {
return std::cout << "number of args: " << x;
}
(argc > 1 ? argp(argc - 1) : argp("none")) << std::endl;
If you want a more general solution, you could create a (template) class that stores references to two objects and a bool. Then overload operator<< to choose which member to print based on the bool.
template<typename T, typename U>
class be_careful_this_class_holds_references_to_its_constructor_arguments
{
public:
be_careful_this_class_holds_references_to_its_constructor_arguments(
bool b, T const& t, U const& u)
:choose_first(b), first(t), second(u)
{}
friend std::ostream& operator<<(
std::ostream& os,
be_careful_this_class_holds_references_to_its_constructor_arguments const& p)
{
if (p.choose_first)
os << p.first;
else
os << p.second;
return os;
}
private:
bool choose_first;
T const& first;
U const& second;
};
Using this class is a bit cumbersome though, you have to name the types. You can fix that with a helper function:
template<typename T, typename U>
be_careful_this_class_holds_references_to_its_constructor_arguments<T,U>
pick(bool choice, T const& first, U const& second)
{
return be_careful_this_class_holds_references_to_its_constructor_arguments<T,U>(choice,first,second);
}
Then you can just call this function, passing a condition to the first parameter, and your two options of what to print to the second and third parameters:
std::cout << pick(argc > 1, argc - 1, "none") << std::endl;
Just for the sake of adding to the existing craziness....
Eventual usage supported:
xstream xs(std::cout) << If(argc <= 1) << argc - 1 << _else << "none" << _endif << '\n';
Note that this does not short-circuit evaluation of the "if" or "else" branches the way ?, : and ; does, but it doesn't require a conversion to a common type either....
Implementation (also at ideone.com.
#include <iostream>
using namespace std;
struct If { If(bool active) : active_(active) { } bool active_; };
struct Else { } _else;
struct Endif { } _endif;
class xstream
{
public:
xstream(std::ostream& os) : os_(os) { }
template <typename T>
friend xstream& operator<<(xstream& s, const T& t)
{
if (s.active_) s.os_ << t;
return s;
}
private:
bool active_;
std::ostream& os_;
};
template <>
xstream& operator<< <If>(xstream& s, const If& _if)
{
s.active_ = _if.active_;
return s;
}
template <>
xstream& operator<<<Else>(xstream& s, const Else& _else)
{
s.active_ ^= true;
return s;
}
template <>
xstream& operator<<<Endif>(xstream& s, const Endif& _endif)
{
s.active_ = true;
return s;
}
int main(int argc, const char* argv[])
{
xstream xs(std::cout);
xs << If(argc <= 1) << argc - 1 << _else << "none" << _endif << '\n';
}
(Note that the xstream constructor only keeps a reference to the stream it's controlling, so the latter's lifetime must outlive the former's use under penalty of undefined behaviour).
This question already has answers here:
How can I use cout << myclass
(5 answers)
Closed 4 months ago.
I've been reading questions here for an hour or two regarding this error I'm getting and most of them forgot to #include string (which I had already done), or to overload the << operator.
Here's the code in question:
void Student::getCoursesEnrolled(const vector<Course>& c)
{
for (int i = 0; i < c.size(); i++)
{
cout << c[i] << endl;
}
}
And the error I'm getting:
Error: No operator matches these operands
operand types are: std::ostream << const Course
All I'm trying to do is return the vector. I read about overloading the << operator but we haven't learned any of that in class so I'm assuming there is another way of doing it?
I appreciate your time!
All I'm trying to do is return the vector.
Not quite; you're trying to print it using cout. And cout has no idea how to print a Course object, unless you provide an overloaded operator<< to tell it how to do so:
std::ostream& operator<<(std::ostream& out, const Course& course)
{
out << course.getName(); // for example
return out;
}
See the operator overloading bible here on StackOverflow for more information.
The problem is that operator << is not overload for type Course objects of which you are trying to output in statement
cout << c[i] << endl;
You need to overload this operator or write your own function that will output an object of type Course in std::ostream
For example let assume that below is a definition of class Course
class Course
{
private:
std::string name;
unsigned int duration;
public:
Course() : duration( 0 ) {}
Course( const std::string &s, unsigned int n ) : name( s ), duration( n ) {}
std::ostream & out( std::ostream &os ) const
{
return ( os << "Course name = " << name << ", course duration = " << duration );
}
};
When you can write
std::vector<Course> v = { { "A", 1 }, { "B", 2 }, { "C", 3 } };
for ( const Course &c : v ) c.out( std::cout ) << std::endl;
Instead member function out you can overload operator <<. For example
class Course
{
private:
std::string name;
unsigned int duration;
public:
Course() : duration( 0 ) {}
Course( const std::string &s, unsigned int n ) : name( s ), duration( n ) {}
friend std::ostream & operator <<( std::ostream &os, const Course & c )
{
return ( os << "Course name = " << c.name << ", course duration = " << c.duration );
}
};
and use it as
std::vector<Course> v = { { "A", 1 }, { "B", 2 }, { "C", 3 } };
for ( const Course &c : v ) std::cout << c << std::endl;
The stream operator << is used to "output" some representation of that object. If you don't want to overload the operator yet just pick some property to output instead:
for (int i = 0; i < c.size(); i++)
{
cout << c[i].Name << endl; // assuming Name is a property of Course
}
When you DO overload the operator you just decide then what the proper representation of a Course is:
ostream& operator<< (ostream &out, Course &c)
{
out << c.Name "(" << c.Description << ")";
return out;
}
Your Course class needs to implement an operator:
class Course
{
public:
/*
* Your code here
*/
// Probably missing this:
friend std::ostream& operator << (std::ostream& os, const Course& course)
{
os << course.name(); // etc..
return os;
};
}; // eo class Course
Since you haven't yet learned to overload operator<<, what you can do instead is to print each member of your Course class. You haven't posted the definition of Course, but perhaps it's something like this:
class Course
{
public:
int get_number() { return _number; }
const std::string& get_name() { return _name; }
private:
int _number;
std::string _name;
};
then you can say:
void Student::getCoursesEnrolled(const vector<Course>& c)
{
for (int i = 0; i < c.size(); i++)
{
cout << c[i].get_number() << " "
<< c[i].get_name() << std::endl;
}
}
Your problem is this particular part:
cout << c[i]
In your case c[i] is an object of type Course as dvnrrs correctly pointed out. So either:
implement the overloaded << operator for your object OR
if your Course object is in someway a typedef to a primitive try explicitly casting it to a string type (or similar)
I'm trying to create a relatively type safe(but dynamic) and efficient concept called "Linkable Properties". A linkable property is similar to C#'s ability to bind properties and such and similar to the signal/slot model.
A linkable property is a type that can link itself to the values of other types. When any value changes all values are updated. This is useful when you need to keep several properties/values updated simultaneously. Once you setup a link everything is taken care of for you.
Can link from any type to any other type (in theory, this is the issue)
Links use a linked list rather than a list. This is more efficient both memory and speed and the real benefit of using this approach.
Converters are used to convert the values from one type to another(from 1, required, also an issue)
Can act like a getter and setter.
The issues I'm struggling with is writing the ability to link and convert to any type. The following code works with minor changes(convert the templated Chain function to a non-templated version and Change Chain<F> to Chain in the SetLink function). The problem is, the links are not correctly called.
This class almost works(it does compile and run but does not work as expected. Without the changes above the binding function never calls. It is only test code and not properly coded(please don't comment about using the static counter, it's just a temporary fix). The Chain and Link elements are the crucial aspect.
Chain is simply suppose to convert and update the value of the property then pass it along(or possibly the original value) to the next property. This continues until one reaches back to the original property in which case it will terminate.
#include <iostream>
#include <string>
#include <boost/bind.hpp>
#include <boost/function.hpp>
using namespace std;
static int iLCount = 1;
template <typename T>
class LinkableProperty
{
public:
std::string Name;
boost::function<void(T)> Link;
T Value;
template<typename F>
void Chain(F val)
{
Value = val;
std::cout << this->Name << " - " << this << ", " << &Link << ", " << val << " ! " << this->Value << " - " << "\n";
if (--iLCount < 0) return;
if (!Link.empty()) Link(Value);
}
LinkableProperty() { Link = NULL; Value = T(); Name = "Level " + std::to_string(iLCount++); };
void operator =(T value) { Value = value; }
template<typename F> void SetLink(LinkableProperty<F> &p)
{
Link = boost::bind(&LinkableProperty<F>::template Chain<F>, &p, _1);
}
void operator ()()
{
if (!Link.empty()) Link(Value);
}
};
int main()
{
LinkableProperty<unsigned short> L1;
LinkableProperty<double> L2;
L2.SetLink(L1);
L1.SetLink(L2);
L1 = 1;
L2 = 1.1;
L1();
cout << "----------\n" << L1.Value << ", " << L2.Value << endl;
getchar();
return 0;
}
The problem most likely stems from here:
template<typename F> void SetLink(LinkableProperty<F> p)
You are passing in a copy of the original property. Change this to accept a reference (or pointer), and you may have better luck. For example:
template<typename F>
void SetLink(LinkableProperty<F>* p)
{
Link = boost::bind(&LinkableProperty<F>::template Chain<F>, p, _1);
}
Should work as expected...
EDIT: Updated to show how to preserve the type across the conversion:
template <typename FT, typename TT>
TT convert(FT v)
{
return v; // default implicit conversion
}
template<>
double convert(unsigned short v)
{
std::cout << "us->d" << std::endl;
return static_cast<double>(v);
}
template<>
unsigned short convert(double v)
{
std::cout << "d->us" << std::endl;
return static_cast<unsigned short>(v);
}
static int iLCount = 1;
template <typename T>
class LinkableProperty
{
template <typename U>
struct _vref
{
typedef U vt;
_vref(vt& v) : _ref(v) {}
U& _ref;
};
public:
std::string Name;
boost::function<void(_vref<T>)> Link;
T Value;
template<typename F>
void Chain(F const& val)
{
Value = convert<typename F::vt, T>(val._ref);
std::cout << this->Name << " - " << this << ", " << &Link << ", " << val._ref << " ! " << this->Value << " - " << "\n";
if (--iLCount < 0) return;
if (!Link.empty()) Link(Value);
}
LinkableProperty() { Link = NULL; Value = T(); Name = "Level " + std::to_string(iLCount++); };
void operator =(T value) { Value = value; }
template<typename F>
void SetLink(LinkableProperty<F>* p)
{
Link = boost::bind(&LinkableProperty<F>::template Chain<_vref<T>>, p, _1);
}
void operator ()()
{
if (!Link.empty()) Link(_vref<T>(Value));
}
};
int main()
{
LinkableProperty<unsigned short> L1;
LinkableProperty<double> L2;
L2.SetLink(&L1);
L1.SetLink(&L2);
L1 = 1;
L2 = 1.1;
L1();
cout << "----------\n" << L1.Value << ", " << L2.Value << endl;
getchar();
return 0;
}
NOTE: There is some link bug which means that the updates trigger more times than necessary - you should check that...