I mostly work with C and have not used classes in quite a while. I am trying to use some class functions that someone else has created but I cannot get the deserialize() function to work. I understand what it does, but I cannot for the life of me figure out how to call this function. I have provided functions and how I am trying to call them below.
//Creates a packet
packet::packet(int t, int s, int l, char * d){
type = t;
seqnum = s;
length = l;
data = d;
}
// This function serializes the data such that type, seqnum, length, and data values are placed
// in a char array, spacket, and separated by a single space; that is, spacket contains the serialized data
void packet::serialize(char * spacket){
cout << "data: " << endl << endl;
sprintf (spacket, "%d %d %d %s", type, seqnum, length, data);
}
// This function deserializes a char array, spacket, which is the result of a call to serialize
void packet::deserialize(char * spacket){
char * itr;
itr = strtok(spacket," ");
char * null_end;
this->type = strtol(itr, &null_end, 10);
itr = strtok(NULL, " ");
this->seqnum = strtol (itr, &null_end, 10);
itr = strtok(NULL, " ");
this->length = strtol (itr, &null_end, 10);
if(this->length == 0){
data = NULL;
}
else{
itr = strtok(NULL, "");
for(int i=0; i < this->length; i++){ // copy data into char array
this->data[i] = itr[i];
}
}
}
And here is how I am trying to get this to work:
packet *test = new packet(1, 4, 4, message); //message is a *char with the data
test->serialize(sendbuf); //this works correctly
packet *test2 = new packet(0,0,0, NULL); //I am not sure if I need to be creating a new packet for the deserialized information to get placed into
test->deserialize(sendbuf); //results in a segmentation fault currently
I just don't understand how to call deserialize(), I have created a packet and serialized it and that part works fine, but I don't understand how to reverse it. Do I need to create an empty packet object first? If so, how? I have tried doing that multiple ways but I cannot get it to work. I know this is very basic but like I said, I have not worked with classes in a couple of years. It took me quite a while just to get serialize to work but I have tried everything I can think of for deserialize and am stuck.
#Pongjazzle,
I agree with Sam that the class design needed refinement. However, I think you can figure it out. Perhaps, you might want to do it this way to test your code, assuming sendbuf can hold all the serialized packet data.
packet *test = new packet(1, 4, 4, message);
test->serialize(sendbuf);
packet *test2 = new packet(0,0,0, NULL); // results in a segmentation fault currently (which is expected as the attempts to access a location referred to by a null pointer in this->data (i.e., NULL based on the object instantiation code)
test->deserialize(sendbuf);
Change it to:
packet *test2 = new packet(0,0,0, newmessage); // assign a valid buffer
test2->deserialize(sendbuf); // Now fill's in the values and buffer from serialized content.
The class you're using is not designed very well.
For starters, the serialize() function takes a pointer to an output buffer, without having any means to specify the size of the buffer. It takes it on faith that the buffer is going to be big enough for the "serialized" data. If it's not, it will happy scribble over random memory.
Then, deserialize() is also impressive. For starters, no self-respecting deserializer requires a mutable pointer to the data being deserialized. A deserializer should only require a constant, or a read-only pointer.
Of course, the reason that this deserialize() wants a mutable buffer is because it scribbles over, and overwrites via strtok(), the buffer it's deserializing. Which means that you cannot use the serialized object to deserialize two or more instances of the object, unless you make a copy of the serialized object beforehand.
It is not actually clear, from what you've shown, where the actual bug is, but it's most likely because you did not allocate a buffer that's big enough for the serialized object. Even though you believe that "it works correctly", it didn't, and ended up corrupting memory, which didn't become evident until the code tried to deserialize the corrupted buffer, resulting in undefined behavior.
But, if you do believe that your buffer was big enough, you should be able to figure out the answer yourself by using your debugger to step through the code, and examine what it's doing. For problems involving segmentation faults at runtime, the correct answer is to always use a debugger, to examine the runtime status of the application and determine the problem.
Related
suppose i have a function like this
int writetofile(wstring name, any sdata){
...
return error;
}
This function have no idea about what data would be stored but would need to know the size of the data stored in sdata. Although it is easy to determine the type of data stored in the sdata but i don't think there is some easy way to know about the size of data in sdata.
i have a data structure which has members of type wstring. Now We cant write that data structure directly to the file as it contains that wstring. As far as i have researched on internet, the best way to write wstring or string, is to write size first of the string and then the string. Then when i would read the string first reading the size then after this read that much of size.
for this i have made a function.
int filemanager::write(any data, fileid uid, DWORD *byteswritten) const
{
// files is a map<fileid, fileinfo> where fileinfo is a struct which has
// members including file's name and handle
// fileid is a typedef of int
if (!files.count(uid)) return -1;
if (!data.has_value()) return -2;
if (data.type() == typeid(wstring)) {
DWORD sz1, sz2;
wstring str = any_cast<wstring>(data);
size_t sz3 = str.length()*sizeof(wchar_t);
if (sz3 == 0) return -2;
if (FALSE == WriteFile(files[uid].handle, &sz3, sizeof(size_t), &sz1, NULL)){
return GetLastError();
}
if (FALSE == WriteFile(files[uid].handle, str.c_str(), sz3, &sz2, NULL) && sz2 != sz3) {
return GetLastError();
}
if (byteswritten != nullptr) *byteswritten = sz1 + sz2;
}
else {
// now if the type is not a wstring then just write it to a file
// here i would need the size of the data stored in the data
}
return 0;
}
std::any is not the correct tool for what you're trying to do. It is a tool primarily intended for communicating between points A and B, where both points know what the type is, but the communication needs to happen through some intermediate code C that doesn't need to know the type.
If B needs to try a bunch of different casts to see which value was provided, any is not the right tool for that job. The cast is there for type safety reasons: to have a well-defined failure path in the event that the wrong any was provided. That is, to make sure that A and B are communicating correctly. It's not there so that you can try a bunch of different things.
You can't ask any what the size of the stored object is because you should already know the answer. And even if you didn't know the answer, you could never validly use that answer.
Take your use case, for example. any is not TriviallyCopyable, so it is not legal C++ to copy its bytes directly to a file and then copy them back. And even if it were conceptually OK to do so, any may only be storing a pointer to the object it contains. So you'd just be writing a pointer to the file.
Serialization in C++ is not going to be as simple as you're trying to make it.
In my program I have a char* buffer which is being used inside a thread sequence which carries text from one function into another, but the text is different through the run-time in my program. The question that I am asking is, which function can I use to clear the previously used text out of the char* ?
For example, I have the following code:
int GameUtils::GetText(char *text)
{
for(int i=0; i<LINES_OF_TEXT; i++)
{
if(line[i][0]!=0)
{
strcpy(text, line[i]);
MessageBox(0, text, 0, 0);
line[i][0]=0;
return 1;
}
}
return 0;
}
line is defined as such: char GameUtils::line[2][32];
When the messagebox is output on the screen (while code is executed). I get some random junk characters in the text field. Can anyone tell me why this is?
Also! Note that line is assigned as stated in my previous question.
The function which assigns line is:
for (int x=0; x<((int)(strlen(szLine)+1)); x++)
{
if (szLine[x]==' ' || szLine[x]=='\0')
{
m=x;
for (y=0, z=n; z<m; y++, z++)
{
line[w][y]=szLine[z];
}
n=x+1;
w++;
}
}
The above function simply takes a parameter szLine[512] which is passed from my game interface and splits up the line assorting each space as a new parameter.
As an example, if inside the game the user states the line:
/msg <player> <message>
The function would assign each separate word to the line variable, respectively.
Such that, after the function is finished. line would look like
line[0] = /msg
line[1] = <player>
line[2] = <message>
So my question overall is as follows. Am I taking the cleaniest/most appropriate approach at this problem? If not, can anyone show me a better way to approach this problem? Also, can anyone explain to me why I am getting junk characters in the text parameter when the messagebox executes?
EDIT
After viewing the preview of my submitted question; I noticed I have defined char GameUtils::line[2][32] as a 2-dimensional array. I had done this earlier to test. I now understand this could have been the cause to my problem. Can anyone suggest me a replacement for this if I don't know the exact amount of parameters that could be inputted into this variable. The user can issue different requests each time like "/help ", "/msg ", "/whois ", "/create "...
When memory is allocated it isn't zeroed first (at least when using malloc, calloc - however, does zero memory first).
To clear a buffer in C (rather than C++), you have a few options:
Allocate the buffer using calloc instead of malloc.
Use Win32's ZeroMemory function
Use memset, like so: memset( buffer, 0x00, BUFFER_SIZE );
However you're clearly using C++, so you should use the standard library and C++ idioms rather than C-style things, that means using std::string instead of char*, and if you have to use buffers directly then the C++ way of zeroing (or filling) an array or buffer is std::fill.
First off, I would avoid using double dimensional arrays if you can avoid it. Maybe look into std::string:
http://www.cplusplus.com/reference/string/string/
As for why a char array might have "random junk" in it, when you allocate a buffer in C++, it always has data in it. You have to manually set the data to 0 if you want it to be empty. So when you first allocate an array, it might be a idea to zero out all the values first.
I have a Struct to send over a socket to a client. Both the Client and the Server is on the same architecture so there is no endian problem. I receive the int values properly. But not able to receive the char[] values properly.
This is the structure.
struct Packet {
int id;
int number;
char data[256];
};
In the Server side I serialize the data and write to the client.
struct Packet *s = new Packet();
s->id= htonl(1000);
s->number= htonl(7788);
memcpy(s->data, "MESSAGE", 7);
n = write(NewSockFD , s ,sizeof(s) );
In the Client side I deserialize the data.
n = read(SockFD , Buffer , sizeof(Buffer));
struct Packet *s = (struct Packet*)Buffer;
char b[256];
int i = ntohl(s->id);
int j = ntohl(s->number);
memcpy(b, s->data, sizeof(s));
I receive the id and number values correctly. Problem is with the data value. What I'm doing wrong here??..
In your code, you use sizeof(s). This will be the size of a Packet*, not a Packet. Replace it with sizeof(*s) to get the correct size.
Additionally, since the values of data are not all initialised, you cause undefined behaviour by reading from it. You need to initialise all the elements one way or the other (the shortest way would be to do char data[256] { }; in the struct definition).
Also since this is C++, you don't need to say struct Packet, you can say just Packet, unless you also have a function named Packet. But it appears in your code only half the time which means you don't, so you can just drop it.
And as Chris G mentioned, you have another problem after you fix that, which is that you're copying an entire Packet into a char[] only big enough to hold a Packet's data. Change
memcpy(b, s->data, sizeof(s))
to
memcpy(b, s->data, sizeof(s->data))
And realise that this data may not be nul-terminated if the sender didn't do it for you (which you may want to take precautions against).
My problem goes like this: I have a class called 'Register'. It has a string attribute called 'trainName' and its setter:
class Register {
private:
string trainName;
public:
string getTrainName();
};
As a matter of fact, it is longer but I want to make this simpler.
In other class, I copy several Register objects into a binary file, previously setting trainName.
Register auxRegister = Register();
auxRegister.setName("name");
for(int i = 0; i < 10; i++) {
file.write(reinterpret_cast<char*>(&auxRegister),sizeof(Register));
}
Later on, I try to retrieve the register from the binary file:
Register auxRegister = Register();
while(!file.eof()) { //I kwnow this is not right. Which is the right way?
file.read(reinterpret_cast<char*>(&auxRegister), sizeof(Register));
}
It occurs it does not work. Register does, in fact, have more attributes (they are int) and I retrieve them OK, but it's not the case with the string.
Am I doing something wrong? Should I take something into consideration when working with binary files and strings?
Thank you very much.
The std::string class contains a pointer to a buffer where the string is stored (along with other member variables). The string buffer itself is not a part of the class. So writing out the contents of an instance of the class is not going to work, since the string will never be part of what you dump into the file, if you do it that way. You need to get a pointer to the string and write that.
Register auxRegister = Register();
auxRegister.setName("name");
auto length = auxRegister.size();
for(int i = 0; i < 10; i++) {
file.write( auxRegister.c_str(), length );
// You'll need to multiply length by sizeof(CharType) if you
// use a wstring instead of string
}
Later on, to read the string, you'll have to keep track of the number of bytes that were written to the file; or maybe fetch that information from the file itself, depending on the file format.
std::unique_ptr<char[]> buffer( new char[length + 1] );
file.read( buffer, length );
buffer[length] = '\0'; // NULL terminate the string
Register auxRegister = Register();
auxRegister.setName( buffer );
You cannot write string this way, as it almost certainly contains pointers to some structs and other binary stuff that cannot be serialized at all.
You need to write your own serializing function, and write the string length + bytes (for example) or use complete library, for example, protobuf, which can solve serializing problem for you.
edit: see praetorian's answer. much better than mine (even with lower score at time of this edit).
So here is the bit of my code that's giving me problems:
void childProcessHandler(string command){
int argCounter = 0;
for(int i=0; i!=command.size(); i++)
argCounter+=( command.at(i) == ' ');
char * temp, *token;
char *childArgs[argCounter];
argCounter = 1;
temp = new char [command.size()+1];
strcpy (temp, command.c_str());
token = strtok (temp," ");
childArgs[0] = token;
while (token!=NULL)
{
token = strtok(NULL," ");
childArgs[argCounter] = token;
argCounter++;
}
//delete[] temp; //Should remove token as well?
execvp(childArgs[0], childArgs);
cout<<"PROBLEM!"<<endl;
exit(-1);
}
In the main() method my code gets to a point where it forks() (the parent process then waits for the child to exit.) then the child process (process ID == 0 yes?) calls the method childProcessHandler with the user input (command to run + args) as it's argument. Then I tokenize the user input and call execvp on it.
Everything compiles and executes. The line after execvp is never reached because execvp only returns when there is an error yes?
The project is to simulate a unix terminal however when I give it the command "date" nothing gets printed like it should... The child exits and the parent process resumes just fine however nothing is sent back up to the terminal window...
What am I doing wrong?
(Also we were "recommended" to use strtok to tokenize it but if anyone has anything simpler i'm open to opinions.)
THANKS!
EDIT
The above code works, for example, if I type in "date " instead of "date". I think there might be something fishy with the "tokenizer" not putting a null character at the end of the childArgs[] array. I'll play around with that and thanks for the quick responses!
(Ninja edit, also commented out delete[] temp for the time being)
You're mixing std::string and char/char*. Fine, but you have to be careful, they have different behaviours.
In particular this line:
temp = new char [command.size()+1];
Is creating an actual array to hold a string in.
token = strtok (temp," ");
This is making token (which is just a pointer) point to a place inside temp. strtok() modifies the input string to make a temporary string within a string (sounds crazy, I know).
You need to copy the string strtok() gives you into a permanent home. Either use std::string to save you time and code, or do it the char* way and allocate the new string yourself. E.g. instead of:
childArgs[0] = token;
you need:
childArgs[0] = new char[strlen(token)+1];
strcpy(childArgs[0], token);
The same applies to tokens stored in the array during the loop over the command arguments.
Your childargs vector of pointers point into the bytes allocated in the block of memory "temp". When you free temp, you are removing the memory pointed to by the childargs pointers, possibly corrupting some of the values within your vector.
Remove the call to delete[] to stop freeing the memory pointed to by the childargs pointers. You will not be leaking memory. Once you call exec_() your entire process image is replaced anyway. The only thing that survives a call to exec_() (for the most part) are your file descriptors.
As a test, try something a bit more simple: After your call to fork() in the child, just call exec with the path to "date". Make that work before fiddling with the parameter list vector.
As another test, remove your call to exec, and print out your entire vector of pointers to make sure that your tokenizing is working the way you think it should. Remember that your final entry must be NULL so that you know where the end of the vector is.