Simple serialization example in c++ - c++

I have the following struct:
typedef struct{
int test;
std::string name;
} test_struct;
Then, I have the following code in the main function:
int main(int argc, char *argv[]){
test_struct tstruct;
tstruct.test = 1;
tstruct.name = "asdfasdf";
char *testout;
int len;
testout = new char[sizeof(test_struct)];
memcpy(testout, &tstruct, sizeof(test_struct) );
std::cout<< testout;
}
However, nothing gets printed. What's wrong?

sizeof(std::string) yeilds same value always. It will not give you the runtime length of the string. To serialize using memcpy, either change the struct to contain char arrray such as char buffer[20] or compute the size of the required serialized buffer by defining a method on the struct which gives the runtime length of the bytes.
If you want to use members like std::string, you need to go through each member of the struct and serialize.
memcpy(testout, (void *)&tstruct.test, sizeof(int) );
memcpy(testout+sizeof(int), tstruct.name.c_str(),tstruct.name.length() );
memcpy against the entire struct will not work in such scenarios.

Try NULL-terminating the string and also emitting a newline:
testout = new char[sizeof(test_struct) + 1];
memcpy(testout, &tstruct, sizeof(test_struct));
testout[sizeof(test_struct)] = '\0';
std::cout<< testout << std::endl;
However, as user3543576 points out, the serialization you get from this process won't be too useful, as it will contain a memory address of a character buffer, and not the actual string itself.

Related

Is there a better way to modify a char array in a struct? C++

I am trying to read in a cstring from a edit control box in MFC, then put it into a char array in a struct, but since I cannot do something like clientPacket->path = convertfuntion(a); I had to create another char array to store the string then store it element by element.
That felt like a bandait solution, is there a better way to approach this? I'd like to learn how to clean up the code.
CString stri;//Read text from edit control box and convert it to std::string
GetDlgItem(IDC_EDIT1)->GetWindowText(stri);
string a;
a = CT2A(stri);
char holder[256];
strcpy_s(holder,a.c_str());
int size = sizeof(holder);
struct packet {
char caseRadio;
char path[256];
};
packet* clientPacket = new packet;
for (int t = 0; t < size; t++) {
clientPacket->path[t] = holder[t] ;
}
EDIT:This is currently what I went with:
CString stri;//Read text from edit control box and convert it to std::string
GetDlgItem(IDC_EDIT1)->GetWindowText(stri);
string a = CT2A(stri);
struct packet {
char caseRadio;
char path[CONSTANT];//#define CONSTANT 256
};
packet* clientPacket = new packet;
a = a.substr(0, sizeof(clientPacket->path) - 1);
strcpy_s(clientPacket->path, a.c_str());
I got a problem where I got "1path" instead of "path", turns out it read in caseRadio='1', fixed it by reading out caseRadio first in the server
I don't see the need to create the intermediate 'holder' char array.
I think you can just directly do
strcpy(clientPacket->path, a.c_str());
You may want to do this:
a= a.substr(0, sizeof(clientPacket->path)-1);
before the strcpy to avoid buffer overrun depending on whether the edit text is size limited or not.
You can copy directly into a user-provided buffer when using the Windows API call GetWindowTextA. The following illustrates how to do this:
struct packet {
char caseRadio;
char path[512];
} p;
::GetWindowTextA(GetDlgItem(IDC_EDIT1)->GetSafeHwnd(), &p.path[0],
static_cast<int>(sizeof(p.path)));
This does an implicit character encoding conversion using the CP_ACP code page. This is not generally desirable, and you may wish to perform the conversion using a known character encoding (such as CP_UTF8).
Use the CString.GetBuffer function to get a pointer to the string. In your struct, store the path as a char* instead of a char array.
struct packet {
char caseRadio;
char* path;
};
packet* clientPacket = new packet;
clientPacket->path = stri.GetBuffer();
Like this, maybe? strncpy(clientPacket->path, CT2A(stri).c_str(), 255);.
Also, better make the 256 bytes a constant and use that name, just in case you change this in 10 years.

How to deal with char array which '\0' is inserted into?

