Filippo Lauria
Filippo Lauria

Reputation: 2064

How can I determine what type a (serialized and templatized) object received via socket is?

I'm using google protobuf to implement a simple Request/Response based protocol.

A peer can receive via socket both Request and Response, (of course serialized) as string.

I'm using my own c++ socket implementation, so I implemented operator>> this way (the same is for operator<<) to receive data from a socket object:

 ...
template<class M>
void operator>>(M& m) throw (socks::exception) {
    std::string str;
    if (!this->recv(str)) {
        throw socks::exception(">> failed to retrieve stream via socket");
        return;
    }

    if (!m.ParseFromString(str))
        throw socks::exception(
                "failed to parse the received stream via socket");
}

So template argument M can be objects Request and Response.

// some lines from req_res.proto

message Request {
  required action_t action = 1; 
}

enum result_t {
   ...
}

message Response {
  required result_t result  = 1;
   ...
}

How can I determine whether I received a Response or a Request using operator>> this way?

my_socket_object s;
 ...
for (;;) {
  Request|Response r;
  s >> r;
   ...
}
 ...

Upvotes: 4

Views: 109

Answers (2)

πάντα ῥεῖ
πάντα ῥεῖ

Reputation: 1

You can have one basic Message object and extend all other types used in your protocol from it:

message Message {
    extensions 100 to max;
}

message Request {
    extends Message {
        optional Request request = 100;
    }
    required action_t action = 1; 
}

message Response {
    extends Message {
        optional Response response = 101;
    }
    required result_t result  = 1;
}

This is a bit more elegant, self contained and easier to extend, than the discriminator/union solution proposed in the other answer IMHO.


You can use this technique even further to structure e.g. your Request/Response messages like this

message Request {
    extends Message {
        optional Request request = 100;
    }

    extensions 100 to max;
}

message Action1 {
    extends Request {
        optional Action1 action1 = 100;
    }
    optional int32 param1 = 1;
    optional int32 param2 = 2;
}

message Action2 {
    extends Request {
        optional Action2 action2 = 101;
    }
    optional int32 param1 = 1;
    optional int32 param2 = 2;
}

Upvotes: 2

Janick Bernet
Janick Bernet

Reputation: 21194

One possible approach is to put any possible message inside another high level message as a kind of tagged-union:

enum protocolmessage_t {
  Request = 1;
  Response = 2;
}

message ProtocolMessage {
  required protocolmessage_t type = 1; 

  optional Request request = 10;
  optional Response response = 11;
}

Then you would provide this ProtocolMessage as the M parameter to the >> operator and you can check the type and extract the corresponding value element.

An alternative way is to prefix every message with 1 byte for the type, make a switch on that type and then call your >> operator with the corresponding type.

Upvotes: 1

Related Questions