Converting struct to byte and back to struct
Asked Answered
E

4

25

I'm currently working with Arduino Unos, 9DOFs, and XBees, and I was trying to create a struct that could be sent over serial, byte by byte, and then re-constructed into a struct.

So far I have the following code:

struct AMG_ANGLES {
    float yaw;
    float pitch;
    float roll;
};

int main() {
    AMG_ANGLES struct_data;

    struct_data.yaw = 87.96;
    struct_data.pitch = -114.58;
    struct_data.roll = 100.50;

    char* data = new char[sizeof(struct_data)];

    for(unsigned int i = 0; i<sizeof(struct_data); i++){
        // cout << (char*)(&struct_data+i) << endl;
        data[i] = (char*)(&struct_data+i); //Store the bytes of the struct to an array.
    }

    AMG_ANGLES* tmp = (AMG_ANGLES*)data; //Re-make the struct
    cout << tmp.yaw; //Display the yaw to see if it's correct.
}

Source: http://codepad.org/xMgxGY9Q

This code doesn't seem to work, and I'm not sure what I'm doing wrong.

How do I solve this?

Entice answered 8/12, 2012 at 8:43 Comment(2)
a plead to all c++ programmers - use c++ style casts instead of c-style casts.Fang
Didn't realize they were c-style casts as this is what my teachers taught my in my C++ classEntice
E
40

It seems I've solved my issue with the following code.

struct AMG_ANGLES {
    float yaw;
    float pitch;
    float roll;
};

int main() {
    AMG_ANGLES struct_data;

    struct_data.yaw = 87.96;
    struct_data.pitch = -114.58;
    struct_data.roll = 100.50;

    //Sending Side
    char b[sizeof(struct_data)];
    memcpy(b, &struct_data, sizeof(struct_data));

    //Receiving Side
    AMG_ANGLES tmp; //Re-make the struct
    memcpy(&tmp, b, sizeof(tmp));
    cout << tmp.yaw; //Display the yaw to see if it's correct
}

WARNING: This code will only work if sending and receiving are using the same endian architecture.

Entice answered 8/12, 2012 at 8:56 Comment(2)
An addition to your warning, both sides need to represent a float identically as well. I'd recommend sending it as fixed point integer (in your example above: 8796, -11458, 10050) and document the endianness of the values. Use functions like htobe32() from endian.h to convert between host and big- or little-endian.Safier
@Entice Couldn't the struct have padding? This would not tightly pack the structure.Garvy
C
7

You do things in the wrong order, the expression

&struct_data+i

takes the address of struct_data and increases it by i times the size of the structure.

Try this instead:

*((char *) &struct_data + i)

This converts the address of struct_data to a char * and then adds the index, and then uses the dereference operator (unary *) to get the "char" at that address.

Circumpolar answered 8/12, 2012 at 8:48 Comment(3)
It doesn't seem to let me call it. I get "Line 23: error: request for member 'yaw' in 'tmp', which is of non-class type 'AMG_ANGLES*'"Entice
See my answer. You have to use '->'Felicidadfelicie
This has helped a lot right now, 6 years later. Thank you kind sir!Chavers
R
4

Always utilize data structures to its fullest..

union AMG_ANGLES {
  struct {
    float yaw;
    float pitch;
    float roll;
  }data;
  char  size8[3*8];
  int   size32[3*4];
  float size64[3*1];
};
Raila answered 9/5, 2016 at 20:1 Comment(2)
Why is float 8 bytes long?Alehouse
it's not. float is 32bit. But there is double, which is basically just a 64bit float. Also there is long double.Alexis
F
1
for(unsigned int i = 0; i<sizeof(struct_data); i++){
    // +i has to be outside of the parentheses in order to increment the address
    // by the size of a char. Otherwise you would increment by the size of
    // struct_data. You also have to dereference the whole thing, or you will
    // assign an address to data[i]
    data[i] = *((char*)(&struct_data) + i); 
}

AMG_ANGLES* tmp = (AMG_ANGLES*)data; //Re-Make the struct
//tmp is a pointer so you have to use -> which is shorthand for (*tmp).yaw
cout << tmp->yaw; 
}
Felicidadfelicie answered 8/12, 2012 at 8:53 Comment(4)
What solution would be more efficient? Yours or mine? This operation will be running ~30-50 times a secondEntice
memcpy is probably more efficient. So, I would go with your solution. It also makes the code easier to read.Felicidadfelicie
Thanks for explaining why I would have to use -> I've still new to C++, only started to learn 2-3months ago, and all the *'s and &'s still confuse me sometimesEntice
yup, pointers are a classic hurdle C and C++ beginners have to clear. As to why memcpy is more efficient, have a look at this questionFelicidadfelicie

© 2022 - 2024 — McMap. All rights reserved.