Use reflect to create struct by its name at run time

Hi,

i need to design a plugin and host architecture, something like this:

==============
package external

type AuthService interface {
Auth()
}

type LocalUserPlugin struct{

}

func (p *LocalUserPlugin) Auth() {
println(“Auth from local user plugin”)
}

type RemotePluginStub struct {

}

func (p *RemotePluginStub) Auth() {
println("Auth from remote plugin stub ")
}

============

package main

func main() {

names :=[]string {“external.LocalUserPlugin”, “external.RemotePluginStub”}

for _, name := range names {

t := reflect.TypeOf(name)
o := reflect.New(t)
e := o.Elem()
f := e.Interface()
s, ok := f.(AuthService)

if !ok {
  println("bad type")
  continue
}

s.Auth()

}

}

===========

notice that the plugins are provided by others , the host only know the package name and the interface it provides.

so in the main package ( the host), it needs to create the specific plugin type at runtime.

but the above code does not work.

why does not the reflect.TypeOf() work for given type name ?
what is the workaround for this ?

Thanks
Albert

Hi @Albert_Liu,

The type of name is string. TypeOf works as designed here. (Try fmt.Println(t))

Regarding the overall goal of using different implementations for AuthService, I am not sure why you would need reflection here. The two structs already implement the AuthService interface and hence can be passed to any function that expects an AuthService.

What do you want to achieve beyond that?

3 Likes

I don’t know if you can do with reflect but in your case I’d create a constructor function like

func NewAuthService(name string) AuthService {
   ...
}

then in the function you create the logic to choose the right implementation

1 Like

because plugins are from a different binary under package named “external”, provided by external vendors in different companies.

the package “main” is the hosting app i developed, the agreement between me and external plugin is the interface:

type AuthService interface {
Auth()
}

the vendors only gives me their binary file (no source code share) so i have to get their objects at runtime to know which object has implemented the above interface.

your suggestion means there will be a big “if … else” , and i have to know the name of each structs that implements the interface

type AuthService interface {
Auth()
}

again, plugins are provided by other companies in binary file. i have no way to know their objects. i only know they will implement the above interface.

Thanks for clarifying.

I have not yet heard of a way of reverse-engineering and accessing data structures and methods out of a different binary using reflection. I guess you would have to dig into the binary’s symbol table and use some debugger techniques to find and call that external code.

You might want to look into how Delve (the popular Go debugger) is doing this, but a much better way IMHO would be to agree with your external providers on a protocol to remote-call the third-party binaries. There are quite a few options available, including -

  • simple stdin/stdout pipelines
  • net/rpc
  • message queues (NATS, or brokerless versions like nsq)
  • gRPC
  • REST
  • GraphQL

Here is a nice article from some random guy on the internet :wink: as a starter: Plugins in Go · Applied Go (Also showcases net/rpc.)

1 Like

Just one (maybe stupid) idea. Can’t you use the plugin package?

so maybe each vendor can provide a plugin that exposes some API to register them self in your system.
For instance the interface might be

const PluginName string = "com.my-company.AuthService"

func NewService() AuthService {
    ....
}

Then at startup your system can load all plugins and create a map like

var PluginsMap map[string]func() = map[string]func(){}

func init() {
   // Load all plugins and put in the map the plugin's Name and NewService
}

func main() {
    var pluginName string
    // Use some criteria to decide the Name

    var svc AuthService
    if f, ok := PluginsMap[string]; ok {
        svc - f()
    } else {
        // default implementation or panic
    }
}
1 Like

thanks.

not a big fan of rpc / grpc. that requires message serialization/

i was hoping there is a way to directly get object using the binary file with relfection.

same thing can be done easily if use microsoft .net reflection

but seems hard to do with golang reflection

thanks.

using the standard library plugin it quite close.

the only thing is that vendors can easily forget to build their code as a plugin with the ‘go build -buildmode=plugin’ option, that results their binary file can not be used to get symbols.

actually under the hood of the plugin.Open() is what missing in the golang reflection.

why can not golang expose the way it implements of plugin.Open as public methods so that anyone can load any binary file to look up symbols in it (not only limit to binary build with the -buildmode=plugin option)?

well, to be honest … this is how other plugins system works. People have to know they are building a plugin and then have to know what are the symbols to expose.

Many languages without any reflection feature (like C/C++) implement plugins in the way I described, the main system define commons interface and the plugin’s creator shall implement it.

In general a plugin shall have

  • a unique identifier (usually in the form of a reverse DNS name like com.my-company.audio-plugin)
  • [optional] a version string
  • a initialization/registration method that who loads the plugin uses to get information and then use the plugin

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.