Derive from std: string to add typedefs and enumerations

For UDP packets exchanged between client and server, I would like to support two types of string fields:

  • null terminated c string named cstring
  • a string with a previous uint8_t dimension field named vstring

To self-document the layout of our packages, I would like to use simple struct declarations:

 struct ABC { vstring a; cstring b; } 

And call the overloaded functions get(char*, vstring& v) and get(char*, cstring&) inside the function / serialization, for example:

 void deserialize(const char* bytes, ABC& msg) { get(msg.a); get(msg.b); } void serialize(char* bytes, const ABC& msg) { put(msg.a); put(msg.b); } 

However, for the user, vstring and cstring should ideally behave like a normal std::string .

My first idea was to simply make std::string public base of vstring and cstring so that the two classes could be disproved at the time of overload, but they would be the same for the user. But since the output from std::string discouraged, I'm not sure what to do.

+5
source share
3 answers

The danger of getting std::string is that the destructor is not virtual, so someone can do this:

 std::string* p = new vstring; delete p; 

You will have undefined behavior. If you think that such a code has no chance of being written in your environment / system, knock yourself out and get it from std::string .

Guide:

  • If the vstring and cstring are used only in a very limited, controlled environment - preferably by one or more developers with whom you can communicate the expected and keep track of the actual - use or where it is clear that dynamic allocation and polymorphism will not be abused to deal with them, all is well.

  • On the other hand, if they are located in your interfaces to an undetermined amount of uncontrolled code, where it is not even useful to tell potential potential developers about the problem, this is not good, and the scenario in which you prefer not to deduce from types without virtual destructors.


However, do you really need to code the serialization style in the type? In any case, they should write serialization and deserialization procedures that list the fields to be serialized, and they could indicate the format (NUL-terminated vs. length-prefixed or something else) there too.

+4
source

Ideologically, the serialization method should not affect the data type, so I better do something like this:

 void serializeNullTerminated(char* bytes, const std::string& msg); void deserializeNullTerminated(const char* bytes, std::string& msg); void serializeWithSize(char* bytes, const std::string& msg); void deserializeWithSize(const char* bytes, std::string& msg); 

Or pass an additional parameter to the functions:

 void serialize(SerializationType st, char* bytes, const std::string& msg); void deserialize(SerializationType st, const char* bytes, std::string& msg); 

Or you can make them a template:

 template<SerializationType st> void serialize(char* bytes, const std::string& msg); template<SerializationType st> void deserialize(const char* bytes, std::string& msg); 

The fact is that the user does not have to deal with various types of lines, in their code they must choose the method of serialization / deserialization.

+2
source

You must separate the calculations from the messages. Therefore, in your program you work with std::string , and when you need to send / receive strings, you send messages that correspond to your protocol.

So, the only public interfaces of your library / application / something like:

 int send_message(std::string const &s, int mode, ... destination ... etc); int receive_message(std::string &s, int mode, ... source ... etc); 

(with possible char *c overloads (with zero termination) for s ).

when the mode is the flag MODE_CSTRING or MODE_VSTRING .

Internally, you send / receive:

  • character vector with zero character or
  • size and then character vector.

You do not even need to create classes for cstring and vstring .

+1
source

All Articles