Добрый день уважаемым форумчанам. Продолжаю потихоньку ковыряться с Golang, и захотелось странного. Есть некий программный продукт, который умеет плагины. Плагины можно писать на FPC,C,C++. С последнего релиза Golang научился в библиотеки с нормальным экспортом. Вполне себе дергаются функции из собранной в Go библиотеке из Lazarus и т.д. Так вот, программный продукт требует на вход следующее:
static char* exports[] =
{
(char*)"Add",(char*)"procedure Add(a, b :integer; var result : integer);"
};
extern "C" void __declspec(dllexport) Add (int a, int b , int & res)
{
res = a + b;
}
extern "C" int __declspec(dllexport) GetPluginABIVersion()
{
return 2;
}
extern "C" int __declspec(dllexport) GetFunctionCount()
{
return NumExports;
}
extern "C" int __declspec(dllexport) GetFunctionInfo(int index, void* &address, char* &def)
{
if (index < NumExports)
{
#if defined _WIN32 || defined _WIN64
address = (void*)GetProcAddress(dllinst, exports[index * 2]);
#else
address = dlsym(RTLD_DEFAULT, exports[index * 2]);
#endif
strcpy(def, exports[index * 2 + 1]);
return index;
}
return -1;
}
Прога загружая плагин, получает из GetFunctionCount количество функций плагина. Затем вызывает n раз GetFunctionCount, из которой получает указатель на функцию, которую надо пробросить из дебрей библиотеки и текстовое определение оной. Мне хочется странного, написать плагин подобного рода на Go. Описательная часть экспортируется, всё замечательно. Текстовая часть тоже пробрасывается и из проги получается. А вот указатель на функцию в библиотеке - никак. Тоесть нет, указатель на функцию то я получить могу, только его нельзя использовать из вне. Сейчас попытка выглядит так:
import "C"
import "fmt"
import "unsafe"
import "reflect"
import "runtime"
func GetFunc(outFuncPtr interface{}, name string) error {
codePtr, err := FindFuncWithName(name)
if err != nil {
return err
}
CreateFuncForCodePtr(outFuncPtr, codePtr)
return nil
}
type Func struct {
codePtr uintptr
}
func CreateFuncForCodePtr(outFuncPtr interface{}, codePtr uintptr) {
outFuncVal := reflect.ValueOf(outFuncPtr).Elem()
newFuncVal := reflect.MakeFunc(outFuncVal.Type(), nil)
funcValuePtr := reflect.ValueOf(newFuncVal).FieldByName("ptr").Pointer()
funcPtr := (*Func)(unsafe.Pointer(funcValuePtr))
funcPtr.codePtr = codePtr
outFuncVal.Set(newFuncVal)
}
func FindFuncWithName(name string) (uintptr, error) {
for moduleData := &Firstmoduledata; moduleData != nil; moduleData = moduleData.next {
for _, ftab := range moduleData.ftab {
f := (*runtime.Func)(unsafe.Pointer(&moduleData.pclntable[ftab.funcoff]))
if f.Name() == name {
return f.Entry(), nil
}
}
}
return 0, fmt.Errorf("Invalid function name: %s", name)
}
var Firstmoduledata Moduledata
type Moduledata struct {
pclntable []byte
ftab []Functab
filetab []uint32
findfunctab uintptr
minpc, maxpc uintptr
text, etext uintptr
noptrdata, enoptrdata uintptr
data, edata uintptr
bss, ebss uintptr
noptrbss, enoptrbss uintptr
end, gcdata, gcbss uintptr
typelinks []interface{}
modulename string
modulehashes []interface{}
gcdatamask, gcbssmask Bitvector
next *Moduledata
}
type Functab struct {
entry uintptr
funcoff uintptr
}
type Bitvector struct {
n int32 // # of bits
bytedata *uint8
}
func GetStrFromDLL(str *C.char) *C.char {
return C.CString(fmt.Sprintf("From DLL: %s!\n", C.GoString(str)))
}
func GetIntFromDLL() int32 {
return 42
}
//export GetPluginABIVersion
func GetPluginABIVersion () int32 {
return 2
}
//export GetFunctionCount
func GetFunctionCount () int32 {
return 2
}
//export GetFunctionInfo
func GetFunctionInfo(x int32, ProcAddr *uintptr, ProcDef **C.char) int32{
var s string
switch x {
case 0: {
s = "function GetStrFromDLL(P: Pchar):Pchar;"
*ProcDef = C.CString(fmt.Sprintf("%s", s))
//var GetStrFromDLLFunc func(*C.char) *C.char
// GetFunc(&GetStrFromDLLFunc, "libGoPlugin.GetStrFromDLL")
*ProcAddr = reflect.ValueOf(GetStrFromDLL).Pointer()
}
case 1: {
s = "function GetIntFromDLL():integer;"
*ProcDef = C.CString(fmt.Sprintf("%s", s))
//var GetIntFromDLLFunc func() int32
// GetFunc(&GetIntFromDLLFunc, "libGoPlugin.GetIntFromDLL")
*ProcAddr = reflect.ValueOf(GetIntFromDLL).Pointer()
}
}
return x;
}
func main() {
// Need a main function to make CGO compile package as C shared library
}
Собственно вопрос заключается в том, можно ли пробросить указатель на Go функцию из библиотеки и использовать её в приложении на другом языке? Или же это не возможно средствами Go?
PS: ProcDef Приложение получает, а указатель роняет его и всё.