When I try to copy a struct into a char array buffer using memcpy function, '\0' is inserted into the char array. I pass the buffer pointer to a function. the content before the first '\0' is past, but the rest is lost. How can I solve this problem? I would appreciate it if you help me.
here is the code, although the the struct Mystruct and the function display() is not designed reasonably。
#include <QCoreApplication>
#include <stdio.h>
struct MyStruct
{
char c;
int order;
char ch;
MyStruct(char c = 'b', int order = 0, char ch = 'a'):order(order), ch(ch), c(c)
{};
};
void display(char* str)
{
printf("%s", str);
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyStruct *mystruct = new MyStruct;
// char* test = new char[50];
char test[50];
memset(test, 0, 50);
memcpy(test, mystruct, sizeof(MyStruct));
display(test);
return a.exec();
}
I want to send struct like 'Mystruct' through socket and the function send(char*) requires a char* pointer pointing to a buffer. The '\0' is inserted into the char array when the int order is copied into the test[50].
We use QTcpSocket::write(char* ) of QT instead of APIs offered by Linux or Windows. And my teammate offer me an interface function send(char ).I need pass the buffer pointed to by the char pointer.
I simplized my question with the code showed and apologize for my confusing expression.And any good way to send the struct through socket?
C-string function works with nul-terminating string.
if you want to handle char* with nul non terminal string, you need size (or other sentinel).
MyStruct mystruct;
std::string buffer {(char*) &mystruct, sizeof (MyStruct)};
Demo
Notice that that way of serializing has lot of pitfall:
padding (you might see random value between c and order and also after ch (those 0 ARE random)).
endianess issue.
...

How can I declare a structure of an unknow size?

I have this structure :
struct __attribute__((packed)) BabelPacket
{
unsigned senderId;
unsigned dataLength;
unsigned char data[0];
};
And to declare it I do :
BabelPacket *packet = reinterpret_cast<BabelPacket *>(new char[sizeof(BabelPacket) + 5]);
packet->senderId = 1;
packet->data = "kappa";
packet->dataLength = 5;
But when I compile I have this error :
error: incompatible types in assignment of ‘const char [6]’ to ‘unsigned char [0]’
packet->data = "kappa";
^
Have you an idea how I can do that ?
And I need to send this structure through a socket, to get the object back in my server, so I can use only C types.
If this was a C program, the error you get is because you try to assign to an array, which is not possible. You can only copy to an array:
memcpy(packet->data, "kappa", 5);
Also note that if you want the data to be a C string, you need to allocate an extra character for the string terminator '\0'. Then you can use strcpy instead of memcpy above. Or strncpy to copy at most a specific amount of characters, but then you might need to manually terminate the string.
However, this should not work in C++ at all, unless your compiler have it as an extension.
You can't assign a literal string that way. You'll need to allocate additional memory for the string, then copy to the data pointer.
struct A {
size_t datasize;
char data[0]; // flexible member must appear last.
};
A* create_A(const char* str)
{
size_t datasize = strlen(str) + 1; // null terminated (?)
A* p = reinterpret_cast<A*>(new char[sizeof(A) + datasize]);
memcpy(p->data, str, datasize);
p->datasize = datasize;
return p;
}
A* p = create_A("data string");
This solution is only applicable in environments supporting zero-length or flexible arrays. In fact, a better solution may be to write the sockets code in C and export that interface for use in C++.
If you are willing/allowed to change the unsigned char to a regular char, you can use strcpy:
#include <iostream>
#include <stdio.h>
#include <string.h>
struct __attribute__((packed)) BabelPacket
{
unsigned senderId;
unsigned dataLength;
char data[0]; // I changed this to char in order to use strcpy
};
int main(){
BabelPacket *packet = reinterpret_cast<BabelPacket *>(new char[sizeof(BabelPacket) + 5]);
packet->senderId = 1;
// Copy the string. Add NULL character at the end of
// the string to indicate its end
strcpy(packet->data, "kappa\0");
packet->dataLength = 5;
// Verify that the string is copied properly
for (int i=0;i<packet->dataLength;++i){
std::cout<<packet->data[i];
}
std::cout<<std::endl;
return 0;
}
Note that this will only work if data is at the end of the struct, otherwise there is no contiguous memory to allocate data. If I swap the order of the elements to:
struct __attribute__((packed)) BabelPacket
{
unsigned senderId;
char data[0]; // I changed this to char in order to use strcpy
unsigned dataLength;
};
the output of the code above (instead of "kappa"), would be "a".
A more reliable way if you are determined to use C-arrays would be to assume a maximum number of elements and preallocate the array, i.e.:
#include <iostream>
#include <stdio.h>
#include <string.h>
#define MAX_NUMBER_OF_CHARACTERS 5 // Many ways to do this, I defined the macro for the purposes of this example
struct __attribute__((packed)) BabelPacket
{
unsigned senderId;
// I changed this to char in order to use strcpy. Allocate the
// max number + 1 element for the termination string
char data[MAX_NUMBER_OF_CHARACTERS+1];
unsigned dataLength;
};
int main(){
BabelPacket *packet = reinterpret_cast<BabelPacket *>(new char[sizeof(BabelPacket) + 5]);
packet->senderId = 1;
packet->dataLength = 5;
if (dataLength>MAX_NUMBER_OF_CHARACTERS){
std::cout<<"String greater than the maximum number of characters"<<std::endl;
}
// Copy the string. Add NULL character at the end of
// the string to indicate its end
strcpy(packet->data, "kappa\0");
// Verify that the string is copied properly
for (int i=0;i<packet->dataLength;++i){
std::cout<<packet->data[i];
}
std::cout<<std::endl;
return 0;
}
This code produces the correct output, and protects you against violations. As you can see, it can get messy pretty quickly, which is why I would recommend to use std::vector for this. The dataLength may then be retrieved automatically as the size of the vector, and you are always protected against overflows.

