我通过CGO链接一个库,并不是所有的实现或版本都有一个我想在可能的情况下使用的功能 - 即函数int feature(void)
的存在。有没有办法在尝试通话之前检查这个符号是否已定义?
任何尝试使用C.feature()
都会导致在具有不支持该功能的库版本的系统上出现构建失败。
如果不清楚,我想针对许多平台构建,这些平台可能有也可能没有该功能。我想我要么能够在运行时检查函数是否存在(更理想),要么使用go:generate
进行检查并根据找到的内容更改代码(不太理想)。无论哪种方式,我都不太确定如何继续下去。
答案 0 :(得分:0)
大多数图书馆都附带一个描述其版本的#define
。
例如,在1.0版中:
// simple.h
#define LIB_SIMPLE_VERSION 0x00010000
void hello();
在1.1版中:
// simple.h
#define LIB_SIMPLE_VERSION 0x00010001
void hello();
void bye();
您有很多选择:
IsByeAvailable()
功能来检查是否存在bye()
的签名,使其返回error
或bool
ok
// simple.go
// #include <simple.h>
import "C"
func IsByeAvailable() bool {
return C.LIB_SIMPLE_VERSION >= 0x00010001
}
func Hello() {
C.hello()
}
func Bye() {
C.bye()
}
// version_support.c
#include <simple.h>
#if LIB_SIMPLE_VERSION < 0x00010001
void bye() { /* Empty */ }
#endif
答案 1 :(得分:0)
在构建程序时,有几种方法可以做到这一点,但这将使您需要始终进行两次构建并维护程序的两个版本,这不方便。在运行时,你是非常有限的,因为C本身不是反射性的,Go运行时功能不会(实际上不能)给你任何好处。
但是,你可以做两个非便携式(但可能是最好的)黑客攻击。而且由于问题更多的是C问题,黑客也更多是C黑客。一个是直接动态链接器接口,即dlopen()
/ dlsym()
,另一个是动态链接器中弱符号的使用。
让我们首先创建一些设置来测试:
$ tree
.
├── lib
│ ├── lib1.c
│ ├── lib1.h
│ └── lib2.c
└── some.go
$ cat lib/lib1.h
int feature(void);
int fun(int a);
$ cat lib/lib1.c
int feature(void)
{
return 5;
}
int fun(int a)
{
return a*a;
}
$ cat lib/lib2.c
int fun(int a)
{
return a*a;
}
这是一个非常简单的库,它声明了两个函数,并且有两个实现,一个有两个,另一个只有一个。建立它们很容易:
$ gcc -shared -o lib/lib1.so.featured lib/lib1.c
$ gcc -shared -o lib/lib1.so.featureless lib/lib2.c
一个符号链接,可以在两个版本之间轻松切换:
$ ln -s lib1.so.featured lib/lib1.so
因此,对于dlopen()
/ dlsym()
,您创建了一个包装器并使用动态链接器接口来获取这样的feature()
指针(是的,您可以在不调用{{1在每次调用时,让我们使用最小的代码):
dlsym()
测试:
package main;
// #cgo LDFLAGS: -Llib -Wl,-rpath lib -l1 -ldl
// #include "lib/lib1.h"
// #include <stddef.h>
// #include <dlfcn.h>
//
// int feature_wrap(void)
// {
// static void* dlhandle;
// static int (*featurep)(void);
//
// if (!dlhandle)
// dlhandle = dlopen(NULL, RTLD_NOW);
// if (!dlhandle) // error
// return 0;
// featurep = dlsym(dlhandle, "feature");
// if (featurep)
// return featurep();
// else
// return 3;
//}
import "C"
import "fmt"
func main() {
r := C.feature_wrap()
fmt.Println(r)
}
对于弱符号方法(因为它更易于IMO)你需要将$ go build some.go
$ ln -sf lib1.so.featured lib/lib1.so
$ ./some
5
$ ln -sf lib1.so.featureless lib/lib1.so
$ ./some
3
函数重新定义为弱函数,并为它创建一个包装器,它将在两个实现之间提供运行时切换:
feature()
测试是一样的。
显然,一旦你有了package main;
// #cgo LDFLAGS: -Llib -Wl,-rpath lib -l1
// #include "lib/lib1.h"
// int feature(void) __attribute__((weak));
// int feature_wrap(void)
// {
// if (feature)
// return feature();
// else
// return 3;
//}
import "C"
import "fmt"
func main() {
r := C.feature_wrap()
fmt.Println(r)
}
,你就可以做任何你需要的东西来代替丢失的if
,包括对Go代码的回调。