0

In C I was used to work with char* to which I mapped some structure representing a network packet. Then I would just assign the packet parameters and send via network:

UPDATED - I forget to add that I have some payload

struct packet {
   uint64_t id __attribute__((packed)); 
   uint64_t something __attribute__((packed));
   short flags __attribute__((packed));
   // here follows payload with variable length
};
...
char *payload = get_payload();
size_t payload_size = get_payload_size();//doesnt matter how
char *data = malloc(sizeof(struct packet)+payload_size);
struct packet *pkt = (struct packet *)data;
pkt->id = 123;
pkt->something = get_something();
pkt->flags = FLAG | FLAG2;
memcpy(data+sizeof(struct packet), payload, payload_size);
send(data, sizeof(struct packet)+payload_size);

Now I would like to implement this with some nice C++ features (if there are any which could do this in better way) Best change I can do in C++ is to use new instead of malloc.

char *data = new char[sizeof(struct packet)];//rest is same

Ok this is called object serialization.. I ve found couple of examples always using boost which I cannot use.. isnt there a native way in C++ to acomplish this?

nayana
  • 3,787
  • 3
  • 20
  • 51
  • 1
    What you are asking about is generally known as "object serialization". Try googling or seraching SO for "C++ object serialization". – piokuc Dec 11 '15 at 15:52

2 Answers2

2

There is no reason to create a char* and an pkt*. You create and send an object like:

packet pkt;  // create automatic object
pkt.id = 123;
pkt.something = get_something();
pktflags = FLAG | FLAG2;
// here we cast the address of pkt to a char* so we can send it
send(reinterpret_cast<char*>(&pkt), sizeof(packet));

Edit:

Since the question was updated to have a payload added to the end of the struct data you are going to have to use the same method that you did in C. When you new up the pointer the syntax would be

char * data = new char[sizeof(packet) + payload_size];

Also the struct keyword is not required in C++ when using the struct like in C. You can just use it by name.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • thanks, I like your solution, but I forgot to add that I am using payload after the packet.. sorry for changing the question. will the "reinterpret_cast way" work if the packet would contain for example char * to the payload? – nayana Dec 11 '15 at 16:04
1

I don't like the way your original code depends on a non-standard extension __attribute__((packed)). Since there is no standard way to require that a struct has no padding at all, the only standard compliant way to copy a struct into a buffer is to copy each member individually.

That inspired me to write a c++11 helper to copy a variadic number of variables into a char array. And to help using that, a helper to count the total size of those variables (that was already done here). Using these helpers, you can easily copy the members of a struct into a buffer without requiring the struct to be packed.

// variadic sizeof
// mostly same as https://stackoverflow.com/a/12666815/2079303
constexpr std::size_t sizeof_all() {
    return 0;
}
template <typename Head, typename... Tail>
constexpr std::size_t sizeof_all(const Head& head, const Tail&... tail) {
    return sizeof head + sizeof_all(tail...);
}

// variadic memcpy
void memcpy_all(char*) {}
template <typename Head, typename... Tail>
void memcpy_all(char* out, const Head& head, const Tail&... tail) {
    auto offset = sizeof head;
    std::memcpy(out, &head, offset);
    memcpy_all(out + offset, tail...);
}

// to use
packet p;
std::size_t buffer_size = sizeof_all(p.id, p.something, p.flags) + payload_size;
char* data = new char[buffer_size];
memcpy_all(data, p.id, p.something, p.flags);

Now, you only need to concern yourself with endianness.

Community
  • 1
  • 1
eerorika
  • 232,697
  • 12
  • 197
  • 326