Go language uses interface{} to transfer function pointers to implement mandatory type conversion of functions

background introduction

Golang's function name cannot use &pointers, so it cannot be used directly unsafe.Pointer, so it is impossible to cast the function or use a function with any signature as a parameter.

problem analysis

But we know that fmt.Printa series of functions can take any function as an input parameter and print its pointer. Therefore, if we analyze its source code as a breakthrough, we may be able to find a solution.
After analyzing the source code, we found that because any type of variable in Golang can be considered as implementing interface{}an interface, by converting the function name into an interface, we can get a variable that can take an address.
This variable cannot be just a function pointer, because reflectthe package can get its type through this variable, that is, the type of the function passed in. Therefore, the variable applies to the following structure:

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

The field here datais the function pointer we want. Once you have this unsafe.Pointer, you can copy it to a function variable via a cast. The type of this function variable does not have to be the same as the type passed in, so the mandatory type conversion of the function can be realized.

sample code

Here a package is used to plugingenerate one plugin.so, the main function loads the plug-in and passes its own Calledfunction pointer to the plug-in, the plug-in saves the function pointer in its own function variable mainfun, and then the main function calls Callthe method of the plug-in, which calls the function pointer mainfunto achieve Plugins call functions in the main function.

main function 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-plugin/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()
}

Compile and run as follows:

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

The output is as follows

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!

Guess you like

Origin blog.csdn.net/u011570312/article/details/120774188