在Go中包装任意函数的封装器

huangapple go评论83阅读模式
英文:

Wrapper for arbitrary function in Go

问题

在Go语言中,是否可以创建一个包装器,用于任意函数,该包装器接受相同的参数并返回相同的值?

我不是在说要创建一个外观完全相同的包装器,它可能外观不同,但它应该解决这个问题。

例如,问题可能是创建一个任意函数的包装器,该包装器首先在缓存中查找函数调用的结果,只有在缓存未命中时才执行包装的函数。

英文:

Is it possible to create a wrapper for arbitrary function in Go that would take the same arguments and return the same value?

I'm not talking about the wrapper that would look exactly the same, it may look differently, but it should solve the problem.

For example the problem might be to create a wrapper of arbitrary function that first looks for the result of the function call in cache and only in case of cache miss executes the wrapped function.

答案1

得分: 3

这是使用reflect.MakeFunc的解决方案。这个特定的解决方案假设你的转换函数知道如何处理每种不同类型的函数。在这里可以看到它的运行效果:http://play.golang.org/p/7ZM4Hlcqjr

package main

import (
	"fmt"
	"reflect"
)

type genericFunction func(args []reflect.Value) (results []reflect.Value)

// A transformation takes a function f,
// and returns a genericFunction which should do whatever
// (ie, cache, call f directly, etc)
type transformation func(f interface{}) genericFunction

// Given a transformation, makeTransformation returns
// a function which you can apply directly to your target
// function, and it will return the transformed function
// (although in interface form, so you'll have to make
// a type assertion).
func makeTransformation(t transformation) func(interface{}) interface{} {
	return func(f interface{}) interface{} {
		// g is the genericFunction that transformation
		// produced. It will work fine, except that it
		// takes reflect.Value arguments and returns
		// reflect.Value return values, which is cumbersome.
		// Thus, we do some reflection magic to turn it
		// into a fully-fledged function with the proper
		// type signature.
		g := t(f)

		// typ is the type of f, and so it will also
		// be the type that of the function that we
		// create from the transformation (that is,
		// it's essentially also the type of g, except
		// that g technically takes reflect.Value
		// arguments, so we need to do the magic described
		// in the comment above).
		typ := reflect.TypeOf(f)

		// v now represents the actual function we want,
		// except that it's stored in a reflect.Value,
		// so we need to get it out as an interface value.
		v := reflect.MakeFunc(typ, g)
		return v.Interface()
	}
}

func main() {
	mult := func(i int) int { return i * 2 }

	timesTwo := func(f interface{}) genericFunction {
		return func(args []reflect.Value) (results []reflect.Value) {
			// We know we'll be getting an int as the only argument,
			// so this type assertion will always succeed.
			arg := args[0].Interface().(int)

			ff := f.(func(int) int)

			result := ff(arg * 2)
			return []reflect.Value{reflect.ValueOf(result)}
		}
	}

	trans := makeTransformation(timesTwo)

	// Since mult multiplies its argument by 2,
	// and timesTwo transforms functions to multiply
	// their arguments by 2, f will multiply its
	// arguments by 4.
	f := trans(mult).(func(int) int)

	fmt.Println(f(1))
}
英文:

Here's a solution using reflect.MakeFunc. This particular solution assumes that your transformation function knows what to do with every different type of function. Watch this in action: http://play.golang.org/p/7ZM4Hlcqjr

