Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to define a function type which accepts any number of arguments in Go?

Tags:

go

I try to write a function which takes any other function and wraps a new function around it. This is what I have tried so far:

package main

import (
    "fmt"
)

func protect (unprotected func (...interface{})) (func (...interface{})) {
    return func (args ...interface{}) {
        fmt.Println ("protected");
        unprotected (args...);
    };
}

func main () {
    a := func () {
        fmt.Println ("unprotected");
    };
    b := protect (a);
    b ();
}

When I compile this I get the error:

cannot use a (type func()) as type func(...interface { }) in function argument

Why is a function without arguments not compatible to a function with a variable number of arguments? What can I do to make them compatible?

Update: The protected function should be compatible with the original:

func take_func_int_int (f func (x int) (y int)) (int) {
    return f (1)
}

func main () {

    a := func (x int) (y int) {
        return 2 * x
    }
    b := protect (a)

    take_func_int_int (a)
    take_func_int_int (b)
}
like image 803
ceving Avatar asked May 18 '11 15:05

ceving


People also ask

How do you pass variable number of arguments in Golang?

In Golang, it is possible to pass a varying number of arguments of the same type as referenced in the function signature. To declare a variadic function, the type of the final parameter is preceded by an ellipsis, "...", which shows that the function may be called with any number of arguments of this type.

Which function accepts a variable number of arguments?

In mathematics and in computer programming, a variadic function is a function of indefinite arity, i.e., one which accepts a variable number of arguments. Support for variadic functions differs widely among programming languages.

How do you pass an argument to a function in Go?

Golang supports two different ways to pass arguments to the function i.e. Pass by Value or Call by Value and Pass By Reference or Call By Reference. By default, Golang uses the call by value way to pass the arguments to the function.

How many arguments can a function accept?

Except for functions with variable-length argument lists, the number of arguments in a function call must be the same as the number of parameters in the function definition. This number can be zero. The maximum number of arguments (and corresponding parameters) is 253 for a single function.


1 Answers

Types are pretty concrete in Go. You could try

a := func(_ ...interface{}) {
    fmt.Println("unprotected")
}

func (...interface{}) does not mean "any function that takes any number of any kind of arguments", it means "only a function which takes a variable number of interface{} arguments"

Alternatively rather than func(...interface{}) you can just use interface{} and the reflect package. See http://github.com/hoisie/web.go for an example.

EDIT: Specifically, this:

package main

import (
    "fmt"
    "reflect"
)

func protect(oldfunc interface{}) (func (...interface{})) {
    if reflect.TypeOf(oldfunc).Kind() != reflect.Func {
        panic("protected item is not a function")
    }
    return func (args ...interface{}) {
        fmt.Println("Protected")
        vargs := make([]reflect.Value, len(args))
        for n, v := range args {
            vargs[n] = reflect.ValueOf(v)
        }
        reflect.ValueOf(oldfunc).Call(vargs)
    }
}

func main() {
    a := func() {
        fmt.Println("unprotected")
    }
    b := func(s string) {
        fmt.Println(s)
    }
    c := protect(a)
    d := protect(b)
    c()
    d("hello")
}

Ouput is

Protected
unprotected
Protected
hello

EDIT: To answer the update

Like I said above, types are pretty concrete in Go. The protect function returns a type func(...interface{}) which will never be assignable to func(int)int. I think you're probably either over-engineering your problem or misunderstanding it. However, here's a highly discouraged code snippet that would make it work.

First change protect to also return values:

func protect(oldfunc interface{}) (func (...interface{}) []interface{}) {
    if reflect.TypeOf(oldfunc).Kind() != reflect.Func {
        panic("protected item is not a function")
    }
    return func (args ...interface{}) []interface{} {
        fmt.Println("Protected")
        vargs := make([]reflect.Value, len(args))
        for n, v := range args {
            vargs[n] = reflect.ValueOf(v)
        }
        ret_vals := reflect.ValueOf(oldfunc).Call(vargs)
        to_return := make([]interface{}, len(ret_vals))
        for n, v := range ret_vals {
                to_return[n] = v.Interface()
        }
        return to_return
    }
}

Then make a convert function:

func convert(f func(...interface{}) (func(int) int) {
    return func(x int) int {
        r := f(x)
        return r[0].(int)
    }
}

Then your call would look like

take_func_int_int(convert(b))

But I promise this isn't what you actually want to do.

Step back and try to rework the problem. I've completely killed type-safety in these examples. What are you trying to accomplish?

like image 176
cthom06 Avatar answered Oct 16 '22 22:10

cthom06