Can boost help me organize/parse lots of binary data?

I am receiving a "live" stream of binary data that represents the states of various objects. Sort of like this: Car Speed (m/s): float32 Temperature (deg): uint16 ... My real packets have hundreds of items in them. I will be decoding many of these "packets" and doing various things with them like printing their values to console, etc. My plan is just to make classes: class Car { public: void Decode(istream&); private: float speed; unsigned temperature ... }; But it will not be very extendable. Unfocused open-ended question: Does boost offer something that will help me with this? Thank you, Chris

[Please do not mail me a copy of your followup]
boost-users@lists.boost.org spake the secret code
Unfocused open-ended question: Does boost offer something that will help me with this?
Boost.Spirit is a recursive descent parser framework that can produce very fast parsers. It uses expression templates to generate parsers from a BNF-like grammar written in C++. You can also use the same techniques to generate output from a datastructure. So conceivably, you could do what you describe by using: Boost.Spirit.Qi to parse the stream into data structures Boost.Spirit.Karma to emit the necessary output from the data structures -- "The Direct3D Graphics Pipeline" free book http://tinyurl.com/d3d-pipeline The Computer Graphics Museum http://computergraphicsmuseum.org The Terminals Wiki http://terminals.classiccmp.org Legalize Adulthood! (my blog) http://legalizeadulthood.wordpress.com

