Golang - structs, initializing and memcpy - what is the proper way?

10.5k views Asked by At

I'm new to Go and I wish to translate some C/C++ code I have to Go but I haven't been able to. The problem resides in 2 places: how can I initialize a struct I've defined and how to do a "memcopy"

The code I'm talking about is this: http://play.golang.org/p/e8N255qEAk line 69 and 74.

The C/C++ code I wanted to "translate" to Go is this:

            typedef char xchr;
            typedef int xint;
            typedef double xdob;
            typedef float xflt;
            typedef struct
            {
               xint p; 
               xdob lat_lon_ele[3];
               xflt psi_the_phi[3];
               xflt gear_flap_vect[3];
            }VEH1;

            avio.p = 0;
            avio.lat_lon_ele[0] = 47.460058;
            avio.lat_lon_ele[1] = -122.32104;
            avio.lat_lon_ele[2] = 8000.000;

            avio.psi_the_phi[0] = 110.5;
            avio.psi_the_phi[1] = 0.0;
            avio.psi_the_phi[2] = 0.0;

            avio.gear_flap_vect[0] = 1.0;
            avio.gear_flap_vect[1] = 0.0;
            avio.gear_flap_vect[2] = 0.0;

            VEH1* av = &avio;

            xchr data_send[6];
            data_send[0]='V';
            data_send[1]='E';
            data_send[2]='H';
            data_send[3]='1';
            data_send[4]=0;

            memcpy(&data_send[5],av,sizeof(VEH1)); // load in the data

The Go code looks like this:

            type xchr int8
            type xint int
            type xdob float64
            type xflt float32

            type VEH1 struct {
                p              xint
                lat_lon_ele    [3]xdob
                psi_the_phi    [3]xflt
                gear_flap_vect [3]xflt
            }

            type VEHA struct {
                num_p xint

                lat_lon_ele    [10][3]xdob
                psi_the_phi    [10][3]xflt
                gear_flap_vect [10][3]xflt

                lat_view, lon_view, ele_view xdob
                psi_view, the_view, phi_view xflt
            }


            var avio VEH1
            avio = &VEH1{0, {47.460058, -122.32104, 8000.000}, {110.5, 0.0, 0.0}, {1.0, 0.0, 0.0}}

            data_send := [6]xchr{'V', 'E', 'H', '1', 0, 0}
            copy(data_send[5:5], avio);

Thank you!

1

There are 1 answers

7
icza On BEST ANSWER

So basically given these basic types:

type xchr int8
type xint int
type xdob float64
type xflt float32

You want to copy the bytes (memory representation) of a value of the following struct type:

type VEH1 struct { // 52 bytes total
    p              xint    // 4 bytes (READ BELOW)
    lat_lon_ele    [3]xdob // 24 bytes
    psi_the_phi    [3]xflt // 12 bytes
    gear_flap_vect [3]xflt // 12 bytes
}

Note that the length of int in Go is platform dependent, it may be 32 bit or 64 bit depending on the target architecture you compile to. That would lead to platform dependent behavior, so let's fix it to int32 for now:

type xint int32

This is how the byte sizes of the above struct is calculated. If you need int64, just change it and add 4 additional bytes in size calculation.

Next you want the result in an array whose element types are xchr. You need a big enough array, which is the "prefix" being "VEH1" followed by the data of the above struct. So it must have a size of 4+sizeof(VEH1) which is 56.

In Go to copy the bytes of one type to the memory space of another type, you can do it the unsafe package and its general Pointer type. Any pointer can be cast to unsafe.Pointer and unsafe.Pointer can be cast to any pointer type, so this is your "gateway" between different pointer types.

You can take the address of a struct value of VHE1, convert it to Pointer, and convert the result Pointer to a pointer to the target data type. Dereference the pointer and now you already have the value of the other type.

The builtin copy() can only be used with slices, so first you need to slice your data arrays to be able to pass them to copy().

You can do it like this:

avio := VEH1{0, [3]xdob{47.460058, -122.32104, 8000.000},
    [3]xflt{110.5, 0.0, 0.0}, [3]xflt{1.0, 0.0, 0.0}}
fmt.Printf("%+v\n", avio)

pavio := unsafe.Pointer(&avio)
pavio_arr := *((*[52]xchr)(pavio))

data_send := [56]xchr{'V', 'E', 'H', '1'}
n := copy(data_send[4:], pavio_arr[:])
fmt.Printf("Copied %d bytes\n", n)

fmt.Printf("%+v\n", data_send)

Output, wrapped to fit in screen:

{p:0 lat_lon_ele:[47.460058 -122.32104 8000] psi_the_phi:[110.5 0 0]
    gear_flap_vect:[1 0 0]}
Copied 52 bytes
[86 69 72 49 0 0 0 0 0 0 0 0 -81 33 56 46 -29 -70 71 64 77 45 91 -21 -117
    -108 94 -64 0 0 0 0 0 64 -65 64 0 0 -35 66 0 0 0 0 0 0 0 0 0 0 -128 63 0 0 0 0]

Try the working demo on the Go Playground.

Having the result as []byte

If you want []byte result (e.g. you want to write the result to an io.Writer), then use [56]byte type for data_send and of course cast pavio to *[52]byte:

pavio := unsafe.Pointer(&avio)
pavio_arr := *((*[52]byte)(pavio))

data_send := [56]byte{'V', 'E', 'H', '1'}
n := copy(data_send[4:], pavio_arr[:])