I'm trying to write a reusable message object that would take its properties, convert them into a delimited string (using 0x1d
group seperator), put that in a char
buffer, and also be able to do the reverse (from char
back to object).
This reason why I must do this is that I am completely limited by the OS capabilities to only send messages that are of type char
and a fixed size, so this is my wrapper for that.
Here is what I have so far. Easy to pack.. now, how can I write a sensible way to unpack it. I am OK if every child of this class must manually unpack this data, but I just don't see how. I tried getline
but then I end up with a string and will have to write many conversion functions.. There must be a simpler way.
Note I am in C++98.
#include <iostream>
#include <sstream>
#include <string.h>
class Msg {
public:
Msg(){
delim = 0x1d;
}
int8_t ia;
int16_t ib;
int32_t ic;
int64_t id;
uint8_t ua;
uint16_t ub;
uint32_t uc;
uint64_t ud;
std::string str = "aaa bbb ccc dddd";
char sz[64];
char delim;
// convert to a char buffer
void ToBuffer(unsigned char* b, int s){
std::stringstream ss;
ss << ia << delim
<< ib << delim
<< ic << delim
<< id << delim
<< ua << delim
<< ub << delim
<< uc << delim
<< ud << delim
<< str << delim
<< sz << delim;
strncpy((char*)b, ss.str().c_str(), s);
b[s-1] = '\0';
}
// convert from a char buffer
void FromBuffer(unsigned char* b, int s){
// what on earth to do here..
// could use getline which returns a string after
// each delimiter, then convert each string to the
// value in a known order.. but at that point I may
// as well have written this all in C... !
}
void Print(){
std::cout
<< " ia " << ia
<< " ib " << ib
<< " ic " << ic
<< " id " << id
<< " ua " << ua
<< " ub " << ub
<< " uc " << uc
<< " ud " << ud
<< " str " << str
<< " sz " << sz;
}
};
int main()
{
Msg msg;
msg.ia = 0xFE;
msg.ib = 0xFEFE;
msg.ic = 0xFEFEFEFE;
msg.id = 0xFEFEFEFEFEFEFEFE;
msg.ua = 0xEE;
msg.ub = 0xDEAD;
msg.uc = 0xDEADBEEF;
msg.ud = 0xDEADBEEFDEADBEEF;
snprintf(msg.sz, 64, "this is a test");
msg.Print();
int s = 128;
unsigned char b[s];
msg.ToBuffer(b, s);
Msg msg2;
msg2.FromBuffer(b, s);
//msg2.Print();
return 0;
}
Ok, so it works but it is quite ugly to put the buffer into a string stream just so you can use std::getline with a delimiter to extract bits and then use another string stream or std::stoi and friends to convert the items to the right types:
https://repl.it/repls/GainsboroInsecureEvents
void FromBuffer(unsigned char* b, int s){
std::string item;
std::stringstream ss((char *)b);
// You don't NEED to use std::stringstream to convert
// the item to the primitive types - you could use
// std::stoi, std::stol, std::stoll, etc but using a
// std::stringstream makes it so you don't need to
// know which primitive type the variable is
std::getline(ss,item,'\x1d'); std::stringstream(item) >> ia;
std::getline(ss,item,'\x1d'); std::stringstream(item) >> ib;
std::getline(ss,item,'\x1d'); std::stringstream(item) >> ic;
std::getline(ss,item,'\x1d'); std::stringstream(item) >> id;
std::getline(ss,item,'\x1d'); std::stringstream(item) >> ua;
std::getline(ss,item,'\x1d'); std::stringstream(item) >> ub;
std::getline(ss,item,'\x1d'); std::stringstream(item) >> uc;
std::getline(ss,item,'\x1d'); std::stringstream(item) >> ud;
// Until you get to here. Then >> stops on a space
// and all the sudden you can't use >> to get the data
std::getline(ss,str,'\x1d');
// And a C string is even worse because you need to
// respect the length of the buffer by using strncpy
std::getline(ss,item,'\x1d'); strncpy(sz,item.c_str(),64); sz[63] = '\0';
}
So I think a much better way is to create a new ctype facet that uses a new delimiter and imbue the string stream with the new facet like was done here changing the delimiter for cin (c++)
That way we can just extract directly which is MUCH better:
https://repl.it/repls/GraveDraftyAdministrators
void FromBuffer(unsigned char* b, int s){
struct delimiter : std::ctype<char> {
delimiter() : std::ctype<char>(get_table()) {}
static mask const* get_table()
{
static mask rc[table_size];
rc[0x1d] = std::ctype_base::space;
return &rc[0];
}
};
std::stringstream ss((char *)b);
ss.imbue(std::locale(ss.getloc(), new delimiter));
ss >> ia
>> ib
>> ic
>> id
>> ua
>> ub
>> uc
>> ud
>> str
>> sz;
}
User contributions licensed under CC BY-SA 3.0