I have this union:
typedef union Message
{
message_base base;
message_with_parameters parameters;
reply_message reply;
buffer_t *buffer; // can't figure out what to put here
} message;
message_with_parameters has a message_base as the first field and reply_message has a message_with_parameters as as the first field which in turns has message_base as as the first field.
So basically I can access any of them and I'll still get all the data I need, however I am getting a buffer from my driver and now I want to serialize it into the message.
I already know that the pointer to the buffer is wrong as it won't correlate with my structs but I can't have a fixed size buffer.
Somewhere along the way I want to do this:
m->buffer = buff->payload;
And no matter what kind of data type I have, it will still serialize.
How can it be done?
EDIT:
Here are my structs:
typedef struct MessageBase
{
uint32_t u32DeviceID;
uint32_t u32CoreID;
uint16_t u16Class;
uint16_t u16CRC;
uint8_t u8OpCode;
void (*states [MAX_OPCODES]) (void *);
} message_base;
typedef struct MessageWithParameters
{
message_base base_class;
uint8_t u8Param1;
uint8_t u8Param2;
} message_with_parameters;
typedef message_with_parameters reply_message;
typedef union Message
{
message_base base;
message_with_parameters parameters;
reply_message reply;
} message;
its because the data in the buffer isn't part of the union.
buffer_t* buffer is a pointer, so the pointer is part of the union, not the data which it points at
you probablly want to do something like
m = (message*) buff->payload;
Related
I have the following struct definition:
typedef struct mb32_packet_t {
union {
struct {
uint16_t preamble;
uint8_t system_id;
uint8_t message_id;
uint8_t reserved;
uint32_t paylen;
};
uint8_t header[9];
};
uint8_t *payload;
uint16_t checksum;
} __attribute__((packed)) mb32_packet_t;
Now I would like to have another union, so that I can get an uint8_t body[] pointer to the entire packet object. Something like this:
typedef struct mb32_packet_t {
union {
struct {
union {
struct {
uint16_t preamble;
uint8_t system_id;
uint8_t message_id;
uint8_t reserved;
uint32_t paylen;
};
uint8_t header[9];
};
uint8_t *payload;
uint16_t checksum;
};
uint8_t body[?];
};
} __attribute__((packed)) mb32_packet_t;
The problem is that the payload field size is dynamically determined at runtime. Is there another way to accomplish this other than making payload fixed sized?
I basically want to send objects of this type through a network socket, so I need a uint8_t pointer that points to an object of this type. At the time of sending the object, I know the size of the entire object in bytes.
Introduction
The question is unclear, so I will discuss three apparent possibilities.
Fixed-length header followed by variable-length payload
A typical way to define a packet for a networking or messaging service is to have a fixed-length header followed by a variable-length payload. In modern C, the variable-length payload may be defined using a flexible array member, which is an array with no dimension at the end of a structure:
typedef struct
{
uint16_t preamble;
uint8_t system_id;
uint8_t message_id;
uint8_t reserved;
uint32_t paylen;
uint8_t payload[];
} mb32_packet_t;
Memory for such a structure is allocated use the base size provided by sizeof plus additional memory for the payload:
mb32_packet_t *MyPacket = malloc(sizeof *MyPacket + PayloadLength);
When you pass such an object to a routine that requires a char * or uint8_t * or similar type for its argument, you can simply convert the pointer:
SendMyMessage(…, (uint8_t *) MyPacket,…);
That cast, (uint8_t *) MyPacket, provides the pointer to the first byte of the packet requested in the question. There is no need to wedge another member into the structure or layer on a union or other declaration.
Prior to the introduction of flexible array members in C 1999, people would use one of two workarounds to create structures with variable amounts of data. One, they might just define a member array with one element and adjust the space calculations accordingly:
typedef struct
{
…
unsigned char payload[1];
} mb32_packet_t;
mb32_packet_t *MyPacket = malloc(sizeof *MyPacket + PayloadLength - 1);
Technically, that violated the C standard, since the structure contained an array of only one element even though more space was allocated for it. However, compilers were not as aggressive in their analysis of program semantics and their optimization as they are now, so it generally worked. So you may still see old code using that method.
Two, GCC had its own pre-standard implementation of flexible array members, just using an array dimension of zero instead of omitting a dimension:
typedef struct
{
…
unsigned char payload[0];
} mb32_packet_t;
Again, you may see old code using that, but new code should use the standard flexible array member.
Fixed-length header with pointer to variable-length payload
The payload-after-header form shown above is the form of packet I would most expect in a messaging packet, because it matches what the hardware has to put “on the wire” when sending bytes across a network: It writes the header bytes followed by the data bytes. So it is convenient to have them arranged that way in memory.
However, your code shows another option: The data is not in the packet but is pointed to by a pointer in the packet, with uint8_t *payload;. I would suspect that is a mistake, that the network or messaging service really wants a flexible array member, but you show it followed by another member, uint16_t checksum. A flexible array member must be the last member in a structure, so the fact that there is another member after the payload suggests this definition with a pointer may be correct for the messaging service you are working with.
However, if that is the case, it is not possible to get a pointer to the complete packet object, because the object is in two pieces. One contains the header, and the other, at some unrelated location in memory, contains the data.
As above, you can produce a uint8_t * pointer to the start of the packet with (uint8_t) MyPacket. If the messaging system knows about the pointer in the structure, that should work. If you have mistaken what the packet structure must be, it will fail.
Fixed-length header followed by fixed-length payload space
Code elsewhere on Stack Overflow shows a struct mb32_packet_t with a fixed amount of space for a payload:
typedef struct mb32_packet_t {
uint8_t compid;
uint8_t servid;
uint8_t payload[248];
uint8_t checksum;
} __attribute__((packed)) mb32_packet_s;
In this form, the packet is always a fixed size, although the amount of space used for the payload could vary. Again, you would obtain a uint8_t * pointer to the packet by a cast. There is no need for a special member for that.
This is possible, but not with a struct or union, because all parts of a struct or union need to have a known size. You can still use a struct for the header.
Because the body starts at a known location, there's a trick you can use to access it as if it was part of the structure. You can declare it with no size at all (a "flexible array member") or as 0 bytes (a GCC extension that predates the standard). The compiler will not allocate any space for it, but it will still let you use the name to refer to the end of the struct. The trick is that you can malloc extra bytes after the end of the struct, and then use body to refer to them.
typedef struct mb32_packet_t {
union {
struct {
uint16_t preamble;
uint8_t system_id;
uint8_t message_id;
uint8_t reserved;
uint32_t paylen;
};
uint8_t header[9];
};
uint8_t body[]; // flexible array member
} __attribute__((packed)) mb32_packet_t;
// This is not valid. The body is 0 bytes long, so the write is out of bounds.
mb32_packet_t my_packet;
my_packet.body[0] = 1;
// This is valid though!
mb32_packet_t *my_packet2 = malloc(sizeof(*my_packet2) + 50);
my_packet2->body[49] = 1;
// Alternative way to calculate size
mb32_packet_t *my_packet3 = malloc(offsetof(mb32_packet_t, body[50]));
my_packet3->body[49] = 1;
The flexible array member must be last. To access the checksum, you will need to allocate an extra 2 bytes, and use pointer arithmetic. Fortunately, this is just for the checksum, and not the entire header.
mb32_packet_t *my_packet = malloc(sizeof(*my_packet) + body_size + 2);
uint16_t *pchecksum = (uint16_t*)&my_packet.body[body_size];
// or
uint16_t *pchecksum = (uint16_t*)(my_packet.body + body_size);
After you fill in the header, body and checksum, then because they are contiguous in memory, a pointer to the header is also a pointer to the entire packet object.
I usually do it this way:
typedef struct
{
size_t payload_size;
double x;
char y[45];
/* another members */
unsigned char payload[];
}my_packet_t;
or if your compiler does not support FAMs
typedef struct
{
size_t payload_size;
double x;
char y[45];
/* another members */
unsigned char payload[0];
}my_packet_t;
So it the payload can be at the end of the header structure
I am currently working on my own octree in C. The tree will contain a few billion objects, so memory efficiency is key. To achieve this I currently use one struct with a flag and a union, but I think it is not clean and it is wasting space for the inner node because I only need an 8-bit flag but memory is being reserved for the 64-bit index. My code currently is as follows:
typedef struct _OctreeNode
{
uint64_t location_code;
union
{
uint8_t child_exists;
uint64_t object_index;
} data;
uint8_t type;
} OctreeNode;
I would like to split this up into two different structs. One leaf node and one inner node. As follows:
typedef struct _OctreeInnerNode
{
uint64_t location_code;
uint8_t child_exists;
uint8_t type;
} OctreeInnerNode;
typedef struct _OctreeLeafNode
{
uint64_t location_code;
uint64_t object_index;
uint8_t type;
} OctreeLeafNode;
Now the problem arises with my unordered map based on the hash of the location code. It uses a void pointer, so storing two different structs is not a problem. I know a possibility would be to have the flag be the first element and dereference the pointer to the flag datatype to derive the type, like so:
typedef struct _OctreeLeafNode
{
uint8_t type;
uint64_t location_code;
uint64_t object_index;
} OctreeLeafNode;
void
func(void* node)
{
uint8_t type = *(uint8_t*)node;
if (type == LEAF_NODE) {
OctreeLeafNode* leaf_node = (OctreeLeafNode*)node;
}
}
I was wondering if there is a cleaner way. Or is this not recommended? How would I be supposed to deal with multiple possibilities for structs and void pointers?
Thanks in advance!
This a method that is used commonly in C.
But just put these field at start of structure (first field) and never change their position. In addition, you need to keep them in all your structures.
A common sample for this approach is the version field in structures (or type in your case). You can keep them at start of structure, and then check structure version by similar method. something like this:
struct _base {
uint8_t ver;
};
#define TYPE_OLD 0
struct _a_old {
struct _base info;
uint8_t a;
};
#define TYPE_NEW 1
struct _a_new {
struct _base info;
uint8_t a;
uint8_t b;
};
Now you can identify different types by casting your data to struct _base and checking ver field.
unsigned char* buf = ...
switch (((struct _base*)buf)->ver)
{
case TYPE_OLD:
{
struct _a_old* old = (struct _a_old*)buf;
// ...
break;
}
case TYPE_NEW:
{
struct _a_new* old = (struct _a_new*)buf;
// ...
break;
}
default:
// ...
}
This will work, assuming the type field is first in each struct. A pointer to a struct may safely be converted to a pointer to its first member, so assuming your structs look like this:
typedef struct _OctreeInnerNode
{
uint8_t type; // type goes first
uint8_t child_exists; // put uint8_t members together to keep size down
uint64_t location_code;
} OctreeInnerNode;
typedef struct _OctreeLeafNode
{
uint8_t type; // type goes first
uint64_t object_index;
uint64_t location_code;
} OctreeLeafNode;
You can cast either a OctreeInnerNode * or a OctreeLeafNode * to a uint8_t *. Then this is possible:
void func(void* node) {
uint8_t type = *(uint8_t*)node;
if (type == LEAF_NODE) {
OctreeLeafNode *leafNode = node;
...
} else if (type == INNER_NODE) {
OctreeInnerNode *innerNode = node;
...
}
}
...
OctreeLeafNode leaf = { LEAF_NODE, 2, 3 };
OctreeInnerNode inner = { INNER_NODE, 5, 1 };
func(&leaf);
func(&inner);
This is guaranteed as per section 6.7.2.1p15 of the C standard:
Within a structure object, the non-bit-field members and the
units in which bit-fields reside have addresses that increase in
the order in which they are declared. A pointer to a structure
object, suitably converted, points to its initial member (or
if that member is a bit-field, then to the unit in which it
resides), and vice versa. There may be unnamed padding within
a structure object, but not at its beginning
I am trying to assign a string to a pointer which is in the structure. Even though, I am able to initialise the string to pointer successfully, when I try to use the string(access the pointer), I get some random values. How should I solve the problem. Following is my code:
typedef struct
{
uint8_t LOG_ID;
uint8_t timestamp;
uint8_t loglength;
uint8_t checksum;
uint8_t *payload;
} Log;
Log LB_t;
void main(){
LB_t.LOG_ID=1;
LB_t.timestamp=3;
LB_t.loglength=17;
LB_t.checksum=89;
LB_t.payload="LED initialised";
log_item(&LB_t,17);
}
void log_item(uint8_t *logptr,uint8_t length){
while(length!=0){
CB_buffer_add_item(tx,*logptr);
length=length-1;
logptr++;
}
}
Is there any alternate way in which I can access the pointer?
OK, I get it , you expect 'payload' to be inside the struct. Its not, just the pointer. You need to do this
typedef struct
{
uint8_t LOG_ID;
uint8_t timestamp;
uint8_t loglength;
uint8_t checksum;
uint8_t payload[100];
} Log;
and then
strncpy(LB_t.payload, "LED initialised", 100);
you probaly want to make the 100 a const or #define somehwere. And change yr 17 to sizeof(Log)
How can I have a variable point to a member of a different struct? This is what I'm trying to do, but the third line fails.
volatile uint8_t tx_message_buffer[sizeof(MESSAGE)];
struct MESSAGE *tx_message = (MESSAGE *)tx_message_buffer;
struct PAYLOAD *tx_payload = (PAYLOAD *)tx_message->payload;
Here are the struct definitions.
#define MSG_MAX_PAYLOAD_LENGTH 64
typedef struct PAYLOAD {
uint8_t descriptor;
uint8_t parameters[MSG_MAX_PAYLOAD_LENGTH-1];
};
typedef struct MESSAGE {
uint8_t address;
uint8_t length;
PAYLOAD payload;
uint8_t checksum;
};
This code has many problems.
As pointed out in other answers, you cannot set a pointer to point at a PAYLOAD payload; member, you need to point at its address, &tx_message->payload.
typedef struct PAYLOAD {} should be typedef struct {} PAYLOAD.
(MESSAGE *)tx_message_buffer is a completely wild cast, which invokes several cases of poorly defined behavior. First of all, you should never cast away volatile qualifiers. But also, as soon as you de-reference this struct you will violate strict aliasing and invoke undefined behavior. Anything can happen.
To solve these pointer bugs, you can do something similar to this:
typedef struct {
uint8_t address;
uint8_t length;
PAYLOAD payload;
uint8_t checksum;
} MESSAGE;
typedef union {
MESSAGE message;
uint8_t tx_message_buffer[sizeof(MESSAGE)];
} message_something;
This code is valid and well-defined.
Using a struct to represent a data protocol is bad practice, as you must ensure that the struct contains no padding at all. The memory layout in your MESSAGE struct is by no means guaranteed to correspond to the memory layout of the data protocol. The struct may have padding bytes to suit the alignment requirements of the specific CPU.
Disabling padding with non-standard C such as #pragma pack(1) may or may not be sufficient, depending on your portability requirements. To achieve full portability, you may have to write serialization/deserialization routines.
You have a bigger issue in your code: the cast on the second line is not valid, because the storage for struct MESSAGE may generally have different alignment requirements than char[] array. For example, changing the type of descriptor to uint32_t could force an even-address location for the entire struct on some platforms.
Doing it the other way around would be valid, through, because you are allowed to convert any object pointer to char *:
volatile struct MESSAGE tx_message;
volatile uint8_t *tx_message_buffer = (char*)tx_message;
The third line fails because you did not take a pointer of PAYLOAD struct:
struct PAYLOAD tx_payload = &tx_message.payload;
There is no need to cast the result, because tx_message.payload is already of the correct type.
By using,
PAYLOAD payload;
You are getting a variable not a pointer. Meaning that
message->payload;
Is not a pointer.
You need to use a pointer.
PAYLOAD * payload;
Or get the address of the struct
&message->payload;
Say we have an instance of
struct Message {
char * topic;
int topicLength;
void * data;
int dataLength;
};
and we want to recreate it as an object of other type
struct CoreMessage {
int messaageId;
char * topic;
int topicLength;
void * data;
int dataLength;
char * senderId;
int senderIdLength;
};
Can we safly turn Message A into CoreMessage B? thing in C without copying contents, having types partly overlaping as shown here?
You can fake this with anonymous structures/unions. Anonymous structures have admittedly only been standardized since C11 but many popular compilers have supported them as an extension for ages.
That is something along these, admittedly-less-than-pretty, lines:
struct Message {
char * topic;
int topicLength;
void * data;
int dataLength;
char * senderId;
int senderIdLength;
};
struct CoreMessage {
int messageId;
union {
struct Message;
struct Message message;
};
};
No, you cannot do what you ask. You could come close, however, if you were willing to change the layout of struct CoreMessage like so:
struct CoreMessage {
struct Message message;
int messaageId;
char * senderId;
int senderIdLength;
};
Note that struct CoreMessage then contains an actual struct Message as a member (as opposed to a pointer to one). Then, given ...
struct CoreMessage cm;
struct CoreMessage *cmp = &cm;
struct Message *mp = &cm.message;
... you have (void *) cmp == (void *) mp, which can be useful for some of the kinds of things you might want to do. This is also adjusts automatically to changes to struct Message.
Alternatively, you can do something like this:
struct Message {
char * topic;
int topicLength;
void * data;
int dataLength;
maximum_alignment_requirement_t resv1;
char resv2[AS_MANY_BYTES_AS_ANY_MESSAGE_TYPE_MAY_NEED_INCLUDING_PADDING];
};
struct CoreMessage {
char * topic;
int topicLength;
void * data;
int dataLength;
maximum_alignment_requirement_t resv1;
int messaageId;
char * senderId;
int senderIdLength;
};
struct Message msg;
struct CoreMessage *cmp = (struct CoreMessage *) &msg;
That has a high probability of working as you would hope (and some system interfaces work pretty much this way) but C does not guarantee that those corresponding elements will be laid out the same way in the two different struct types.
Note, too, that it was no accident that I moved CoreMessage.messageId after the members corresponding to those of struct Message. It is much harder to arrange for corresponding layout if you do not do this, and the pointer value equivalence of the first alternative depends on it.
Not sure what you mean by "safely turn", but I would expect the answer to be "no". The structures are different, of course the smaller one can't magically be expected to expand into memory it didn't previously use.
There is no concept of "overlapping types" in C.
You can of course declare CoreMessage in terms of Message, but it won't help for the reverse transform from the smaller to the larger type except by making the transfer of the shared information easier:
struct CoreMessage {
int messageId;
struct Message message;
char *senderId;
int senderIdLength;
};
Now if we have:
struct Message a = { ... }; /* fully initialized */
struct CoreMessage b; /* we want to convert Message into this */
we can do:
b.messageId = 4711;
b.message = a; /* Copy all Message data over. */
b.senderId = "foo";
b.senderIdLength = 3;
Nothing is automatic here though, you have to do it yourself.
I'm not sure exactly what you mean by "turn into". I'm not sure how you can get casting to do this for you.
But, the C standard allows the compiler to put unused space between fields of a struct so, in general nothing would really work. You could make "CoreMessage" contain a "Message" and produce your result with a single assignment.