-1

I want to return an array to C caller, just like below, how to do it?

//export EtcdGetAllNodes
func EtcdGetAllNodes()[]uint32 {
    a := []uint32{1,2,3}
    return a
}

This function EtcdGetAllNodes will try to get value with a prefix for the specific key from etcd, it will return multiple values. How to return these values to C caller?

peterSO
  • 158,998
  • 31
  • 281
  • 276
dingrui
  • 417
  • 2
  • 5
  • 15

1 Answers1

1

Command cgo

Passing pointers

A Go function called by C code may not return a Go pointer (which implies that it may not return a string, slice, channel, and so forth). A Go function called by C code may take C pointers as arguments, and it may store non-pointer or C pointer data through those pointers, but it may not store a Go pointer in memory pointed to by a C pointer. A Go function called by C code may take a Go pointer as an argument, but it must preserve the property that the Go memory to which it points does not contain any Go pointers.


I want to return an array to C caller.

//export EtcdGetAllNodes
func EtcdGetAllNodes() []uint32 {
  a := []uint32{1, 2, 3}
  return a
}

A Go function called by C code may not return a Go pointer (which implies that it may not return a slice).


There are many possible solutions: Command cgo.

For example, here is one simple solution:

Output:

$ go build -buildmode=c-archive -o cmem.a cmem.go
$ gcc -pthread -o cmem cmem.c cmem.a
$ ./cmem
-- EtcdGetAllNodes --
nodes: 3
node 0: 1
node 1: 2
node 2: 3
$ echo $?
0
$ 

cmem.go:

package main

/*
#include <stdint.h>
#include <stdlib.h>
*/
import "C"

import "unsafe"

// toC: Go slice to C array
// c[0] is the number of elements,
// c[1] through c[c[0]] are the elements.
// When no longer in use, free the C array.
func toC(a []uint32) *C.uint32_t {
    // C array
    ca := (*C.uint32_t)(C.calloc(C.size_t(1+len(a)), C.sizeof_uint32_t))
    // Go slice of C array
    ga := (*[1 << 30]uint32)(unsafe.Pointer(ca))[: 1+len(a) : 1+len(a)]
    // number of elements
    ga[0] = uint32(len(a))
    // elements
    for i, e := range a {
        ga[1+i] = e
    }
    return ca
}

//export EtcdGetAllNodes
// EtcdGetAllNodes: return all nodes as a C array.
// nodes[0] is the number of node elements.
// nodes[1] through nodes[nodes[0]] are the node elements.
// When no longer in use, free the nodes array.
func EtcdGetAllNodes() *C.uint32_t {
    // TODO: code to get all etcd nodes
    a := []uint32{1, 2, 3}
    // nodes as a C array
    return toC(a)
}

func main() {}

cmem.c:

#include "cmem.h"
#include <stdint.h>
#include <stdio.h>

int main() {
    printf("-- EtcdGetAllNodes --\n");
    // nodes[0] is the number of node elements.
    // nodes[1] through nodes[nodes[0]] are the node elements.
    // When no longer in use, free the nodes array.
    uint32_t *nodes = EtcdGetAllNodes();
    if (!nodes) {
        return 1;
    }
    printf("nodes: %d\n", *nodes);
    for (uint32_t i = 1; i <= *nodes; i++) {
        printf("node %d: %d\n", i-1,*(nodes+i));
    }
    free(nodes);
    return 0;
}
peterSO
  • 158,998
  • 31
  • 281
  • 276