Go 言語は、interface{} を使用して関数ポインタを転送し、関数の必須の型変換を実装します。

背景紹介

Golang の関数名は&ポインタを使用できないため、直接使用することはできませんunsafe.Pointer。そのため、関数をキャストしたり、シグネチャを持つ関数をパラメータとして使用したりすることはできません。

問題分析

しかし、fmt.Print一連の関数が任意の関数を入力パラメーターとして受け取り、そのポインターを出力できることはわかっています。そこで、突破口としてソースコードを解析してみると解決策が見つかるかもしれません。
ソースコードを解析した結果、 Golangではどのような種類のinterface{}変数もインターフェースを実装しているとみなせるため、関数名をインターフェースに変換することでアドレスを取得できる変数を取得できることが分かりました。パッケージはこの変数を通じてその型、つまり渡される関数の型を取得できる
ため、この変数を単なる関数ポインターにすることはできません。reflectしたがって、変数は次の構造に適用されます。

// 没有方法的interface
type eface struct {
    
    
	_type uintptr
	data  unsafe.Pointer
}

ここのフィールドdataは、必要な関数ポインターです。これを取得したらunsafe.Pointer、キャストを介して関数変数にコピーできます。この関数変数の型は渡される型と同じである必要はないため、関数の必須の型変換を実現できます。

サンプルコード

ここでは、パッケージを使用して をplugin生成しplugin.so、メイン関数がプラグインをロードして独自のCalled関数ポインタをプラグインに渡し、プラグインは関数ポインタを独自の関数変数に保存してから、mainfunメイン関数Callがプラグインのメソッド。mainfunメイン関数でプラグイン呼び出し関数を実現するために関数ポインターを呼び出します。

メイン関数 main.go

package main

import (
	"fmt"
	"plugin"
)

func Called() {
    
    
	fmt.Println("main func is called!")
}

func main() {
    
    
	h, err := plugin.Open("plugin/plugin.so")
	var initfun, callfun plugin.Symbol
	if err == nil {
    
    
		initfun, err = h.Lookup("Inita")
		if err == nil {
    
    
			callfun, err = h.Lookup("Call")
			if err == nil {
    
    
				fmt.Println("addr from main:", Called)
				initfun.(func(interface{
    
    }))(Called)
				Called()
				callfun.(func())()
			}
		}
	}
	if err != nil {
    
    
		fmt.Println(err)
	}
}

プラグイン-プラグイン/plugin.go

package main

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

var mainfun func()

// 没有方法的interface
type eface struct {
    
    
	_type uintptr
	data  unsafe.Pointer
}

func Inita(ptr interface{
    
    }) {
    
    
	fmt.Println("addr val of ptr:", ptr)
	fmt.Printf("addr val of unsafe: 0x%x\n", reflect.ValueOf(ptr).Pointer())
	f := (*eface)(unsafe.Pointer(&ptr)).data
	mainfun = *(*(func()))(unsafe.Pointer(&f))
	fmt.Println("addr from plug:", mainfun)
}

func Call() {
    
    
	mainfun()
}

次のようにコンパイルして実行します。

go build -ldflags "-s -w" -o main
cd plugin
go build -buildmode=plugin -ldflags "-s -w"
cd ..
./main 

出力は次のとおりです

addr from main: 0x40cdc40
addr val of ptr: 0x40cdc40
addr val of unsafe: 0x40cdc40
addr from plug: 0x40cdc40
main func is called!
main func is called!

おすすめ

転載: blog.csdn.net/u011570312/article/details/120774188