On Wed, Feb 20, 2013 at 3:27 PM, Richard
Boost.Spirit.Qi to parse the stream into data structures Boost.Spirit.Karma to emit the necessary output from the data structures
Richard,
Thank you. I am unfamiliar with Spirit and Karma. Your comment not
only introduced me to them but highlighted just how vague my question
was. I was thinking less about the actual decoding (who would have
known based on what I wrote) and more on how I would "generically" and
"extensibly" handle the many fields I am about to receive.
For example, consider the following horrible code. Is there a "boost"
way to do this kind of thing... (perhaps Spirit/Karma is the answer
and I am just more out of touch than I imagine):
Good things about the classes below:
It is easy to add a new parameter. Just push one onto the back of
EmployeePacket::Items
The class EmployeePacket doesn't have hundreds of data members such as
"string name_, int age_"
Bad things about the classes below:
Extracting data from the EmployeePacket requires hideous dynamic_casts
and hard-coded vector indices
Thank you again for your comments/criticisms/suggestions,
Chris
===
class EmployeePacket
{
std::vector

[Please do not mail me a copy of your followup]
boost-users@lists.boost.org spake the secret code
For example, consider the following horrible code. Is there a "boost" way to do this kind of thing... (perhaps Spirit/Karma is the answer and I am just more out of touch than I imagine):
OK, this is starting to look more like a serialization problem. You have serialized objects into a binary representation on your input stream and you need to deserialize them from the stream into your data structure. Boost has a serialization library for this sort of thing, but I haven't used it myself. Does this look more like what you need? http://www.boost.org/doc/libs/1_53_0/libs/serialization/doc/index.html -- "The Direct3D Graphics Pipeline" free book http://tinyurl.com/d3d-pipeline The Computer Graphics Museum http://computergraphicsmuseum.org The Terminals Wiki http://terminals.classiccmp.org Legalize Adulthood! (my blog) http://legalizeadulthood.wordpress.com

On 21 February 2013 02:20 Richard [mailto:legalize+jeeves@mail.xmission.com] wrote :-
OK, this is starting to look more like a serialization problem. You have serialized objects into a binary representation on your input stream and you need to deserialize them from the stream into your data structure.
Boost has a serialization library for this sort of thing, but I haven't used it myself.
Does this look more like what you need? http://www.boost.org/doc/libs/1_53_0/libs/serialization/doc/index.html --
Whilst the serialization library is a very useful tool and may well do what you want it might be worth having a look at Google's protocol buffers library [1] See [2] For some pros/cons of the 2 approaches Alex [1]https://developers.google.com/protocol-buffers/docs/overview [2]http://stackoverflow.com/questions/1061169/boost-serialization-vs-google-pro...

On 02/20/13 20:19, Richard wrote:
[Please do not mail me a copy of your followup]
boost-users@lists.boost.org spake the secret code
thusly: For example, consider the following horrible code. Is there a "boost" way to do this kind of thing... (perhaps Spirit/Karma is the answer and I am just more out of touch than I imagine):
OK, this is starting to look more like a serialization problem. You have serialized objects into a binary representation on your input stream and you need to deserialize them from the stream into your data structure.
Boost has a serialization library for this sort of thing, but I haven't used it myself.
Does this look more like what you need? http://www.boost.org/doc/libs/1_53_0/libs/serialization/doc/index.html Hi Richard,
I've not used serialization either; however, I would assume it uses some sort of archive "coding" to represent a class which is probably not the same "coding" used by Chris' "live" binary data stream. Chris, what's the format of your binary data stream? -regards, Larry

This looks like a perfect place for Boost.Variant. Here is some pseudo code:
#include

On 02/21/13 08:15, Antony Polukhin wrote:
This looks like a perfect place for Boost.Variant. Here is some pseudo code:
#include
struct DecoderVisitor: boost ::static_vizitor<> { istream& Stream;
explicit DecoderVisitor(istream& Stream): Stream(Stream){}
void operator()(String& s) const { s.Decode(Stream); }
void operator()(Double& d) const { d.Decode(Stream); }
// or you may just write // template <class T> // void operator()(T& val) const { // val.Decode(Stream); //} };
class EmployeePacket { typedef boost::variant
variant_t; std::vector Items; EmployeePacket() { Items.push_back(String("name", "John Doe")); Items.push_back(Double("salary", "USD", 1, 1)); }
void Decode(istream& Stream) { for (auto pItem : Items) boost::apply_visitor(DecoderVisitor(Stream), pItem) }
double GetSalary() { return boost::get<Double>(Items[1]).value; } }
class Double { string name; string units; // double decode_conversion_scale; // double decode_conversion_translate; // unsigned number_of_bits_used_to_encode // double scale_factor double value; void Decode(istream& Stream) { Stream.read(&value, 8); value = value * decode_conversion_scale + decode_conversion_translate; }
class String { string name; string value; void Decode(istream& Stream); }
Hi Anthony, I haven't a clear idea how this would work because I assumed that the binary input stream would have to have something that 1st would be decoded into the tag for the variant. Then, if your variant visitor solution is used, some code (not shown) would create some default constructed value for the bounded type corresponding to that tag and then call the visitor. All the above is just off the top of my head, but, as I said, I'm not real sure the variant visitor solution would work. Could you provide more pseudo code to clarify. Also, Chris, could you provide more details about this binary input stream? -regards, Larry

On 02/21/13 10:47, Larry Evans wrote:
On 02/21/13 08:15, Antony Polukhin wrote:
This looks like a perfect place for Boost.Variant. Here is some pseudo code:
#include
struct DecoderVisitor: boost ::static_vizitor<> { istream& Stream;
explicit DecoderVisitor(istream& Stream): Stream(Stream){}
void operator()(String& s) const { s.Decode(Stream); }
void operator()(Double& d) const { d.Decode(Stream); }
// or you may just write // template <class T> // void operator()(T& val) const { // val.Decode(Stream); //} };
class EmployeePacket { typedef boost::variant
variant_t; std::vector Items; EmployeePacket() { Items.push_back(String("name", "John Doe")); Items.push_back(Double("salary", "USD", 1, 1)); }
void Decode(istream& Stream) { for (auto pItem : Items) boost::apply_visitor(DecoderVisitor(Stream), pItem) }
double GetSalary() { return boost::get<Double>(Items[1]).value; } }
class Double { string name; string units; // double decode_conversion_scale; // double decode_conversion_translate; // unsigned number_of_bits_used_to_encode // double scale_factor double value; void Decode(istream& Stream) { Stream.read(&value, 8); value = value * decode_conversion_scale + decode_conversion_translate; }
class String { string name; string value; void Decode(istream& Stream); }
Hi Anthony,
I haven't a clear idea how this would work because I assumed that the binary input stream would have to have something that 1st would be decoded into the tag for the variant. Or, something like the serialization library's "external key":
http://www.boost.org/doc/libs/1_53_0/libs/serialization/doc/extended_type_in... AFAICT, such an external key serves a purpose similar to variant's tag in that it's used to tag which value is stored in some "storage medium'. In the case of variant, the "storage medium" is a character buffer inside variant. In the case of the serialization library, it's the serialization's archive file. Of course maybe I'm oversimplifying. [snip]

2013/2/21 Larry Evans
I haven't a clear idea how this would work because I assumed that the binary input stream would have to have something that 1st would be decoded into the tag for the variant. Then, if your variant visitor solution is used, some code (not shown) would create some default constructed value for the bounded type corresponding to that tag and then call the visitor.
All the above is just off the top of my head, but, as I said, I'm not real sure the variant visitor solution would work.
Could you provide more pseudo code to clarify.
Here is some more pseudo code, may be it will help you.
#include

On 02/22/13 00:16, Antony Polukhin wrote:
2013/2/21 Larry Evans
: I haven't a clear idea how this would work because I assumed that the binary input stream would have to have something that 1st would be decoded into the tag for the variant.
That "something" looks like the 'S' or 'D' or 'P' character returned by the Stream.getc() call in the EmployeePacket::Decode method you provided below.
Then, if your variant visitor solution is used, some code (not shown) would create some default constructed value for the bounded type corresponding to that tag and then call the visitor.
All the above is just off the top of my head, but, as I said, I'm not real sure the variant visitor solution would work.
Could you provide more pseudo code to clarify.
Here is some more pseudo code, may be it will help you.
Yes, Anthony, it helps a lot. I was confused by your earlier code, the one that appeared to use a variant visitor to do the decoding of Chris' binary input stream: http://article.gmane.org/gmane.comp.lib.boost.user/77401 In the example code from your last post, which is quoted below, the variant visitor is not used to decode this input stream, it's only used to perform some operaton on the variant. Quoting from Chris' OP: I will be decoding many of these "packets" and doing various things with them like printing their values to console, etc. That quote mentions two purposes: 1) Decoding packets 2) Operating on the decoded packets The code quoted below does 2); however, the 1) is done by the Decode methods shown below, the most complicated of which is the EmployeePacket::Decode. This EmployeePacket::Decode is somewhat like a very simple Spirit alternative parser: http://www.boost.org/doc/libs/1_53_0/libs/spirit/doc/html/spirit/qi/referenc... So, I think *both* variant and spirit could solve both of Chris' problems; however, I don't think using variant with its visitor is sufficient to solve both problems.
#include
struct name_visitor: public boost::static_visitorstd::string { template <class T> const std::string& opertor()(const T& val) const { return val.name; }
// Specific visitor for Person std::string opertor()(const Person& val) const { return val.first_name + " " + val.second_name; }
// Specific visitor for Something std::string opertor()(const Something& val) const { throw std::logic_error("Class Something has no name!"); }
};
class EmployeePacket { typedef boost::variant
variant_t; std::vector Items; EmployeePacket() { // copying variants is not as fast as copying pointers, // so it is better to reserve some space first Items.reserve(200); }
void Decode(istream& Stream) { while (Stream) { switch (Stream.getc()) { case 'S': Items.push_back(String(Stream)); break; case 'D': Items.push_back(Double(Stream)); break; case 'P': Items.push_back(Person(Stream)); break; } } }
double GetSalary(std::size_t index) const { // Will throw if at 'index' is not a Double type return boost::get<Double>(Items[index]).value; }
std::string GetName(std::size_t index) const { return boost::apply_visitor(name_visitor(), Items[index]); }
} // All the folloving classes must have a constructor that accepts an
istream&
class Double { string name; string units; // double decode_conversion_scale; // double decode_conversion_translate; // unsigned number_of_bits_used_to_encode // double scale_factor double value; void Decode(istream& Stream) // Will be called from constructor { Stream.read(&value, 8); value = value * decode_conversion_scale + decode_conversion_translate; }
class String { string name; string value; void Decode(istream& Stream); // Will be called from constructor }
class Person { string first_name; string second_name; void Decode(istream& Stream); // Will be called from constructor }
class Something { /*string name; Has NO name! */ void Decode(istream& Stream); }
[snip] -Best Regards, Larry

On 02/22/13 08:38, Larry Evans wrote:
On 02/22/13 00:16, Antony Polukhin wrote: [snip] In the example code from your last post, which is quoted below, the variant visitor is not used to decode this input stream, it's only used to perform some operaton on the variant. Quoting from Chris' OP:
I will be decoding many of these "packets" and doing various things with them like printing their values to console, etc.
That quote mentions two purposes:
1) Decoding packets 2) Operating on the decoded packets
The code quoted below does 2); however, the 1) is done by the Decode
This should read: The variant visitor code quoted below does 2)
methods shown below, the most complicated of which is the EmployeePacket::Decode. This EmployeePacket::Decode is somewhat like a very simple Spirit alternative parser:
http://www.boost.org/doc/libs/1_53_0/libs/spirit/doc/html/spirit/qi/referenc...
Actually, I think a Spirit would be an instance of the "inversion of control": http://martinfowler.com/bliki/InversionOfControl.html of EmployeePacket::Decode. IOW, the Decode method of processing the binary input stream uses "Normal Control" whereas using spirit to process the input stream would use "Inversion of the Normal Control" to process the binary input stream. That's because, like the #ruby example in the InversionOfControl.html, the Decode method uses "Normal Control" because it decides when to read the input stream (corresponding to the #ruby example's "when to ask questions, when to read responses"), and using a switch statement, when to call another Decode function to process the read data (corresponding to the #ruby example's "when to process those results".). OTOH, spirit, AFAICT, uses "Inverson of the Normal Control" in that it, instead of the user, decides when to read the input and when to call the processing functions (a.k.a the semantic actions, or the "process_name" and "process_quest" functions in the Tk exmaple in InversionOfControl.html). Hence, if you decide to use spirit, I think the Decode functions would have to be rewritten to be semantic actions to be called by the spirit's parsers :( [snip] HTH. -Larry

I'm working on a boost-based solution that addresses most of my concerns. I'll post here for review. Thanks again, Chris

On 02/20/13 19:21, Chris Stankevitz wrote:
On Wed, Feb 20, 2013 at 3:27 PM, Richard
wrote: Boost.Spirit.Qi to parse the stream into data structures Boost.Spirit.Karma to emit the necessary output from the data structures
Richard,
Thank you. I am unfamiliar with Spirit and Karma. Your comment not only introduced me to them but highlighted just how vague my question was. I was thinking less about the actual decoding (who would have known based on what I wrote) and more on how I would "generically" and "extensibly" handle the many fields I am about to receive.
For example, consider the following horrible code. Is there a "boost" way to do this kind of thing... (perhaps Spirit/Karma is the answer and I am just more out of touch than I imagine):
Good things about the classes below: It is easy to add a new parameter. Just push one onto the back of EmployeePacket::Items The class EmployeePacket doesn't have hundreds of data members such as "string name_, int age_"
Bad things about the classes below: Extracting data from the EmployeePacket requires hideous dynamic_casts and hard-coded vector indices
Thank you again for your comments/criticisms/suggestions,
Chris
===
class EmployeePacket { std::vector
- Items;
EmployeePacket() { Items.push_back(new String("name", "John Doe")); Items.push_back(new Double("salary", "USD", 1, 1)); }
void Decode(istream& Stream) { for (auto pItem : Items) pItem->Decode(Stream) }
double GetSalary() { return dynamic_cast
(Items[1])->value; } } class Item { virtual void Decode(istream& Stream) = 0; }
class Double : public Item { string name; string units; // double decode_conversion_scale; // double decode_conversion_translate; // unsigned number_of_bits_used_to_encode // double scale_factor double value; void Decode(istream& Stream) { Stream.read(&value, 8); value = value * decode_conversion_scale + decode_conversion_translate; }
class String : public Item { string name; string value; void Decode(istream& Stream); } Hi Chris,
How do you decide whether the Stream argument to Decode contains a String or a Double? Is that already known? IOW, if you open the istream, do you *know* what types it contains already and only need to fill in the values? -regards, Larry

On Thu, Feb 21, 2013 at 9:04 AM, Larry Evans
How do you decide whether the Stream argument to Decode contains a String or a Double? Is that already known? IOW, if you open the istream, do you *know* what types it contains already and only need to fill in the values?
Thank you all for your interest and help. I LOVE boost and have been using it for almost a year now. I cannot live without signals2, thread, and shared_ptr. They have changed the way I. I am eager to learn about the other components. I created a simple program that basically does what I want to do. Of course in my real application the objects are more complicated and this is just one small piece of everything I need to do. What I hope to highlight with this sample cpp is: 1. I am decoding data. The format is about as simple and "fixed" as you can imagine. 2. The data is not "POD-encoded" values. Sometimes I have to perform conversions on the values. For example "temperature" in the example. 3. I would like to keep track of some static known-at-compile-time meta-data for each of the values such as the units they are in. This I use for display purposes. 4. Some values have meta data that other values do not, such as floating point values which have a "number of significant digits after the decimal point" which I illustrate with the "temperature" example. 5. I will have many values. ~60. In my example I attempt to highlight what a mess the class will turn into if I make no attempt to use boost::variant, boost::any, polymorphism, etc. 6. An external class "TCTimer" in my example will want access to Get/Set values in the class. 7. I appear to be confusing/merging several concepts: 1) decoding 2) tracking meta data for members 3) replace-lots-of-members-and-with-a-vector-of-something-like-boost::variant Again, thank you, Chris

On 02/21/13 12:53, Chris Stankevitz wrote:
On Thu, Feb 21, 2013 at 9:04 AM, Larry Evans
wrote: How do you decide whether the Stream argument to Decode contains a String or a Double? Is that already known? IOW, if you open the istream, do you *know* what types it contains already and only need to fill in the values?
Thank you all for your interest and help. I LOVE boost and have been using it for almost a year now. I cannot live without signals2, thread, and shared_ptr. They have changed the way I. I am eager to learn about the other components.
I created a simple program that basically does what I want to do. Of course in my real application the objects are more complicated and this is just one small piece of everything I need to do.
What I hope to highlight with this sample cpp is:
1. I am decoding data. The format is about as simple and "fixed" as you can imagine.
2. The data is not "POD-encoded" values. Sometimes I have to perform conversions on the values. For example "temperature" in the example.
3. I would like to keep track of some static known-at-compile-time meta-data for each of the values such as the units they are in. This I use for display purposes.
4. Some values have meta data that other values do not, such as floating point values which have a "number of significant digits after the decimal point" which I illustrate with the "temperature" example.
5. I will have many values. ~60. In my example I attempt to highlight what a mess the class will turn into if I make no attempt to use boost::variant, boost::any, polymorphism, etc.
6. An external class "TCTimer" in my example will want access to Get/Set values in the class.
7. I appear to be confusing/merging several concepts: 1) decoding 2) tracking meta data for members 3) replace-lots-of-members-and-with-a-vector-of-something-like-boost::variant
This 7th feature is not represented by your example code. The example
code suggests the type being read is already known. OTOH, your
previous example code had a vector of pointers to an abstract class:
class EmployeePacket
{
std::vector
Again, thank you,
Chris
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users

AMDG On 02/20/2013 03:05 PM, Chris Stankevitz wrote:
I am receiving a "live" stream of binary data that represents the states of various objects. Sort of like this: Car Speed (m/s): float32 Temperature (deg): uint16 ...
My real packets have hundreds of items in them. I will be decoding many of these "packets" and doing various things with them like printing their values to console, etc.
My plan is just to make classes:
class Car { public: void Decode(istream&); private: float speed; unsigned temperature ... };
But it will not be very extendable.
Unfocused open-ended question: Does boost offer something that will help me with this?
I suppose that you could try something with Boost.Fusion (warning untested): BOOST_FUSION_DEFINE_STRUCT_INLINE(struct Car, (float, speed) (unsigned, temperature) ) struct decoder { typedef void result_type; template<class T> void operator()(T& t, istream& is) { decode(t, is); } }; // recursively decode nested structs template<class T> typename boost::enable_if< boost::fusion::traits::is_sequence<T>
::type decode(T& t, istream& is) { boost::fusion::for_each(t, boost::phoenix::bind(decoder(), _1, is)); }
// load arithmetic types directly template<class T> typename boost::enable_if< boost::fusion::traits::is_arithmetic<T>
::type decode(T& t, istream& is) { ... }
Anyway, the idea is that Boost.Fusion allows you to iterate over the members of a struct. In Christ, Steven Watanabe
participants (6)
-
Alex Perry
-
Antony Polukhin
-
Chris Stankevitz
-
Larry Evans
-
legalize+jeeves@mail.xmission.com
-
Steven Watanabe