Search code examples
pythonlistgoctypescgo

from **char to python list


I've solved the problem, please see my answer below the question.

But I finally found it very stupid to embed GO functions into Python. This kind of embedding fails mainly because Go function can hardly know when to recycling memory resources, and thus results in memory leaking.

At present, I realize that the best way to combine them together might be message communicating, like socks.

Please tell me anything correct if my thought is wrong.


Original Question:


At C side, a function returns an array of strings (say ["i 0","i 1","i 2","i 3"]), type is **char.

At Python side, that **char output is read into a variable (say cArray), with the type POINTER(c_char_p)

My question: How to create a python list from cArray? that is obtaining pylist == ["i 0","i 1","i 2","i 3"]

I also wonder if there is a value taking operation in python, like * operation in C.

Following is the code example:

C side (actually Go)

package main

//#include <stdlib.h>
import "C"
import (
    "unsafe"
)

//export TestLoad
func TestLoad(cstr *C.char) **C.char {
    gostr := C.GoString(cstr)
    goslice := []string{gostr, "i 0", "i 1", "i 2", "i 3"}
    cArray := C.malloc(C.size_t(len(goslice)) * C.size_t(unsafe.Sizeof(uintptr(0))))
    defer C.free(unsafe.Pointer(cArray))
    temp := (*[1<<30 - 1]*C.char)(cArray)
    for k, v := range goslice {
        temp[k] = C.CString(v)
    }

    return (**C.char)(cArray)
}

func main() {
}

Python side

from ctypes import *

mylib = cdll.LoadLibrary("./mylib.so")
mylib.TestLoad.argtype = c_char_p
mylib.TestLoad.restype = POINTER(c_char_p)  # ***Is it possible to have two or more restypes?*** 

pystr = "hello"                             #  python str
b = pystr.encode("utf-8")                   #  convert str to bytes
resp = mylib.TestLoad(b)                    #  call CGo function, and get resp typed POINTER(c_char_p)
list_len = 5                                #  assume the length of list is known
'''
TODO
'''

BTW, is it possible to have two or more returns for a single C or CGO function? I tried but failed to complile.

Thanks for help.


Solution

  • Solution:

    C side (GO) -- the line where defer C.free... locates should be commented

    package main
    
    //#include <stdlib.h>
    import "C"
    import (
        "fmt"
        "unsafe"
    )
    
    //export TestLoad
    func TestLoad(cstr *C.char) **C.char {
        gostr := C.GoString(cstr)
        fmt.Println("In Go: ", gostr)
        goslice := []string{gostr, "i 0", "i 1", "i 2", "i 3"}
        cArray := C.malloc(C.size_t(len(goslice)) * C.size_t(unsafe.Sizeof(uintptr(0))))
        // defer C.free(unsafe.Pointer(cArray))
        temp := (*[1<<30 - 1]*C.char)(cArray)
        for k, v := range goslice {
            temp[k] = C.CString(v)
        }
    
        return (**C.char)(cArray)
    }
    
    func main() {
    }
    

    Python side -- revised: POINTER(c_char_p * 5), then call resp.contents to access each python-bytes. See Example #17 for the source.

    from ctypes import *
    
    mylib = cdll.LoadLibrary("./_mylib.so")
    mylib.TestLoad.argtype = c_char_p
    mylib.TestLoad.restype = POINTER(c_char_p*5)      
    
    pystr = "Hello, world!"                             
    b = pystr.encode("utf-8")                      
    resp = mylib.TestLoad(b)  
            
    '''**********************************'''
    output = []
    for seq in resp.contents:
        s = seq.decode("utf-8")
        output.append(s)
        print(s,type(s))
    print(output)
    '''**********************************'''
    

    A new question arises:

    Would commenting // defer C.free(unsafe.Pointer(cArray)) cause memory leaking on the C-Side in this example case?