英文:
How to get an arbitrary method signature using reflection?
问题
我可以帮你解决这个挑战,这里是playground。
最终目标是将函数和结构体的公共函数注册到活动管理器中,并通过函数名执行它们,大致如下所示:
pool := map[string]interface{}{
"Sample": func(ctx context.Context) error,
"Sample2": func(ctx context.Context, args ...interface{}) error,
"SampleFromStruct": func(ctx context.Context) error,
"Sample2FromStruct": func(ctx context.Context, args ...interface{}) error,
}
这些函数看起来像这样:
func Sample(ctx context.Context) error {
fmt.Println("exec Sample")
return nil
}
func Sample2(ctx context.Context, args interface{}) error {
arguments := struct {
Foo string `json:"foo"`
Bar string `json:"bar"`
}{}
b, err := json.Marshal(args)
if err != nil {
return err
}
if err := json.Unmarshal(b, &arguments); err != nil {
return err
}
fmt.Println("exec Sample2 with args", arguments)
return nil
}
// 以及相同的结构体
type ActivityInStruct struct {
Bar string
}
func (a *ActivityInStruct) SampleInStruct(ctx context.Context) error {
fmt.Println("Value of Bar", a.Bar)
return Sample(ctx)
}
func (a *ActivityInStruct) Sample2InStruct(ctx context.Context, args interface{}) error {
fmt.Println("Value of Bar", a.Bar)
return Sample2(ctx, args)
}
说到这里,我已经用以下实现使函数正常工作:
type activityManager struct {
fnStorage map[string]interface{}
}
func (lm *activityManager) Register(fn interface{}) error {
fnName := strings.Split((runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name()), ".")
name := fnName[len(fnName)-1]
lm.fnStorage[name] = fn
return nil
}
func (lm *activityManager) Exec(ctx context.Context, fnName string, args ...interface{}) error {
fn, ok := lm.fnStorage[fnName]
if !ok {
return fmt.Errorf("activity %s not found", fnName)
}
if signatureCtx, ok := fn.(func(context.Context) error); ok {
return signatureCtx(ctx)
}
if signatureWithArgument, ok := fn.(func(context.Context, interface{}) error); ok {
return signatureWithArgument(ctx, args[0])
}
return fmt.Errorf("signature for %s not supported", fnName)
}
因此,执行过程如下:
func NewManager() *activityManager {
return &activityManager{
fnStorage: map[string]interface{}{},
}
}
/*...*/
ctx := context.Background()
manager := NewManager()
manager.Register(Sample)
manager.Register(Sample2)
if err := manager.Exec(ctx, "Sample"); err != nil {
fmt.Println("Sample error", err.Error())
return
}
args1 := map[string]interface{}{
"foo": "isFoo",
"bar": "isBar",
}
if err := manager.Exec(ctx, "Sample2", args1); err != nil {
fmt.Println("Sample2 error", err.Error())
return
}
然而,要注册这样的内容:
func (lm *activityManager) RegisterStruct(fn interface{}) error {
t := reflect.TypeOf(fn)
for i := 0; i < t.NumMethod(); i++ {
m := t.Method(i)
if m.IsExported() {
/*
This won't work cause m.Type are
func(*main.ActivityInStruct, context.Context, interface {}) error
func(*main.ActivityInStruct, context.Context) error
instead of
func(context.Context, interface {}) error
func(context.Context) error
*/
lm.fnStorage[m.Name] = m.Func
}
}
return nil
}
/* Register Activity from Public methods in struct */
activitiesStruct := &ActivityInStruct{
Bar: "I'm the Bar",
}
manager.RegisterStruct(activitiesStruct)
我无法使其正常工作,因为反射显示的方法签名是这样的:func(*main.ActivityInStruct, context.Context, interface {}) error
。
有什么办法可以解决这个问题吗?完整的playground在这里。
英文:
I can use an extra pair of eyes solving this challenge, playground here
Ultimate goal is to register functions and struct public functions into an activity manager and execute them via function name, so something along the lines of:
pool := map[string]interface{
"Sample": func(ctx context.Context) error,
"Sample2": func(ctx context.Context, args ...interface{}) error,
"SampleFromStruct": func(ctx context.Context) error,
"Sample2FromStruct": func(ctx context.Context, args ...interface{}) error,
}
the functions looks like:
func Sample(ctx context.Context) error {
fmt.Println("exec Sample")
return nil
}
func Sample2(ctx context.Context, args interface{}) error {
arguments := struct {
Foo string `json:"foo"`
Bar string `json:"bar"`
}{}
b, err := json.Marshal(args)
if err != nil {
return err
}
if err := json.Unmarshal(b, &arguments); err != nil {
return err
}
fmt.Println("exec Sample2 with args", arguments)
return nil
}
// and same but with struct
type ActivityInStruct struct {
Bar string
}
func (a *ActivityInStruct) SampleInStruct(ctx context.Context) error {
fmt.Println("Value of Bar", a.Bar)
return Sample(ctx)
}
func (a *ActivityInStruct) Sample2InStruct(ctx context.Context, args interface{}) error {
fmt.Println("Value of Bar", a.Bar)
return Sample2(ctx, args)
}
Said this, I got it working with functions with the followed implementation:
type activityManager struct {
fnStorage map[string]interface{}
}
func (lm *activityManager) Register(fn interface{}) error {
fnName := strings.Split((runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name()), ".")
name := fnName[len(fnName)-1]
lm.fnStorage[name] = fn
return nil
}
func (lm *activityManager) Exec(ctx context.Context, fnName string, args ...interface{}) error {
fn, ok := lm.fnStorage[fnName]
if !ok {
return fmt.Errorf("activity %s not found", fnName)
}
if signatureCtx, ok := fn.(func(context.Context) error); ok {
return signatureCtx(ctx)
}
if signatureWithArgument, ok := fn.(func(context.Context, interface{}) error); ok {
return signatureWithArgument(ctx, args[0])
}
return fmt.Errorf("signature for %s not supported", fnName)
}
so the Execution looks like this:
func NewManager() *activityManager {
return &activityManager{
fnStorage: map[string]interface{}{},
}
}
/*...*/
ctx := context.Background()
manager := NewManager()
manager.Register(Sample)
manager.Register(Sample2)
if err := manager.Exec(ctx, "Sample"); err != nil {
fmt.Println("Sample error", err.Error())
return
}
args1 := map[string]interface{}{
"foo": "isFoo",
"bar": "isBar",
}
if err := manager.Exec(ctx, "Sample2", args1); err != nil {
fmt.Println("Sample2 error", err.Error())
return
}
However, to register something like this:
func (lm *activityManager) RegisterStruct(fn interface{}) error {
t := reflect.TypeOf(fn)
for i := 0; i < t.NumMethod(); i++ {
m := t.Method(i)
if m.IsExported() {
/*
This won't work cause m.Type are
func(*main.ActivityInStruct, context.Context, interface {}) error
func(*main.ActivityInStruct, context.Context) error
instead of
func(context.Context, interface {}) error
func(context.Context) error
*/
lm.fnStorage[m.Name] = m.Func
}
}
return nil
}
/* Register Activity from Public methods in struct */
activitiesStruct := &ActivityInStruct{
Bar: "I'm the Bar",
}
manager.RegisterStruct(activitiesStruct)
I cant get this to work cause the reflection shows the method signature like this instead, func(*main.ActivityInStruct, context.Context, interface {}) error
Any idea how to go around that? the full playground is here
答案1
得分: 0
调用Value.Method来获取方法值。
func (lm *activityManager) RegisterStruct(fn interface{}) error {
v := reflect.ValueOf(fn)
t := v.Type()
for i := 0; i < t.NumMethod(); i++ {
m := t.Method(i)
if m.IsExported() {
lm.fnStorage[m.Name] = v.Method(i).Interface()
}
}
return nil
}
英文:
Call Value.Method to get the method value.
func (lm *activityManager) RegisterStruct(fn interface{}) error {
v := reflect.ValueOf(fn)
t := v.Type()
for i := 0; i < t.NumMethod(); i++ {
m := t.Method(i)
if m.IsExported() {
lm.fnStorage[m.Name] = v.Method(i).Interface()
}
}
return nil
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论