Function to copy data in a reference char array

How can I write in C++ a function who put the data in a char array, I don't how to explain well but I'll write an example
void functionCopyData(char& buf){
// get data from a char variable
char data1 = "textdata1";
char data2 = "textdata2"
// copy data
copy(&buf,data1);
copy(&buf,data2);
}
char variable[20];
functionCopyData(&variable);
printf(variable);
// return: textdata1textdata2
So summarizing, I need to copy the data from two variables and put in a char array. Sorry if I was not clear enough.
Why this way?
because I need to get the data from and a variable and send it in a function request a reference data sendFunction(&data,sizeVar);
how can I do that?
How can I write in C++ a function who put the data in a char array
Change the argument type.
void functionCopyData(char& buf){
will be good if you want to update just one character, not an array of characters. To update an array of characters, use:
void functionCopyData(char buf[]){
or
void functionCopyData(char* buf){
That could easily lead to problems if buf does not have enough memory to store the data. To make function more robust, pass a number that indicates the capacity of buf.
void functionCopyData(char buf[], size_t capacity){
Fix the definitions of data1 and data2.
char data1 = "textdata1";
char data2 = "textdata2"
are not syntactically valid. Change them to:
char data1[] = "textdata1";
char data2[] = "textdata2";
or
char const* data1 = "textdata1";
char const* data2 = "textdata2";
Make sure that buf can hold data1 and data2.
// You need the additional 1 for the terminating null
// character.
size_t capacityNeeded = strlen(data1) + strlen(data2) + 1;
if ( capacityNeeded > capacity )
{
// Deal with the problem.
}
else
{
// Now copy the contents of data1 and data2 to buf
}
Copy the contents data1 and data2 to buf using standard library functions.
strcpy(buf, data1);
strcat(buf, data2);
Fix the call to the function.
functionCopyData(&variable);
is not the right syntax to use to call the function, given the function declaration. Use:
functionCopyData(variable, 20);
An easy solution is to write a function to copy one array, and then call it twice with different pointers pointing to different positions in the result array.
For example, if you have char data[20] to hold the result, and want to copy two arrays char a[10] and char b[10], then write a function
void copy(char* destination, char* source, int length)
That copies length bytes from source to destination, then call it with
copy(data, a, 10);
copy(data+10, b, 10);
The functions strcpy and strlen can help you:
#include <string.h>
void functionCopyData(char buf[]){
char data1[] = "textdata1";
char data2[] = "textdata2";
strcpy(buf, data1);
strcpy(buf+strlen(buf), data2);
}
...
functionCopyData(variable);
...
void functionCopyData(char* buf){
// get data from a char variable
char* data1 = "textdata1";
char* data2 = "textdata2"
// copy data
copy(buf,data1);
copy(buf,data2);
}
char variable[20];
functionCopyData(variable);
printf(variable);
I think it's more clear use pointer but not reference.

c++: write a char at a given char* causes segfault

I want to copy a char to an address where a given char* points to.
it's in a function which is called by main:
char data = " ";
myfunction(data, somethingelse);
...
inside the function i have something like
void myfunction(char* data, short somethingelse) {
...
char byte = 0;
inputfilestream.read(&byte, 1);
*data = byte; // here i get the segfault
data++;
...
}
the segfault also comes when i to the copy using strncpy:
strncpy(data, byte, 1);
why is there a segfault? data isn't const and the address where i actually write to is exactly the same as the one where i allocated the data-array. i've tested that multiple times.
thanks in advance.
String literals are readonly. If you want a modifyable string, you must use an array, e.g.:
char data[10];
Or:
char *data = new char[10];
To elaborate a bit more: the type of a string literal is actually const char*. Assigning a string literal to a non-const char* is therefore technically invalid, but most compilers allow it anyway for legacy reasons. Many modern compilers will at least issue a warning when you try to do that.
data is assigned a string literal. String literals are ready only, and writing to them will cause segfaults.
Try this:
char data[10]; // or whatever size you want.
instead.
why is there a segfault? data isn't const and the address where i actually write to is exactly the same as the one where i allocated the data-array.
You didn't allocate anything. char *data = " "; shouldn't even compile in C++. You are assigning a constant string to a non-constant.
char byte = 0;
inputfilestream.read(&byte, 1);
*data = byte; // here i get the segfault
data++; // << How many times?
No problem
#include <stdio.h>
int main(int argc, char **argv)
{
char *data = "Yello"; // or char data[] = "Yello";
*data = 'H';
puts(data); // Hello
}