dtrgqjcd877528 2018-01-10 13:34
浏览 51

CGO和某些字符串切片的运行时错误

I have stripped back a problem I have come across whilst wrapping some C code to work with golang using swig but the problem doesn't rest with swig.

I can pass in a basic string slice but as soon as I construct the slice with anything other than basic strings, I get a panic: runtime error: cgo argument has Go pointer to Go pointer.

go version go1.8.5 linux/amd64

This is the sample code and its output

package main

import (
    "fmt"
    "reflect"
    "unsafe"
)

/*
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct { char *p; int n; } _gostring_;
typedef struct { void* array; int len; int cap; } _goslice_;

void prtText(char * const *txt, int len)
{
    int i = 0;

    for ( i=0; i<len; i++ ) {
        printf("Text %d is: %s
", i, txt[i]);
    }
}

void _wrap_printText(_goslice_ _swig_go_0) {

  _gostring_ *p;

  char **arg1 = (char **)calloc(_swig_go_0.len, sizeof(char*));
  if (arg1) {
    for (int i=0; i<_swig_go_0.len; i++) {
      p = &(((_gostring_*)_swig_go_0.array)[i]);
      arg1[i] = calloc(1,(p->n)+1);
      strncpy(arg1[i], p->p, p->n);
    }
  }
  int arg2 = _swig_go_0.len;

  prtText((char *const *)arg1,arg2);
}

*/
import "C"

func PrintText(arg1 []string) {
    C._wrap_printText(*(*C._goslice_)(unsafe.Pointer(&arg1)))
}

func main() {
    s := []string{}

    s = append(s, "blah")
    s = append(s, "hello")
    s = append(s, "again")

    ns := []string{}

    ns = append(ns, "ns: "+s[0])
    ns = append(ns, "ns: "+s[1])
    ns = append(ns, "ns: "+s[2])

    fmt.Println("type s:", reflect.TypeOf(s))
    fmt.Println("type ns:", reflect.TypeOf(ns))
    fmt.Println("s:", s)
    fmt.Println("ns:", ns)

    PrintText(s)
    PrintText(ns)
}


go build -i -x -gcflags '-N -l' main.go

./main
type s: []string
type ns: []string
s: [blah hello again]
ns: [ns: blah ns: hello ns: again]
Text 0 is: blah
Text 1 is: hello
Text 2 is: again
panic: runtime error: cgo argument has Go pointer to Go pointer

As you can see, the first string slice works fine but as soon as I do anything other than basic strings, it fails. I've tried making new strings first before appending them to the slice but the problem remains.

What am I doing wrong?

  • 写回答

1条回答 默认 最新

  • doujiu1447 2018-01-10 13:50
    关注

    You're basically passing the raw Go pointers. Instead, you should build C arrays yourself.

    As a general rule, seeing unsafe pretty much anywhere should make you suspicious. It is rarely the right way around issues with cgo.

    Using the helpers from Passing array of string as parameter from go to C function and using them in your code:

    package main
    
    import (
      "fmt"
      "reflect"
    )
    
    /*
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    void prtText(char * const *txt, int len)
    {
        int i = 0;
    
        for ( i=0; i<len; i++ ) {
            printf("Text %d is: %s
    ", i, txt[i]);
        }
    }
    
    static char**makeCharArray(int size) {
            return calloc(sizeof(char*), size);
    }
    
    static void setArrayString(char **a, char *s, int n) {
            a[n] = s;
    }
    
    static void freeCharArray(char **a, int size) {
            int i;
            for (i = 0; i < size; i++)
                    free(a[i]);
            free(a);
    }
    
    */
    import "C"
    
    func main() {
      s := []string{}
    
      s = append(s, "blah")
      s = append(s, "hello")
      s = append(s, "again")
    
      ns := []string{}
    
      ns = append(ns, "ns: "+s[0])
      ns = append(ns, "ns: "+s[1])
      ns = append(ns, "ns: "+s[2])
    
      fmt.Println("type s:", reflect.TypeOf(s))
      fmt.Println("type ns:", reflect.TypeOf(ns))
      fmt.Println("s:", s)
      fmt.Println("ns:", ns)
    
      sargs := C.makeCharArray(C.int(len(s)))
      defer C.freeCharArray(sargs, C.int(len(s)))
      for i, p := range s {
        C.setArrayString(sargs, C.CString(p), C.int(i))
      }
    
      nsargs := C.makeCharArray(C.int(len(ns)))
      defer C.freeCharArray(nsargs, C.int(len(ns)))
      for i, p := range ns {
        C.setArrayString(nsargs, C.CString(p), C.int(i))
      }
    
      C.prtText(sargs, C.int(len(s)))
      C.prtText(nsargs, C.int(len(ns)))
    }
    

    The output is now as expected:

    $ ./main 
    type s: []string
    type ns: []string
    s: [blah hello again]
    ns: [ns: blah ns: hello ns: again]
    Text 0 is: blah
    Text 1 is: hello
    Text 2 is: again
    Text 0 is: ns: blah
    Text 1 is: ns: hello
    Text 2 is: ns: again
    
    评论

报告相同问题?

悬赏问题

  • ¥15 求解O-S方程的特征值问题给出边界层布拉休斯平行流的中性曲线
  • ¥15 谁有desed数据集呀
  • ¥20 手写数字识别运行c仿真时,程序报错错误代码sim211-100
  • ¥15 关于#hadoop#的问题
  • ¥15 (标签-Python|关键词-socket)
  • ¥15 keil里为什么main.c定义的函数在it.c调用不了
  • ¥50 切换TabTip键盘的输入法
  • ¥15 可否在不同线程中调用封装数据库操作的类
  • ¥15 微带串馈天线阵列每个阵元宽度计算
  • ¥15 keil的map文件中Image component sizes各项意思