0

I have a C struct which contains data that needs to be sent via a USB protocol as a message made of separated bytes. I am trying to understand what is the standard way to do this. The current method I use seems convoluted especially for big structs..

typedef struct {
float Results;
int settings;
} Test_t;

Test_t T;

int8_t GetTestData(uint8_t * outputBytes) {
      //ptr to the output
      uint8_t *ptr = outputBytes;
      memcpy(ptr, T.Results, sizeof(float)); // would do this for every member
}

Is there a way to send all of the struct at once to the pointer? I didn't want to screw something up, but could I just do

memcpy(ptr, T, sizeof(test_t));
JCSB
  • 345
  • 2
  • 16
  • 1
    You migh read a bit about serialization. If your sender and receiver are always guaranteed to have same CPU architecture (sizes of datatypes, alignment restrictions, padding mechanism, ...) then you could just copy the whole struct at once. At least unless there are any pointers inside the struct. In any other case you need to handle each member serparately and then simply copying it will not be sufficient. – Gerhardh Mar 29 '21 at 17:53
  • Related: https://stackoverflow.com/questions/30945121/dealing-with-data-serialization-without-violating-the-strict-aliasing-rule – Eugene Sh. Mar 29 '21 at 18:04

1 Answers1

0

what is the standard way to do this

There are as many standard as stars in the sky. A "standard" is just what you make up, it has to be enough so that data are understood.

The "standard" (as in exists in C standard) is to printf/scanf utilities. And this type of serialization is clean clear, easy to debug, human readable, just has a very big overhead.

int test_serialize(const test_t *t, char **buf) {
      return asprintf(buf, "%g %d", t->results, t->settings);
}

int test_deserialize(test_t *t, const char *buf) {
      return sscanf(buf, "%g %d", &->results, &t->settings) == 2 ? 0 : 1;
}

If the structure is meant to be sent and received within the same machine (the same ABI/endianess/alignment/compiler), you could just:

typedef struct {
   float Results;
   int settings;
} test_t;

int test_serialize(const test_t *t, char **buf) {
    *buf = malloc(sizeof(*t));
    if (*buf == NULL) return -1;
    memcpy(*buf, t, sizeof(*t));    
    return sizeof(*t);
}

int test_deserialize(const test_t t, const char *buf) {
    memcpy(t, buf, sizeof(*t)); // TADA!
    return 0;
}

With special considerations, you can also handle alignment and all the stuff within your code, for example by sending each element one after another in network byte order as you suggested. And making sure all types are in standard formats - int is required to be for example a twos-complement 4-byte signed integer and float is required to be a IEE-754 single precision 32-bit binary floating point number. By properly documenting what you send you are... creating yet another standard. See for example UBX protocol (page 168) that is a (great) binary protocol specification - see how they handled all the issues and standarized possible binary formats.

Popular human-readable easy-to-debug formats nowadays are json and yaml. There are some good json libraries in C. Csv - comma separated values - will never die, it's simple to work with. Which format to use depends on your requirements - how easy to use you want it to be, what do you want to use, is packet size an issue, etc.

KamilCuk
  • 120,984
  • 8
  • 59
  • 111