I have the following packet layout:
struct PacketLayout
{
int GateCode;
BYTE StringLen;
char String[StringLen];
BYTE ServerStatus;
BYTE ServerStorage;
BYTE ServerNumber;
}
The class is this:
class ServerInfo
{
short PacketSize; //Size of the whole packet
BYTE TotalServers; //total of PacketLayout structs
PacketLayout Server[TotalServers];
int GlobalSecCode;
short EncryptedPacketSize; //Same as the first member, just xored
}
So the problem i have is making an variable size array inside an class or an struct which size depends of the last member pointed by BYTE StringLen (for struct) and BYTE TotalServers (for the class).
I don't know what is the solution to this, maybe implement a template?, if that's so can i see an example (i am not familiar with templates yet) also i want to reference my member names without calculating the pointer position by myself (as i am currently doing now).
Thanks.
Doing this with a template is possible, for example:
template <int StringSize>
struct PacketLayout
{
int GateCode;
BYTE StringLen;
char String[StringSize];
BYTE ServerStatus;
BYTE ServerStorage;
BYTE ServerNumber;
};
lets you use this as:
PacketLayout<100> pkt;
Normally what you would want to do is simpler though. For example if you reorder the packet and know the upper limit on size you can do simply:
struct PacketLayout
{
int GateCode;
BYTE StringLen;
BYTE ServerStatus;
BYTE ServerStorage;
BYTE ServerNumber;
char String[MAX_POSSIBLE_SIZE];
};
Or alternatively:
struct PacketLayout
{
int GateCode;
BYTE StringLen;
BYTE ServerStatus;
BYTE ServerStorage;
BYTE ServerNumber;
char *String;
};
and allocate/set String during reading.
Personally though I'd skip all this messy low level details and use something like protobuf to do the work for you and leave you free to concentrate on the more important higher level things that add value to your project.
There's a common but dirty trick used sometimes too:
struct PacketLayout
{
int GateCode;
BYTE StringLen;
BYTE ServerStatus;
BYTE ServerStorage;
BYTE ServerNumber;
char String[1];
};
Where people define the size of the variable part at the end to be 1 and then deliberately allocate more memory than needed for the struct so they can write past the end of it. This is evil and very much not recommended though.
Using templates is definitely the way to go:
template <size_t TotalServers>
class ServerInfo
{
PacketLayout Server[TotalServers];
int GlobalSecCode;
};
This has the disadvantage of not having one ServerInfo assignable to another, so possibly using a std::vector is important, if that means a lot to you.
There is no nice way in C++ to achieve this.
This is how you can create variable size array in PacketLayout:
struct PacketLayout
{
int GateCode;
BYTE StringLen;
BYTE ServerStatus;
BYTE ServerStorage;
BYTE ServerNumber;
char String[1];
}
then you allocate an instance:
PacketLayout* createPacketLayout(BYTE stringLen)
{
PacketLayout* packetLayout = (PacketLayout*)new char[sizeof(PacketLayout) - 1 + stringLen];
packetLayout->StringLen = stringLen;
return packetLayout;
}
in this case ServerInfo could hold array of pointers.
Related
I have a typedef struct with different data types in it. The number array has negative and non-negative values. How do I convert this struct in to a unint8t array in C++ on the Linux platform. Appreciate some help on this. Thank you. The reason I am trying to do the conversation is to send this uint8_t buffer as a parameter to a function.
typedef struct
{
int enable;
char name;
int numbers[5];
float counter;
};
appreciate any example on doing this. thank you
For a plain old data structure like the one you show this is trivial:
You know the size of the structure in bytes (from the sizeof operator).
That means you can create a vector of bytes of that size.
Once you have that vector you can copy the bytes of the structure object into the vector.
Now you have, essentially, an array of bytes representation of the structure object.
In code it would be something like this:
struct my_struct_type
{
int enable;
char name;
int numbers[5];
float counter;
};
my_struct_type my_struct_object = {
// TODO: Some valid initialization here...
};
// Depending on the compiler you're using, and its C++ standard used
// you might need to use `std::uint8_t` instead of `std::byte`
std::vector<std::byte> bytes(sizeof my_struct_object);
std::memcpy(bytes.data(), reinterpret_cast<void*>(&my_struct_object), sizeof my_struct_object);
Suggestion: use char*. For that type, C++ allows you to cast any pointer to it and use reinterpret_cast<char*>(&obj). (Assuming Obj obj;.)
If you need to use uint8_t* (and it's not a typedef to char*, you can allocate sufficient memory and do memcpy:
uint8_t buffer[sizeof(Obj)];
memcpy(buffer, &obj, sizeof(obj));
Struct A
{
uint16_t len;
uint8_t cnt;
uint8_t unit;
uint32_t seq;
};
This struct A is serialized into a char * buf. If I want to deserialize the individual values eg:
uint16_t len = 0;
memcpy(&len, buf, sizeof(len));
or I can just do
uint16_t len = (uint16_t) buf;
Which one is better or are both the same?
Also to deserialize the whole struct, if I just do
A tmp;
memcpy(&tmp, buf, sizeof(A));
Would this work fine or should I be worried about padding etc from the compiler?
When the data is copied into char[] buffer, it may not be properly aligned in memory for access as multi-byte types. Copying the data back into struct restores proper alignment.
If I want to deserialize the individual values eg:
uint16_t len = 0;
memcpy(&len, buf, sizeof(len));
Assuming that you have copied the struct into buf, this is perfectly valid, because the language guarantees that the initial member would be aligned with the beginning of the structure. However, casting buf to uint16_t* is invalid, because the buffer many not be properly aligned in memory to be addressed as uint16_t.
Note that getting elements of the struct other than the initial one require computing proper offset:
uint32_t seq;
memcpy(&seq, buf+offsetof(struct A, seq), sizeof(seq));
Also to deserialize the whole struct, if I just do
A tmp;
memcpy(&tmp, buf, sizeof(A));
Would this work fine or should I be worried about padding etc from the compiler?
This would work fine. Any padding embedded in the struct when you copied it into the buf would come back into tmp, along with the actual data.
I have a struct like the following
struct Struct {
int length; //dynamicTest length
unsigned int b: 1;
unsigned int a: 1;
unsigned int padding: 10;
int* dynamicTest;
int flag;
}
I want to copy this into a char array buffer (to send over a socket). I'm curious how I would do that.
To be precise, you do this with memcpy, e.g.:
#include <string.h>
/* ... */
Struct s = /*... */;
char buf[1024]
memcpy(buf, &s, sizeof(s));
/* now [buf, buf + sizeof(s)) holds the needed data */
Alternatively you can avoid copying at all and view an instance of struct as an array of char (since everything in computer memory is sequence of bytes, this approach works).
Struct s = /* ... */;
const char* buf = (char*)(&s);
/* now [buf, buf + sizeof(s)) holds the needed data */
If you are going to send it over the network, you need to care of byte order, int size and many other details.
Copying bit fields present no problem, but for dynamic fields, such as your char* this naive approach won't work. The more general solution, that works with any other types is serialization.
How would you represent byte array and its size nicely? I'd like to store (in main memory or within a file) raw byte arrays(unsigned chars) in which first 2/4 bytes will represents its size. But operations on such array does not look well:
void func(unsigned char *bytearray)
{
int size;
memcpy(&size, bytearray, sizeof(int));
//rest of operation when we know bytearray size
}
How can I avoid that? I think about a simple structure:
struct bytearray
{
int size;
unsigned char *data;
};
bytearray *b = reinterpret_cast<bytearray*>(new unsigned char[10]);
b->data = reinterpret_cast<unsigned char*>(&(b->size) + 1);
And I've got an access to a size and data part of bytearray. But it still looks ugly. Could you recommend an another approach?
Unless you have some overwhelming reason to do otherwise, just do the idiomatic thing and use std::vector<unsigned char>.
You're effectively re-inventing the "Pascal string". However
b->data = reinterpret_cast<unsigned char*>(&(b->size) + 1);
won't work at all, because the pointer points to itself, and the pointer will get overwritten.
You should be able to use an array with unspecified size for the last element of a structure:
struct bytearray
{
int size;
unsigned char data[];
};
bytearray *b = reinterpret_cast<bytearray*>(::operator new(sizeof (bytearray) + 10));
b->size = 10;
//...
::operator delete(b);
Unlike std::vector, this actually stores the size and data together, so you can, for example, write it to a file in one operation. And memory locality is better.
Still, the fact that std::vector is already tested and many useful algorithms are implemented for you makes it very attractive.
I would use std::vector<unsigned char> to manage the memory, and write a conversion function to create some iovec like structure for you at the time that you need such a thing.
iovec make_iovec (std::vector<unsigned char> &v) {
iovec iv = { &v[0], v.size() };
return iv;
}
Using iovec, if you need to write both the length and data in a single system call, you can use the writev call to accomplish it.
ssize_t write_vector(int fd, std::vector<unsigned char> &v) {
uint32_t len = htonl(v.size());
iovec iv[2] = { { &len, sizeof(uint32_t) }, make_iovec(v) };
return writev(fd, iv, 2);
}
Structs seem like a useful way to parse a binary blob of data (ie a file or network packet). This is fine and dandy until you have variable size arrays in the blob. For instance:
struct nodeheader{
int flags;
int data_size;
char data[];
};
This allows me to find the last data character:
nodeheader b;
cout << b.data[b.data_size-1];
Problem being, I want to have multiple variable length arrays:
struct nodeheader{
int friend_size;
int data_size;
char data[];
char friend[];
};
I'm not manually allocating these structures. I have a file like so:
char file_data[1024];
nodeheader* node = &(file_data[10]);
As I'm trying to parse a binary file (more specifically a class file). I've written an implementation in Java (which was my class assignment), no I'm doing a personal version in C++ and was hoping to get away without having to write 100 lines of code. Any ideas?
Thanks,
Stefan
You cannot have multiple variable sized arrays. How should the compiler at compile time know where friend[] is located? The location of friend depends on the size of data[] and the size of data is unknown at compile time.
This is a very dangerous construct, and I'd advise against it. You can only include a variable-length array in a struct when it is the LAST element, and when you do so, you have to make sure you allocate enough memory, e.g.:
nodeheader *nh = (nodeheader *)malloc(sizeof(nodeheader) + max_data_size);
What you want to do is just use regular dynamically allocated arrays:
struct nodeheader
{
char *data;
size_t data_size;
char *friend;
size_t friend_size;
};
nodeheader AllocNodeHeader(size_t data_size, size_t friend_size)
{
nodeheader nh;
nh.data = (char *)malloc(data_size); // check for NULL return
nh.data_size = data_size;
nh.friend = (char *)malloc(friend_size); // check for NULL return
nh.friend_size = friend_size;
return nh;
}
void FreeNodeHeader(nodeheader *nh)
{
free(nh->data);
nh->data = NULL;
free(nh->friend);
nh->friend = NULL;
}
You can't - at least not in the simple way that you're attempting. The unsized array at the end of a structure is basically an offset to the end of the structure, with no build-in way to find the end.
All the fields are converted to numeric offsets at compile time, so they need to be calculable at that time.
The answers so far are seriously over-complicating a simple problem. Mecki is right about why it can't be done the way you are trying to do it, however you can do it very similarly:
struct nodeheader
{
int friend_size;
int data_size;
};
struct nodefile
{
nodeheader *header;
char *data;
char *friend;
};
char file_data[1024];
// .. file in file_data ..
nodefile file;
file.header = (nodeheader *)&file_data[0];
file.data = (char *)&file.header[1];
file.friend = &file.data[file->header.data_size];
For what you are doing you need an encoder/decoder for the format. The decoder takes the raw data and fills out your structure (in your case allocating space for the copy of each section of the data), and the decoder writes raw binary.
(Was 'Use std::vector')
Edit:
On reading feedback, I suppose I should expand my answer. You can effectively fit two variable length arrays in your structure as follows, and the storage will be freed for you automatically when file_data goes out of scope:
struct nodeheader {
std::vector<unsigned char> data;
std::vector<unsigned char> friend_buf; // 'friend' is a keyword!
// etc...
};
nodeheader file_data;
Now file_data.data.size(), etc gives you the length and and &file_data.data[0] gives you a raw pointer to the data if you need it.
You'll have to fill file data from the file piecemeal - read the length of each buffer, call resize() on the destination vector, then read in the data. (There are ways to do this slightly more efficiently. In the context of disk file I/O, I'm assuming it doesn't matter).
Incidentally OP's technique is incorrect even for his 'fine and dandy' cases, e.g. with only one VLA at the end.
char file_data[1024];
nodeheader* node = &(file_data[10]);
There's no guarantee that file_data is properly aligned for the nodeheader type. Prefer to obtain file_data by malloc() - which guarantees to return a pointer aligned for any type - or else (better) declare the buffer to be of the correct type in the first place:
struct biggestnodeheader {
int flags;
int data_size;
char data[ENOUGH_SPACE_FOR_LARGEST_HEADER_I_EVER_NEED];
};
biggestnodeheader file_data;
// etc...