package main
import (
"fmt"
"reflect"
)
type genericFunction func(args []reflect.Value) (results []reflect.Value)
// A transformation takes a function f,
// and returns a genericFunction which should do whatever
// (ie, cache, call f directly, etc)
type transformation func(f interface{}) genericFunction
// Given a transformation, makeTransformation returns
// a function which you can apply directly to your target
// function, and it will return the transformed function
// (although in interface form, so you'll have to make
// a type assertion).
func makeTransformation(t transformation) func(interface{}) interface{} {
return func(f interface{}) interface{} {
// g is the genericFunction that transformation
// produced. It will work fine, except that it
// takes reflect.Value arguments and returns
// reflect.Value return values, which is cumbersome.
// Thus, we do some reflection magic to turn it
// into a fully-fledged function with the proper
// type signature.
g := t(f)
// typ is the type of f, and so it will also
// be the type that of the function that we
// create from the transformation (that is,
// it's essentially also the type of g, except
// that g technically takes reflect.Value
// arguments, so we need to do the magic described
// in the comment above).
typ := reflect.TypeOf(f)
// v now represents the actual function we want,
// except that it's stored in a reflect.Value,
// so we need to get it out as an interface value.
v := reflect.MakeFunc(typ, g)
return v.Interface()
}
}
func main() {
mult := func(i int) int { return i * 2 }
timesTwo := func(f interface{}) genericFunction {
return func(args []reflect.Value) (results []reflect.Value) {
// We know we'll be getting an int as the only argument,
// so this type assertion will always succeed.
arg := args[0].Interface().(int)
ff := f.(func(int) int)
result := ff(arg * 2)
return []reflect.Value{reflect.ValueOf(result)}
}
}
trans := makeTransformation(timesTwo)
// Since mult multiplies its argument by 2,
// and timesTwo transforms functions to multiply
// their arguments by 2, f will multiply its
// arguments by 4.
f := trans(mult).(func(int) int)
fmt.Println(f(1))
}

答案2

得分: 3

基于@joshlf13的想法和答案,但对我来说似乎更简单。

package main

import (
	"fmt"
	"reflect"
)

type (
	// 被包装的函数类型
	sumFuncT func(int, int) int

	// 包装函数类型
	wrappedSumFuncT func(sumFuncT, int, int) int
)

// 通用包装函数
// 数组的第一个元素是被包装的函数
// 其他元素是函数的参数
func genericWrapper(in []reflect.Value) []reflect.Value {
	// 在包装函数中进行有用的操作
	return in[0].Call(in[1:])
}

// 创建包装函数并将其设置为传递的函数指针
func createWrapperFunction(function interface{}) {
	fn := reflect.ValueOf(function).Elem()
	v := reflect.MakeFunc(reflect.TypeOf(function).Elem(), genericWrapper)
	fn.Set(v)
}

func main() {
	var wrappedSumFunc wrappedSumFuncT

	createWrapperFunction(&wrappedSumFunc)

	// 被包装的函数本身
	sumFunc := func(a int, b int) int {
		return a + b
	}

	result := wrappedSumFunc(sumFunc, 1, 3)
	fmt.Printf("结果是 %v", result)
}

以上是代码的翻译。

英文:

The answer based on @joshlf13 idea and answer, but seems more simple to me.
http://play.golang.org/p/v3zdMGfKy9

package main
import (
"fmt"
"reflect"
)
type (
// Type of function being wrapped
sumFuncT func(int, int) (int)
// Type of the wrapper function
wrappedSumFuncT func(sumFuncT, int, int) (int)
)
// Wrapper of any function
// First element of array is the function being wrapped
// Other elements are arguments to the function
func genericWrapper(in []reflect.Value) []reflect.Value {
// this is the place to do something useful in the wrapper
return in[0].Call(in[1:])
}
// Creates wrapper function and sets it to the passed pointer to function
func createWrapperFunction(function interface {}) {
fn := reflect.ValueOf(function).Elem()
v := reflect.MakeFunc(reflect.TypeOf(function).Elem(), genericWrapper)
fn.Set(v)
}
func main() {
var wrappedSumFunc wrappedSumFuncT
createWrapperFunction(&wrappedSumFunc)
// The function being wrapped itself
sumFunc := func (a int, b int) int {
return a + b
}
result := wrappedSumFunc(sumFunc, 1, 3)
fmt.Printf("Result is %v", result)
}

答案3

得分: 2

我最好的想法是使用函数定义并返回一个接口,之后需要进行类型断言:

func Wrapper(metaParams string, f func() (interface{}, string, error)) (interface{}, error) {
    // 你的包装器代码
    res, metaResults, err := f()
    // 你的包装器代码
    return res, err
}

然后使用这个函数需要稍微修改一下,使其像一个包装器一样工作:

resInterface, err := Wrapper("用于包装器的数据", func() (interface{}, string, error) {
    res, err := YourActualFuntion(whatever, params, needed)
    metaResults := "用于包装器的更多数据"
    return res, metaResults, err
}) // 注意这里没有调用 f()!传递函数本身,而不是其结果
if err != nil {
    // 处理错误
}
res, ok := resInterface.(actualType)
if !ok {
    // 处理类型断言失败
}

优点是这种方法比较通用,可以处理任何带有一个返回类型和错误的函数,而且不需要使用反射。

缺点是使用起来需要一些工作,因为它不是一个简单的包装器或装饰器。

英文:

The best I've come up with is to take a function def and return an interface, which will need type assertion afterwards:

func Wrapper(metaParams string, f func() (interface{}, string, error)) (interface{}, error) {
// your wrapper code
res, metaResults, err := f()
// your wrapper code
return res, err
}

Then to use this also takes a little work to function like a wrapper:

resInterface, err := Wrapper("data for wrapper", func() (interface{}, string, error) {
res, err := YourActualFuntion(whatever, params, needed)
metaResults := "more data for wrapper"
return res, metaResults, err
}) // note f() is not called here! Pass the func, not its results
if err != nil {
// handle it
}
res, ok := resInterface.(actualType)
if !ok {
// handle it
}

The upside is this is somewhat generic, can handle anything with 1 return type + error, and doesn't require reflection.

The downside is this takes a lot of work to use as it's not a simple wrapper or decorator.

答案4

得分: 2

基于之前的回答并利用Go的新泛型功能,我认为这可以实现得相当优雅(playground链接):

package main

import (
	"fmt"
	"reflect"
)

// 创建包装函数并将其设置为传递的函数指针
func wrapFunction[T any](function T) T {
	v := reflect.MakeFunc(reflect.TypeOf(function), func(in []reflect.Value) []reflect.Value {
		// 这是拦截调用的地方。
		fmt.Println("参数为:", in)

		f := reflect.ValueOf(function)
		return f.Call(in)
	})
	return v.Interface().(T)
}

func main() {
	// 被包装的函数本身
	sum := func(a int, b int) int {
		return a + b
	}
	wrapped := wrapFunction(sum)

	fmt.Printf("结果为 %v", wrapped(1, 3))
}
英文:

Building on previous answers and using Go's new generic capabilities, I believe this can be implemented quite elegantly (playground link):

package main

import (
	"fmt"
	"reflect"
)

// Creates wrapper function and sets it to the passed pointer to function
func wrapFunction[T any](function T) T {
	v := reflect.MakeFunc(reflect.TypeOf(function), func(in []reflect.Value) []reflect.Value {
		// This is the place to intercept your call.
		fmt.Println("Params are:", in)

		f := reflect.ValueOf(function)
		return f.Call(in)
	})
	return v.Interface().(T)
}

func main() {
	// The function being wrapped itself
	sum := func(a int, b int) int {
		return a + b
	}
	wrapped := wrapFunction(sum)

	fmt.Printf("Result is %v", wrapped(1, 3))
}

答案5

得分: 0

像这样吗?

var cache = make(map[string]string)
func doStuff(key string) {
//做一些耗时的操作
cache[key] = value
return value
}
func DoStuff(key string) {
if v, ok := cache[key]; ok {
return v
}
return doStuff(key)
}
英文:

Like this?

var cache = make(map[string]string)
func doStuff(key string) {
//do-something-that-takes-a-long-time
cache[key] = value
return value
}
fun DoStuff(key string) {
if v, ok := cache[key]; ok {
return v
}
return doStuff(key)
}

huangapple
  • 本文由 发表于 2014年4月19日 13:48:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/23166411.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定