Vector of types

A

Andrea Crotti

So I have the following situation, a simple Packet type which is the
superclass of all the possible packet types.

Then I have many subclasses, for example this:

class CoordAns : public Packet
{
private:
PadCoordinate ans;
PadCoordinate dest;
node_idx_t node;

and for this the code below creates and parses a stream.

Stream CoordAns::toStream() const
{
Stream res = FieldHeader::sizeField(ans);
res += FieldHeader::sizeField(dest);
res += FieldHeader::basicSizeField(node);
return FieldHeader::typeField(res, COORD_ANS);
}

CoordAns CoordAns::parseStream(Stream &raw)
{
CoordAns cr;
Stream tmp;
size_t next = FieldHeader::getNextSize(raw);
tmp = raw.trim(next);
cr.ans = PadCoordinate::parseStream(tmp);

next = FieldHeader::getNextSize(raw);
tmp = raw.trim(next);
cr.dest = PadCoordinate::parseStream(tmp);

next = FieldHeader::getNextSize(raw);
tmp = raw.trim(next);
cr.node = tmp.toBasic<node_idx_t>();
return cr;
}


It's a bit ugly and more importantly it's the same for every packet
class, increasing as the size of the fields increase.

Since I have already quite generic functions my idea was to do the
following.

std::vector<FieldType *> fields;

and then use typeid(x).name() to get the right type for the templated
functions.
I would already have a superclass for most of the fields, but some are
basic types (int, size_t).

But if this approach WOULD work maybe I'd better define (just a few)
also some wrapper classes for those simple types.

Any other idea (which does not involve boost unfortunately?)?
 
A

Andrea Crotti

I tried with a simpler example and it surprisingly works!!

Stream Packet::toStream() const
{
Stream res;
// use the field list to get the stuff out
for (size_t i=0; i < fields.size(); ++i) {
std::cout << typeid(*fields).name() << std::endl;
res += FieldHeader::sizeField(*fields);
}

return FieldHeader::typeField(res, type);
}

now I can define ONCE this function for all.
The only "problem" is to add 2/3 more classes to make all the
types Serializable, and then everything should be fine :)
 
A

Andrea Crotti

toStream seems to work very well, very happy about that.
But things get more complicated with the parseStream.

This must be a template function and static also, but the
fields variable is not static and I can't access to it.

And also I'm not sure this is going to work "res.(*fields) = "

template<typename T>
static T parseStream(Stream& raw) {
T res;
size_t next;
Stream tmp;
for (size_t i = 0; i < fields.size(); ++i) {
next = FieldHeader::getNextSize(raw);
tmp = raw.trim(next);
res.(*fields) = T::parseStream(tmp);
}
return res;
}
 
A

Andrea Crotti

Very nice for the basic types I even have a templated class now, so it's
even easier...

#ifndef BASICTYPE_H
#define BASICTYPE_H

#include "Serializable.hpp"
#include "Stream.hpp"

template<class T>
class BasicType : public Serializable
{

public:
T value;
BasicType() {}
BasicType(T _value) : value(_value) {}
Stream toStream() const {
return Stream::fromBasic<T>(value);
}

static BasicType<T> parseStream(Stream& raw) {
BasicType<T> res;
res.value = raw.toBasic<T>();
return res;
}

bool operator==(const BasicType &other) const {
return (value == other.value);
}
};

#endif /* BASICTYPE_H */


But for the other way around I don't see how I can do that.
From something like
"A02301sdfJSADJKAkj2039" (a stream of data)
I have to create an object with all the fields of some type set to some
value.
So I have to pass to it probably a vector of the types, but I don't get
how (if it's possible at all).
 
A

Andrea Crotti

Good I don't even need the generic parseStream anymore.
I created another template function and now I can do

TestPkt TestPkt::parseStream(Stream& raw)
{
TestPkt pkt;

pkt.coord = FieldHeader::getNextSection<PadCoordinate>(raw);
pkt.idx = FieldHeader::getNextSection<NodeIdx>(raw);

return pkt;
}

which is really not bad, really clear and concise.
 
J

James Kanze

I tried with a simpler example and it surprisingly works!!
Stream Packet::toStream() const
{
Stream res;
// use the field list to get the stuff out
for (size_t i=0; i < fields.size(); ++i) {
std::cout << typeid(*fields).name() << std::endl;
res += FieldHeader::sizeField(*fields);
}
return FieldHeader::typeField(res, type);
}


For what definition of "works"? You have no control over what
typeid().name() returns. It varies greatly from implementation
to implementation, and may change from one version to the next
within a given implementation. For all practical purposes,
typeid().name() is useless except for debugging messages (and
in some implementations, like g++, it's useless for that as
well).

The usual way to handle serialization is to hand write the
serialization of the fundamental types, and then use a code
generator for the rest: define a simple "language" to specify
the serializable data types, and write a simple program (often
in a scripting language like AWK) to "compile" it to C++, with
the necessary functions.
 
A

Andrea Crotti

James Kanze said:
For what definition of "works"? You have no control over what
typeid().name() returns. It varies greatly from implementation
to implementation, and may change from one version to the next
within a given implementation. For all practical purposes,
typeid().name() is useless except for debugging messages (and
in some implementations, like g++, it's useless for that as
well).

The usual way to handle serialization is to hand write the
serialization of the fundamental types, and then use a code
generator for the rest: define a simple "language" to specify
the serializable data types, and write a simple program (often
in a scripting language like AWK) to "compile" it to C++, with
the necessary functions.

NO no I didn't mean that works, I don't use typeid, it's just this the
code

Stream Packet::toStream() const
{
Stream res;
// use the field list to get the stuff out
for (size_t i=0; i < fields.size(); ++i) {
res += FieldHeader::sizeField(*fields);
}
return FieldHeader::typeField(res, type);
}

for the serialization of basic types I solved with:

#ifndef BASICTYPE_H
#define BASICTYPE_H

#include "Serializable.hpp"
#include "Stream.hpp"

template<class T>
class BasicType : public Serializable
{
private:
T value;

public:
BasicType() {}
BasicType(T _value) : value(_value) {}
Stream toStream() const {
return Stream::fromBasic<T>(value);
}

static BasicType<T> parseStream(Stream& raw) {
BasicType<T> res;
res.value = raw.toBasic<T>();
return res;
}

bool operator==(const BasicType &other) const {
return (value == other.value);
}

T getValue() const { return value; }

template<typename X>
friend std::eek:stream& operator<<(std::eek:stream&, const BasicType<X>&);
};

template<typename T>
std::eek:stream& operator<<(std::eek:stream& s, const BasicType<T>& basic)
{
s << basic.value;
return s;
}

#endif /* BASICTYPE_H */

it's working quite well apparently, for example if I do

BasicType<unsigned int> var(10);

this var can generate the correct stream and get the right stream.
Maybe another thing would be to redefine operator=, so I can do
BasicType<int> var = 10;
which looks even better.

Why by the way I have to define operator<< in this way?
Like this it works but I didn't get the double template...
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top