dou426098 2019-01-16 01:32
浏览 55
已采纳

如何将FUNCTION名称放宽为变量

I code a plugin generator in Go. The repo is open.

The project creates some Go code in some temporary file defining a function taken from command-line arguments, compiles that code into a plugin, loads that plugin, gets the function in it, and invokes that with parameters, then prints the results.

We want to be able to process several functions and several plugins. For example, a function SUM of body return x+y;, a function PROD of body return x*y; and so on.

I don't want the generated code to always use the constant name FUNCTION. Can't the generated .go file contain a function whose name is given at runtime, i.e. my funame in the code below? Is there some feature of the Go language forbidding that?

//TODO: Investigate how to relax the name FUNCTION into a variable
type Xinterface interface {
    FUNCTION(x int, y int) int
}

Complete code

package main

import (
    "fmt"
    "os"
    "os/exec"
    "path/filepath"
    "plugin"
    "reflect"
    "strconv"
    "strings"
)


//TODO: Investigate how to relax the name FUNCTION into a variable
type Xinterface interface {
    FUNCTION(x int, y int) int
}

func main() {
    //Declare good variable names and create the file for the code
    funame := os.Args[1]
    funamel := strings.ToLower(funame)
    funamet := strings.Title(funame)
    fubody := os.Args[2]
    x1, err := strconv.Atoi(os.Args[3])
    y1, err := strconv.Atoi(os.Args[4])
    filename := fmt.Sprintf("/tmp/%s.go", funame)
    f, err := os.Create(filename)
    if err != nil {
        fmt.Println(err)
        return
    }
    //Here comes the program
    strprg := fmt.Sprintf(`package main 
import (
    "fmt"
)
type %s string 
func(s %s) FUNCTION (x int, y int) int { fmt.Println("")
%s} 
var %s %s`, funamel, funamel, fubody, funamet, funamel)
    fmt.Printf("func(s %s) FUNCTION (x int, y int) int { 
", funamel)
    fmt.Printf("start of %s: x=%d, y=%d
", funamel, x1, y1)
    l, err := f.WriteString(strprg)
    if err != nil {
        fmt.Println(err)
        f.Close()
        return
    }
    fmt.Println(l, "bytes written successfully")
    err = f.Close()
    if err != nil {
        fmt.Println(err)
        return
    }

    ex, err := os.Executable()
    if err != nil {
        panic(err)
    }
    exPath := filepath.Dir(ex)
    fmt.Println(exPath)
    fmt.Println("compiling plugin")
    cmd := exec.Command("go", "build", "-buildmode=plugin", "-o", fmt.Sprintf("%s%s%s", "/tmp/", funame, ".so"), fmt.Sprintf("%s%s%s", "/tmp/", funame, ".go"))

    out, err2 := cmd.Output()
    fmt.Println(out)

    if err2 != nil {
        fmt.Println(err2)
        return
    }
    fmt.Println("loading module")
    // load module
    // 1. open the so file to load the symbols
    plug, err := plugin.Open(fmt.Sprintf("%s%s%s", "/tmp/", funame, ".so"))
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    fmt.Println("looking up symbol")
    // 2. look up a symbol (an exported function or variable)
    // in this case, variable funame
    symX, err := plug.Lookup(funame)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    fmt.Println("checking module")
    // 3. Assert that loaded symbol is of a desired type
    // in this case interface type X (defined above)
    var myvar Xinterface
    myvar, ok := symX.(Xinterface)
    if !ok {
        fmt.Println(fmt.Sprintf("unexpected type from module symbol %s", reflect.TypeOf(symX.(Xinterface))))
        os.Exit(1)
    }

    // 4. use the module

    fmt.Println(myvar.FUNCTION(x1, y1))
    fmt.Println(fmt.Sprintf("Generated code: %s", fmt.Sprintf("/tmp/%s%s", funamet , ".go") ))
    fmt.Println(fmt.Sprintf("Generated object file: %s", fmt.Sprintf("/tmp/%s%s", funamet , ".so") ))

}

Example usage:

$ go run forbasile.go SUM 'return x+y' 3 5
func(s sum) FUNCTION (x int, y int) int { 
start of sum: x=3, y=5
131 bytes written successfully
/tmp/go-build104174513/b001/exe
compiling plugin
[]
loading module
looking up symbol
checking module

8
Generated code: /tmp/SUM.go
Generated object file: /tmp/SUM.so
$ go run forbasile.go SUMSQUARE 'return x*x + y*y' 3 4
func(s sumsquare) FUNCTION (x int, y int) int { 
start of sumsquare: x=3, y=4
161 bytes written successfully
/tmp/go-build555823501/b001/exe
compiling plugin
[]
loading module
looking up symbol
checking module

25
Generated code: /tmp/SUMSQUARE.go
Generated object file: /tmp/SUMSQUARE.so
  • 写回答

1条回答 默认 最新

  • dongwen2896 2019-01-16 01:45
    关注

    godoc

    A Symbol is a pointer to a variable or function.

    For example, a plugin defined as

    package main
    
    import "fmt"
    
    func F() { fmt.Printf("Hello, number %d
    ", V) }
    

    may be loaded with the Open function and then the exported package symbols V and F can be accessed

    p, err := plugin.Open("plugin_name.so")
    if err != nil {
        panic(err)
    }
    f, err := p.Lookup("F")
    if err != nil {
        panic(err)
    }
    f.(func())() // prints "Hello, number 7"
    

    "F" is just a String, so you can change its value at runtime anyway.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 matlab图像高斯低通滤波
  • ¥15 针对曲面部件的制孔路径规划,大家有什么思路吗
  • ¥15 钢筋实图交点识别,机器视觉代码
  • ¥15 如何在Linux系统中,但是在window系统上idea里面可以正常运行?(相关搜索:jar包)
  • ¥50 400g qsfp 光模块iphy方案
  • ¥15 两块ADC0804用proteus仿真时,出现异常
  • ¥15 关于风控系统,如何去选择
  • ¥15 这款软件是什么?需要能满足我的需求
  • ¥15 SpringSecurityOauth2登陆前后request不一致
  • ¥15 禅道二次开发编辑版本,上传不了发行包