MatrixAI / Emergence

Distributed Infrastructure Orchestration
Apache License 2.0
1 stars 0 forks source link

What is the best way to serialise structs to/from Go #30

Closed mokuki082 closed 5 years ago

mokuki082 commented 6 years ago

A way to pass structs to and from Go is needed for the creation of containers.

The main structs we need to serialise are spec.Spec and Container.

This stackoverflow post suggests that directly accessing C structs from Go is possible.

I should investigate how pointers work and what is provided in cgo.

mokuki082 commented 6 years ago

My goal is to create a Go function createContainer, where I should be able to give it some config struct from C/Haskell, and it would call the underlying libcontainer API to create the container, and return another c struct with all necessary information about the container back to C.

As a simplification of the problem, I created a practice project with a function Foo() that acts as the createContainer function, and two structs Fooz and Barz which acts as the input and outputs of the Foo function respectively.

func foo(f *C.struct_Fooz) (*C.struct_Barz, error) {
    // Do some operations...
    return ConvBarz(bar(ConvFooz(f))), nil
}

The pseudo-module C has to be imported for this to work. "C" is not a real module, it represents the C namespace and it gets replaced from C.xxx to _C_xxx during compilation phase, which can be interpreted as unexported Go types/variables.

Although untested, this function is supposed to take in a C struct and return a C struct using the C pseudo-module, which is precisely what I wanted to do for createContainer.

The marshalling processes ConvBarz and ConvFooz can be a little more complicated when dealing with structs of structs, or structs of arrays, but for simplicity Fooz and Barz both contains an int field n, which can be converted easily using int() as follows:

func ConvFooz(f *C.struct_Fooz) *data.Fooz {
    return &data.Fooz { int(f.n) }
}

func ConvBarz(b *data.Barz) *C.struct_Barz {
    return &C.struct_Barz{ C.int(b.N) } // N is exported because it's in a different package.
}

Altogether this is compiled into a shared object.

There are a lot of restrictions in passing pointers between C and Go. I'm not confident that this code works but at least it compiled. I'll be working on calling the shared library from C/Haskell tomorrow

mokuki082 commented 5 years ago

It is working!

I renamed the variables so they are more readable now, here is the code

Here are some main points that I've discovered:

CMCDragonkai commented 5 years ago

It makes sense that a go function called by C cannot return a go pointer. The usual idiom in C when expecting the procedure to allocate something onto the heap is to pass it the result pointer. C functions can only return by value. That is primitive values. There was some work done to allow C to pass structs by value though.