如何使用 CGo 为标准库调用 C++ 变量

我正在尝试使用 cgo 从 C++ 代码中获取变量值。对于以.hall 结尾的库工作正常,但对于,等库<iostream>,我收到以下错误:<map><string>


fatal error: iostream: No such file or directory

    4 | #include <iostream>

      |          ^~~~~~~~~~

在我的代码下面:


package main


/*

#cgo LDFLAGS: -lc++

#include <iostream>

std::string plus() {

    return "Hello World!\n";

}

*/

import "C"

import "fmt"


func main() {


    a := Plus_go()

    fmt.Println(a)


}

func Plus_go() string {

    return C.plus()

}

我添加了这个标志,因为我在https://stackoverflow.com/a/41615301/15024997#cgo LDFLAGS: -lc++的 stackoverflow 上的一个答案中看到了这个建议。


我正在使用 VS Code(不是 VS Studio)、Windows 10、Go 1.18(最新版本)。


我运行了以下命令go tool cgo -debug-gcc mycode.go来跟踪编译器的执行和输出:


$ gcc -E -dM -xc -m64 - <<EOF


#line 1 "cgo-builtin-prolog"

#include <stddef.h> /* for ptrdiff_t and size_t below */


/* Define intgo when compiling with GCC.  */

typedef ptrdiff_t intgo;


#define GO_CGO_GOSTRING_TYPEDEF

typedef struct { const char *p; intgo n; } _GoString_;

typedef struct { char *p; intgo n; intgo c; } _GoBytes_;

_GoString_ GoString(char *p);

_GoString_ GoStringN(char *p, int l);

_GoBytes_ GoBytes(void *p, int n);

char *CString(_GoString_);

void *CBytes(_GoBytes_);

void *_CMalloc(size_t);


__attribute__ ((unused))

static size_t _GoStringLen(_GoString_ s) { return (size_t)s.n; }


__attribute__ ((unused))

static const char *_GoStringPtr(_GoString_ s) { return s.p; }

#line 3 "C:\\Users\\Home\\OneDrive\\Desktop\\DevicesC++\\devices.go"



#include <iostream>

std::string plus() {

    return "Hello World!\n";

}


#line 1 "cgo-generated-wrapper"

EOF

C:\Users\Home\OneDrive\Desktop\DevicesC++\devices.go:5:10: fatal error: iostream: No such file or directory

    5 | #include <iostream>

      |          ^~~~~~~~~~

compilation terminated.

C:\Users\Home\OneDrive\Desktop\DevicesC++\devices.go:5:10: fatal error: iostream: No such file or directory

    5 | #include <iostream>

      |          ^~~~~~~~~~

compilation terminated.


慕斯王
浏览 361回答 1
1回答

侃侃无极

CGo 允许您将 Go 代码链接到实现 C 风格外部函数接口的代码。这并不意味着您可以将任意语言代码固定到位。让我们从第一个问题开始,即import "C"您的一个 Go 文件中的行必须仅包含其上方的 C 代码。那是:/*#include <stdlib.h>extern char *cstyle_plus();*/可以,但是:/*#include <stdlib.h>extern std::string *plus();*/不是,您也不能#include在这里使用任何 C++ 标头。为了稍微简化事情,这里的注释实际上被剪掉并提供给C 编译器。如果它不是有效的 C,它就不会编译。如果你想包含 C++ 代码,你可以,但你必须将它放在一个或多个单独的文件中(从技术上讲,C 或 C++ 术语中的“翻译单元”)。然后 CGo 将该文件编译为目标代码。然而,下一个问题是目标代码必须符合CGo 实现的 C外部函数接口。这意味着您的 C++ 代码必须返回 C 类型(和/或接收此类类型作为参数)。由于std::string不是 C 字符串,您实际上不能直接返回它。它不是很有效(并且存在一些解决此问题的尝试),但处理此问题的常用方法是让 C 函数返回 C 风格的“char *”或“const char *”字符串。如果字符串本身具有非静态持续时间——就像你的那样——你必须malloc在这里使用,特别是C malloc(std::malloc可能是不可互操作的)。函数本身也必须可以从C 代码调用。这意味着我们需要使用extern "C"它。因此,我们的plus.cpp文件(或任何你想称呼它的东西)可能会这样读:#include <stdlib.h>#include <iostream>std::string plus() {&nbsp; &nbsp; &nbsp; &nbsp; return "Hello World!\n";}extern "C" {char *cstyle_plus() {&nbsp; &nbsp; &nbsp; &nbsp; // Ideally we'd use strdup here, but Windows calls it _strdup&nbsp; &nbsp; &nbsp; &nbsp; char *ret = static_cast<char *>(malloc(plus().length() + 1));&nbsp; &nbsp; &nbsp; &nbsp; if (ret != NULL) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; strcpy(ret, plus().c_str());&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; return static_cast<char *>(ret);}}然后我们可以使用这个从 Go 中调用它main.go:package main/*#include <stdlib.h>extern char *cstyle_plus();*/import "C"import (&nbsp; &nbsp; &nbsp; &nbsp; "fmt"&nbsp; &nbsp; &nbsp; &nbsp; "unsafe")func Plus_go() string {&nbsp; &nbsp; &nbsp; &nbsp; s := C.cstyle_plus()&nbsp; &nbsp; &nbsp; &nbsp; defer C.free(unsafe.Pointer(s))&nbsp; &nbsp; &nbsp; &nbsp; return C.GoString(s)}func main() {&nbsp; &nbsp; &nbsp; &nbsp; a := Plus_go()&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(a)}添加一个 trivialgo.mod和 building,生成的代码运行;双换行是因为C字符串里面有一个换行符,加fmt.Println了一个换行符:$ go build$ ./cgo_cppHello World!这段代码有点草率:应该malloc失败,它返回 NULL,并将C.GoString其变成一个空字符串。然而,真正的代码应该尽量避免这种愚蠢的分配和释放序列:例如,我们可能知道字符串长度,或者有一个static不需要这种愚蠢的字符串malloc。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go