cross platform-ness

Started by
10 comments, last by hplus0603 16 years, 8 months ago
Quote:it would be nice if there was just a library that would wrap the socket functions on all platforms and deal with endianess


The problem is that you need to know what the data is to be able to deal with endian-ness. Only your application knows that. Most of the libraries that go to the length of letting you describe your application data, by necessity, do a lot of other things too.

The most common solution is to build a serializer class, and use operator overloading or get/set functions to transfer data. Those functions would, in turn, do the appropriate swapping.

Rather than having a byte for format at the front, just define a format for network packets. For example, say that all packets are always in network byte order (big endian), and make the serializer class convert as necessary when running on a little-endian machine.

The best way to structure that code is to define a visitor template for each struct you want to send, that calls a template argument for each member. Then you can pass in an actor that does serialization, or de-serialization, and use the same visitor function, which reduces the risk of the reading and writing parts falling out of sync.

struct A {  int value;  float x, y, z;  template<typename Actor>  static void visit(A &a, Actor &act) {    act.visit(value, 0, 100000); // min/max values    act.visit(x, 0.01, -5000.0, 5000.0); // precision, min/max values    act.visit(y, 0.01, -5000.0, 5000.0); // precision, min/max values    act.visit(z, 0.01, -5000.0, 5000.0); // precision, min/max values  }};struct B {  std::string name;  int hitpoints;  template<typename Actor>  static void visit(B &b, Actor &act) {    act.visit(name);    act.visit(hitpoints, 0, 255); // min/max values  }};// the generic visitor functiontemplate<typename T, typename Actor> void Visit(T &t, Actor &a) {  T::visit(t, a);}class Writer {  public:    Writer();    void visit(int i, int min, int max) {      int bytes = htonl(i);      write_bytes(&bytes, sizeof(bytes)); // ignore min/max    }    void visit(float f, float prec, float min, float max) {      int bytes = htonl((int)(f / prec));      write_bytes(&bytes, sizeof(bytes)); // ignore min/max    }    void visit(std::string &str) {      int bytes = htonl(str.length());      write_bytes(&bytes, sizeof(bytes));      write_bytes(str.c_str(), str.length());    }};int SendMessage() {  // let's assume this sends a message containing an A and a B  A a;  B b;  b.name = "my name";  a.value = 7;  a.x = 14.5;  ...  Writer wr;  Visit(a, wr);  Visit(b, wr);  return ::sendto(socket, wr.data(), wr.size(), 0, &addr, sizeof(addr));}

enum Bool { True, False, FileNotFound };
Advertisement
PS: apologies for turning the "marshalling packets strategy" post also into a lesson in applied C++ templates.
enum Bool { True, False, FileNotFound };

This topic is closed to new replies.

Advertisement