So I'm currently designing a simple transmission protocol for a ring network implemented in UART.
To transmit, I am converting the data from a struct to a char stream, started by < and terminated by > - I know of the possibility of any 0x3c or 0x3e value being mistaken for a < or > respectively, i am working on a solution to escape that. That's not part of my question.
So, the structure of it is something like <UINT32UINT32UINT8UINT8UINT16char[0...n]>, those types representing: <destinationId senderId TimeToLive Cmdtype CmdId Payloadlength Payload>. This always stays the same, so I can assume it without any delimiters between the values. This works, and I can theoretically also decode this. To easily access the bytes, I implemented the struct with unions:
typedef struct{
union{
uint32_t val;
char bytes[sizeof(uint32_t)];
} recipientId;
union{
uint32_t val;
char bytes[sizeof(uint32_t)];
} senderId;
union{
uint8_t val;
char bytes[sizeof(uint8_t)];
} timeToLive;
union{
uint8_t val;
char bytes[sizeof(uint8_t)];
} cmdType;
union{
uint8_t val;
char bytes[sizeof(uint8_t)];
} cmdId;
union{
uint16_t val;
char bytes[sizeof(uint16_t)];
} payloadLength;
char *payload;
char *commandRaw;
} aurPacket_t;
Once a packet exists, I decode it with something akin to this:
void decode_command(aurPacket_t packet){
if((packet.commandRaw[0] != '<' ) || (packet.commandRaw[strlen(packet.commandRaw) - 1] != '>') ){
printf("\n\nINVALID COMMAND\n\n");
}
else{
aurPacket_t newpacket;
// EITHER:
// for (int i = 0; i < strlen(newpacket.recipientId.bytes); i++){
// newpacket.recipientId.bytes[i] = (char)*(packet.commandRaw + 1 + i);
// }
// OR:
strncpy(newpacket.recipientId.bytes, (packet.commandRaw + 1), sizeof(newpacket.recipientId.bytes));
}
}
commandRaw contains the char stream that would be received in a message.
Using something like this I'd be able to do it, but I'd need to iterate it one by one since not all values are the same datatype - copying the string out to my payload in the end. Is there a way to make this more elegant than iterating through every single variable, to somehow iterate by using my struct as a guide for the iterator?
I was made aware of memcpy, but since I want to keep the protocol platform-independent, I'd prefer not to use it unless there is an elegant way to. Or is there a way to use it elegantly? Is my method of adding the variables even different from just using memcpy? Thinking about it, it doesn't seem like it would be, considering the ordering of the vars inside my struct. If I made a string containing <, memcpy-appended everything up to the payload, then memcpy-appended the payload, then appended a >, would there be any actual difference in the data? If not, I could just use that process in reverse to decode a message.
I'm currently encoding the message by using this function:
#define RAW_PACKET_LEN 1024
void parse_command(aurPacket_t packet){
snprintf(packet.commandRaw, RAW_PACKET_LEN, "<%.4s%.4s%.1s%.1s%.1s%.02s%.*s>",
packet.recipientId.bytes,
packet.senderId.bytes,
packet.timeToLive.bytes,
packet.cmdType.bytes,
packet.cmdId.bytes,
packet.payloadLength.bytes,
packet.payloadLength.val, packet.payload
);
} // memory for commandRaw allocated outside of function
which has the problem of not actually writing 0x00-bytes to the stream, but that's not part of the question - I'm looking for an answer to that at the moment (of course, if you know an easy fix, feel free to let me know :) )
The system is an ESP32 programmed using ESP-IDF.
Some tips here:
Your packet should not contain a pointer, it would point to an address that only the sender would know about. You want to actually copy the array into the command (see 4 below).
“char bytes[sizeof(uint8_t)]” is literally preprocessed at compile time into “char bytes[1]”, so you might as well just have “char byte”, which is singular.
If you are just using a union for a uint8_t and a char, don’t bother just cast it before you use it: (eg; printf(“%c”,(char)val);
You will save yourself a ton of headache if you can just agree on fixed packet size. It looks like the only thing that will be dynamic is the payload, and the commandraw. Pick your worst case and go with that. I know that you are using serial, but unless you are doing something other than 8N1 it won’t have checks anyway, so you don’t want anything too long. I suggest you pick a total length under 1472 incase you move to UDP/TCP one day.
Assuming you can do these things, copying the data out is a piece of cake. You make your command as a struct. Then you make a union which contains the command as the first member and an array the size of the command as the second member. For example I would used uint8s (you could use chars).
union CommandMsg{
//you could also define this ahead of time..inlining b/c lazy.
struct Command{
uint32_t recipientId;
uint32_t senderId;
uint8_t val;
char timeToLive; //why char?
uint8_t cmdType;
uint8_t cmdId;
uint16_t val;
uint16_t payloadLength; //maybe just strlen later if term'd?
char payload[256];
char commandRaw[256];
} asCommand;
uint8_t asByteArray[sizeof(Command)];
} commandMsg;
Safety first, before you stuff your command struct memcpy zeros into the whole thing. The zeros will act like terminators for any strings you memcpy later.
Make sure to __pack your struct
When you copy it back out cast your arrays to void pointer memcpy((void *)localCopy, (const void *)incoming, sizeof(CommandMsg));
Not really sure what counts as "elegant", but here are some diverse solutions.
The old-fashioned C way to deal with something like this would be to create a look-up table. Given that you only have a static "singleton" buffer static aurPacket_t aur_packet;, then you can create a pointer look-up table at compile-time:
#define AUR_PACKET_MEMBERS_N 6
static char* const AUR_PACKET_LOOKUP[AUR_PACKET_MEMBERS_N] =
{
aur_packet.recipientId.bytes,
aur_packet.senderId.bytes,
aur_packet.timeToLive.bytes,
aur_packet.cmdType.bytes,
aur_packet.cmdId.bytes,
aur_packet.payloadLength.bytes,
};
And now you can iterate over each part of the struct by accessing AUR_PACKET_LOOKUP[i] and get a char* to its bytes member.
A more radical (and not necessarily readable) approach would be "X macros", creating something like
#define AUR_PACKET_LIST \
X(UINT32, recipientId) \
X(UINT32, senderId) \
X(UINT8, timeToLive) \
X(UINT8, cmdType) \
X(UINT8, cmdId) \
X(UINT16, payloadLength) \
You can now generate the string at compile-time, without involving sprintf functions:
strcpy(packet.commandRaw,
#define X(type, member) #type
AUR_PACKET_LIST
#undef X
);
Which expands to strcpy(packet.commandRaw, "UINT32" "UINT32" ... and the pre-processor concatenates the string literals from there.
And finally, it's perfectly possible to go completely macro ape and use X macros to define the type itself too, which I don't really recommend unless you have severe requirements to avoid code repetition:
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef uint8_t UINT8;
typedef uint16_t UINT16;
typedef uint32_t UINT32;
#define AUR_PACKET_LIST(X) \
/* type name */ \
X(UINT32, recipientId) \
X(UINT32, senderId) \
X(UINT8, timeToLive) \
X(UINT8, cmdType) \
X(UINT8, cmdId) \
X(UINT16, payloadLength) \
#define AUR_PACKET_DECL_MEMBER(type, name) \
union { \
type val; \
char bytes[sizeof(type)]; \
} name;
typedef struct{
AUR_PACKET_LIST(AUR_PACKET_DECL_MEMBER)
char *payload;
char *commandRaw;
} aurPacket_t;
int main(void)
{
aurPacket_t packet = {.commandRaw=malloc(256)};
packet.commandRaw[0]='\0';
#define AUR_PACKET_COMMANDRAW(type, name) #type
strcpy(packet.commandRaw, AUR_PACKET_LIST(AUR_PACKET_COMMANDRAW));
puts(packet.commandRaw);
return 0;
}
Your struct looks redundant. It's better to use something like this:
typedef struct {
uint8_t head;
uint32_t recipientId;
uint32_t senderId;
uint8_t timeToLive;
uint8_t cmdType;
uint8_t cmdId;
uint16_t payloadLength;
uint8_t *payload;
uint8_t tail;
} aurPacket_t;
Anyway you should deal with buffer to have memory for payload. It can be global array with fixed size, it's better for embedded software. memcpy is good solution when you controls all the sizes you operates with, because it's usually optimized for target hardware. You should use only fixed size types, like uint8_t and uint32_ for your data members in your packet to make it cross-platform and remember about big- or little-endian on your machines, when you storing data to packet.
Also it's good practice to make check sum a part of your packet to validate data.
Related
So I have some structs containing data that I want to send to another process using a unix socket. This process may not be compiled using the same compiler version, or even be written in C for that matter. This is the struct (note that some stuff is commented out):
struct nested_struct {
uint8_t a;
uint8_t b;
uint16_t c;
} */__attribute__((packed))*/;
struct my_struct {
uint32_t num_nested_structs;
/* uint8_t padding[3];*/
uint8_t x;
uint16_t y;
uint16_t z;
struct nested_struct nested[];
} /*__attribute__((packed))*/;
For convenience and performance, I'd like to get away with something like
write(socket, &data.x, data.num_nested_structs * sizeof(struct nested_struct) + 5)
or something -- but I doubt this would be safe, given that struct my_struct is not nicely aligned. But how about if we un-comment the packed attribute? This feels like it should work, but I've read that referencing fields in __packed__ structs by address can be dangerous.
What if we instead uncomment the uint8_t padding[3]; field? Now both structs are word size-aligned (on a system with WORD_BIT = 32). Is it safe to assume that the compiler won't add any padding in this case? If so, is this enough to ensure that accessing 5 + 4*num_nested_structs bytes of memory starting from &my_struct.x is safe?
I have a little data-hiding module that looks like this:
/** mydata.h */
struct _mystruct_t;
typedef struct _mystruct_t mystruct;
mystruct *newMystruct();
void freeMystruct( mystruct** p );
/** mydata.c */
#include "mydata.h"
struct _mystruct_t {
int64_t data1;
int16_t data2;
int16_t data3;
};
// ... related definitions ... //
For the most part, this is what I want; although simple, the struct has strict consistency requirements and I really don't want to provide access to the data members.
The problem is that in client code I would like to include the struct in another struct which I would like to allocate on the stack. Right now I am jumping through hoops to free the mystruct*s in some client code. Since a) mystruct is pretty small and I really don't think it's going to get big anytime soon and b) it's not a problem that client code has to recompile if I ever change mystruct, I would like to make the size of mystruct public (i.e. in the header).
Two possibilities I've considered:
/** mydata.h */
typedef struct {
// SERIOUSLY DON'T ACCESS THESE MEMBERS
int64_t data1;
int16_t data2;
int16_t data3;
} mystruct;
I think the drawbacks here speak for themselves.
OR
/** mydata.h */
#define SIZEOF_MYSTRUCT (sizeof(int64_t)+sizeof(int16_t)+sizeof(int16_t))
// everything else same as before...
/** mydata.c */
// same as before...
_Static_assert (SIZEOF_MYSTRUCT == sizeof(mystruct), "SIZEOF_MYSTRUCT is incorrect")
Of course this seems non-ideal since I have to update this value manually and I don't know if/how alignment of the struct could actually cause this to be incorrect (I thought of the static assert while writing this question, it partially addresses this concern).
Is one of these preferred? Or even better, is there some clever trick to provide the actual struct definition in the header while later somehow hiding the ability to access the members?
You can create different .h file distributed to the end user that would define your secret structure just as byte array (you can't hide data without crypto/checksumming more than just saying "here are some bytes"):
typedef struct {
unsigned char data[12];
} your_struct;
You just have to make sure that both structures are the same for all the compilers and options, thus using __declspec(align()) (for VC) in your library code, so for example:
// Client side
__declspec(align(32)) typedef struct {
int64_t data1;
int16_t data2;
int16_t data3;
} mystruct;
To prevent structure from being 16B long instead of commonly expected 12B. Or just use /Zp compiler option.
I would stay with a configure time generated #define describing the size of the mystruct and possibly a typedef char[SIZEOF_MYSTRUCT] opaque_mystruct to simplify creation of placeholders for mystruct.
Likely the idea of configure time actions deserves some explanations. The general idea is to
place the definition of the mystruct into a private, non-exported but nevertheless distributed header,
create a small test application being built and executed before the library. The test application would #include the private header, and print actual sizeof (mystruct) for a given compiler and compile options
create an appropriate script which would create a library config.h with #define SIZEOF_MYSTRUCT <calculated_number> and possibly definition of opaque_mystruct.
It's convenient to automate these steps with a decent build system, for examplecmake, gnu autotools or any other with support of configure stage. Actually all mentioned systems have built-in facilities which simplify the whole task to invocation of few predefined macros.
I've been researching and thinking and took one of my potential answers and took it to the next level; I think it addresses all of my concerns. Please critique.
/** in mydata.h */
typedef const struct { const char data[12]; } mystruct;
mystruct createMystruct();
int16_t exampleMystructGetter( mystruct *p );
// other func decls operating on mystruct ...
/** in mydata.c */
typedef union {
mystruct public_block;
struct mystruct_data_s {
int64_t d1;
int16_t d2
int16_t d3;
} data;
} mystruct_data;
// Optionally use '==' instead of '<=' to force minimal space usage
_Static_assert (sizeof(struct mystruct_data_s) <= sizeof(mystruct), "mystruct not big enough");
mystruct createMystruct(){
static mystruct_data mystruct_blank = { .data = { .d1 = 1, .d2 = 2, .d3 = 3 } };
return mystruct_blank.public_block;
}
int16_t exampleMystructGetter(mystruct *p) {
mystruct_data *a = (mystruct_data*)p;
return a->data.d2;
}
Under gcc 4.7.3 this compiles without warnings. A simple test program to create and access via the getter also compiles and works as expected.
(btw I'm not allowed to malloc in this, I'm writing in c for c99)
I'm trying to create a wrapper struct in c for arrays to keep things tidy so I don't need to keep passing the array pointer and the length around, and can just use a struct:
#define MAX_LEN 64
typedef struct {
uint8_t data[MAX_LEN];
size_t len;
} byteArray_t;
Which is fine if MAX_LEN is known, but is there a way to make the length variable although known at compile time, so that for example I could have something like:
typedef struct {
byteArray_t tag;
byteArray_t length;
byteArray_t value;
} tlv_t;
In which the array corresponding to tag would have size MAX_TAG_LEN and so on for the others - so I'd need to tell the compiler somehow that that was the case...
Anyone got any ideas? Thanks!
Edit
Alright, sorry for the confusion. Here's what I'm trying to do.
I basically have the following structures at present:
// tag object
typedef struct {
uint8_t data[MAX_TAG_LENGTH_IN_BYTES];
uint32_t len;
} tlvTag_t;
// length object
typedef struct {
uint8_t data[MAX_LENGTH_OF_LENGTH_IN_BYTES];
uint32_t len;
} tlvLength_t;
typedef struct tlv tlv_t;
// value object definition
typedef struct {
// can consist of a byte array, or a list of sub TLVs
union {
uint8_t data[MAX_VALUE_LENGTH_IN_BYTES];
// data can be parsed into subTLVs
tlv_t* subTLVs;
};
// need to store the number of subTLVs
uint32_t numberOfSubTLVs;
// len is the total length of the data represented:
// the total length of the subTLVs placed end to end
// or the length of the data array.
uint32_t len;
} tlvValue_t;
// tlv object definition
struct tlv {
tlvTag_t tag;
tlvLength_t len;
tlvValue_t value;
// total length of entire tlv block (not value length)
// (if there are sub TLVs, place them end to end)
uint32_t totalLen;
};
I thought the design would be better if I could wrap the arrays in another struct to avoid all the code duplication and be able to pass fewer arguments around, but I can't because I don't know how to tell the compiler to create different sized byte arrays - maybe it's possible using macros? Hope that makes sense.
It seems you are trying to somehow declare a struct whose contents depend on a parameter. In c++ this could be implemented by a template:
template <size_t MAX_LEN>
struct byteArray_t
{
uint8_t data[MAX_LEN];
size_t len;
};
...
byteArray_t<MAX_TAG_LENGTH_IN_BYTES> tag;
byteArray_t<MAX_LENGTH_OF_LENGTH_IN_BYTES> len;
...
This is as straightforward as it can get.
To accomplish the same in C, you can use macros:
#define DECLARE_BYTE_ARRAY_T(your_type_name, MAX_LEN) \
typedef struct { \
uint8_t data[MAX_LEN]; \
size_t len; \
} your_type_name
DECLARE_BYTE_ARRAY_T(tlvTag_t, MAX_TAG_LENGTH_IN_BYTES);
DECLARE_BYTE_ARRAY_T(tlvLenght_t, MAX_LENGTH_OF_LENGTH_IN_BYTES);
...
tlvTag_t tag;
tlvLength_t len;
Or (the same) without declaring types (good if you don't need names for your structs):
#define BYTE_ARRAY_T(MAX_LEN) \
struct { \
uint8_t data[MAX_LEN]; \
size_t len; \
}
BYTE_ARRAY_T(MAX_TAG_LENGTH_IN_BYTES) tag;
BYTE_ARRAY_T(MAX_LENGTH_OF_LENGTH_IN_BYTES) len;
This may be marginally better than the code you already have. However, in my opinion, this is not worth the effort, because any non-trivial macro decreases readability of code.
If you make a struct like this and then pass it by value to a function, then the whole array gets passed by value. You do not want that.
Actually you don't need an array inside the struct, just declare it elsewhere.
typedef struct {
uint8_t* data;
size_t len;
} byteArray_t;
int main()
{
uint8_t some_array[X];
...
byteArray_t wrapper = {some_array, X};
some_function (&wrapper);
}
In the embedded world we often have data structures that are passed around via fixed-length buffers. These are relatively easy to handle using something like this:
#define TOTAL_BUFFER_LENGTH 4096
struct overlay {
uint16_t field1;
uint16_t field2;
uint8_t array1[ARY1_LEN];
};
static_assert(sizeof(struct overlay) <= TOTAL_BUFFER_LENGTH);
struct overlay* overlay = malloc(TOTAL_BUFFER_LENGTH);
That is, we use a data structure as an overlay to allow easy access to the part of the buffer that is currently being used.
We have a number of buffer formats, however, that also use the last few bytes of the buffer to store things like checksums. We currently use constructions like this:
struct overlay {
uint16_t field1;
uint16_t field2;
uint8_t array1[ARY1_LEN];
char reserved[TOTAL_BUFFER_LENGTH -
sizeof(uint16_t) - sizeof(uint16_t) -
(sizeof(uint8_t) * ARY1_LEN) -
sizeof(uint32_t)];
uint32_t crc;
};
As ugly as this looks for this simple data structure, it's an absolute monstrosity when the structure grows to have dozens of fields. It's also a maintainability nightmare, as adding or removing a structure field means that the size calculation for reserved must be updated at the same time.
When the end of the structure only contains one item (like a checksum), then we sometimes use a helper function for reading/writing the value. That keeps the data structure clean and maintainable, but it doesn't scale well when the end of the buffer has multiple fields.
It would help greatly if we could do something like this instead:
struct overlay {
uint16_t field1;
uint16_t field2;
uint8_t array1[ARY1_LEN];
char reserved[TOTAL_BUFFER_LENGTH -
offsetof(struct overlay, reserved) -
sizeof(uint32_t)];
uint32_t crc;
};
Unfortunately, offsetof only works on complete object types and since this is in the middle of the definition of struct overlay, that type isn't yet complete.
Is there a cleaner, more maintainable way to do this sort of thing? I essentially need a fixed-length structure with fields at the beginning and at the end, with the remaining space in the middle reserved/unused.
In your situation, I think I'd probably do things this way:
typedef struct overlay_head
{
uint16_t field1;
uint16_t field2;
uint8_t array1[ARY1_LEN];
} overlay_head;
typedef struct overlay_tail
{
uint32_t crc;
} overlay_tail;
enum { OVERLAY_RSVD = TOTAL_BUFFER_LENGTH - sizeof(overlay_head)
- sizeof(overlay_tail) };
typedef struct overlay
{
overlay_head h;
uint8_t reserved[OVERLAY_RSVD];
overlay_tail t;
} overlay;
You can then work almost as before, except that where you used to write p->field1
you now write p->h.field1, and where you used to write p->crc you now write p->t.crc.
Note that this handles arbitrarily large tail structures quite effectively, as long as the head and tail both fit inside the overall size.
You could define a structure that simply has the buffer with a CRC field at the end:
struct checked_buffer {
char data[TOTAL_BUFFER_LENGTH - sizeof(uint32_t)];
uint32_t crc;
};
and then place your "overlays" on its data field. You're presumably already casting pointers to "convert" a raw buffer's char* into an overlay*, so it shouldn't be a big deal to cast from overlay* to checked_buffer* when you want to access the CRC field.
But if you want to have a field in a consistent position across a bunch of structures, it'd be easier to put it at the beginning of each structure. That way you can declare it directly in each structure without needing to do anything strange, and you don't need any pointer casts to access it.
How about that?
union a256
{
struct
{
int field_a;
int field_b;
char name[16];
//
int crcshadow;
};
struct
{
char buff[256-sizeof(int)];
int crc;
};
} ;
static_assert(offsetof(a256, crcshadow) < offsetof(a256, crc), "data too big");
The first struct contains data, the second define fixed size for this union.
edit: a better way of phrasing this: What's the correct [modern] way to ensure that a struct is a specific size in bytes?
just spending a relaxing saturday afternoon debugging a legacy codebase, and having a bit of trouble figuring this out. The compiler error I get is this:
INC/flx.h:33: error: dereferencing pointer to incomplete type
the code at line 33 looks like this
typedef struct flx_head {
FHEAD_COMMON;
LONG frames_in_table; /* size of index */
LONG index_oset; /* offset to index */
LONG path_oset; /* offset to flipath record chunk */
/* this will insure that a Flx_head is the same size as a fli_head but won't
* work if there is < 2 bytes left (value <= 0) */
PADTO(sizeof(Fli_head),flx_head,flxpad); /* line 33 is this one */
} Flx_head;
well okay so I can see that the struct is referring to itself to pad it out somehow. But I don't know an alternative way of doing what PADTO does without the self reference.
here's what PADTO is defined as
#define MEMBER(struc,field) \
((struc*)NULL)->field
/* returns offset of field within a given struct name,
* and field name ie: OFFSET(struct sname,fieldname) */
#define OFFSET(struc,field) \
(USHORT)((ULONG)((PTR)&MEMBER(struc,field)-(PTR)NULL))
/* offset to first byte after a field */
#define POSTOSET(struc,field) \
(OFFSET(struc,field)+sizeof(MEMBER(struc,field)))
/* macro for defining pad sizes in structures can not define a pad of
* less than two bytes one may use pname for the offset to it but
* sizeof(struc->pname) will not be valid
*
* struct sname {
* char fld1[64];
* PADTO(68,sname,pname);
* };
* will make:
*
* struct sname {
* char fld1[64];
* UBYTE pname[1];
* UBYTE __pname[3];
* };
*/
#define PADTO(sz,struc,padfld) \
UBYTE padfld[1];UBYTE __##padfld[(sz)-OFFSET(struct struc,padfld)-1]
here is FHEAD_COMMON
#define FHEAD_COMMON \
CHUNKID_FIELDS;\
USHORT frame_count;\
USHORT width;\
USHORT height;\
USHORT bits_a_pixel;\
SHORT flags;\
LONG speed;\
USHORT unused;\
Fli_id id;\
USHORT aspect_dx;\
USHORT aspect_dy;\
UBYTE commonpad[38] /* should be total of 80 bytes (48 for unique) */
and flihead
typedef struct fli_head {
FHEAD_COMMON;
LONG frame1_oset;
LONG frame2_oset;
UBYTE padfill[40];
} Fli_head;
this is Autodesk animator pro. what I am working on is the "reference" implementation for the FLI file format- which you can see a spec for here:
http://www.compuphase.com/flic.htm
Incidentally, I'm pretty sure that what the /source code/ there refers to as "flx" is actually what that webpage there calls "flc" , not what it calls "flx"
update:
better source for format info http://drdobbs.com/architecture-and-design/184408954
It isn't pretty, but one possibility is to define another identical structure and use its size to determine the padding for the one you actually want to use:
#define FLX_HEAD \
FHEAD_COMMON;\
LONG frames_in_table; /* size of index */ \
LONG index_oset; /* offset to index */ \
LONG path_oset /* offset to flipath record chunk */
struct flx_head_unpadded {
FLX_HEAD;
};
typedef struct flx_head {
FLX_HEAD;
char __flxpad[sizeof(Fli_head)-sizeof(struct flx_head_unpadded)];
} Flx_head;
I suppose the answer depends on what you're trying to achieve. In most cases, the correct, modern way to pad a struct is not to. The only situation I can think of where it's legitimate to pad a struct is when you have a library interface where the caller creates objects of a structure type and passes pointers to them to the library, and where you want to leave room to add additional fields to the structure without breaking the ABI. In this case, I would start out with something like char pad[256]; and change it to char pad[256-3*sizeof(long)]; or similar as you add fields (making sure to avoid internal padding when you add fields).
Define it in a union with a byte/char array of the desired size?
I can think quickly of some scenarios where this is needed:
1) Compatibility with old software that uses flat binary files to store data, (as in OP).
2) Interaction with drivers and/or hardware
3) Forcing structs to be an exact multiple of the cache line size to prevent false sharing in inter-thread comms.
If you only want to achieve specific size you can use (for sure working in GCC):
typedef union {
struct {
FHEAD_COMMON;
LONG frames_in_table; /* size of index */
LONG index_oset; /* offset to index */
LONG path_oset; /* offset to flipath record chunk */
};
uint8_t __padding[128];
} Flx_head;
void test() {
Flx_head boo;
boo.frames_in_table= 0;
}
I am not sure if this is modern enough. If youc compiler does not support anonymous struct (the one inside union) it will get "messy".
Also you must remember that struct is now padded, but not aligned to specific data size.
thanks everyone. not sure who to award the green checkmark to, since I found this solution as a result of everyone kind of hinting and pointing in the right direction. After looking at the problem, it struck me that the struct just needs to be exactly 128 bytes. I "hand parsed" the macro, cross referencing with the spec and ended up with this:
typedef struct flx_head {
FHEAD_COMMON;
LONG frames_in_table; /* size of index */
LONG index_oset; /* offset to index */
LONG path_oset; /* offset to flipath record chunk */
UBYTE flxpad[36];
} Flx_head;
which is 128-(80+4+4+4) = 36