How to call methods which take ... arguments using reflection?

3,505 views
Skip to first unread message

Esko Luontola

unread,
Feb 1, 2010, 3:34:11 PM2/1/10
to golang-nuts
I have a problem that how pass parameters to call a method which
takes ... arguments, when the parameters must be read dynamically from
a field.

In the following code, if the call to Sprintf would be in the NewFoo
method, then I could pass args directly, because the language spec has
a special case for it. But now that Sprintf is called in the String
method, how can I do the same? (In Java this would be easy, because
there ... is just syntactic sugar for an array parameter, but with Go
I'm lost.)

type Foo struct {
format string
args interface{}
}

func NewFoo(format string, args ...) *Foo {
return &Foo{format, args}
}

func (this *Foo) String() string {
return fmt.Sprintf(this.format, this.args) // doesn't work
}

And some test code:

foo := NewFoo("%v * %v = %v", 1, 2, 3)
print(foo.String())

Right now the above code will print "{1 2 3} * %vmissing = %vmissing".
How should it be changed, so that it will print "1 * 2 = 3" and the
call to Sprintf is still made lazily in the String() method?

Mue

unread,
Feb 1, 2010, 4:34:09 PM2/1/10
to golang-nuts
Hi,

I've got a little helping function for it:

func ArgsToInterfaces(args ...) []interface{} {
av := reflect.NewValue(args).(*reflect.StructValue)
ia := make([]interface{}, av.NumField())

for i := 0; i < av.NumField(); i++ {
ia[i] = av.Field(i).Interface()
}

return ia
}

It's part of my Common Go Library: http://code.google.com/p/tideland-cgl/

mue

Esko Luontola

unread,
Feb 1, 2010, 7:04:38 PM2/1/10
to golang-nuts
On Feb 1, 11:34 pm, Mue <m...@tideland.biz> wrote:
> I've got a little helping function for it:
>
> func ArgsToInterfaces(args ...) []interface{} {

That doesn't help. The following code still does not pass the
parameters right to fmt.Sprintf(). The problem is not in reading the
varargs, but in calling another method which takes varargs.

type Foo struct {
format string
args []interface{}
}

func NewFoo(format string, args ...) *Foo {

return &Foo{format, ArgsToInterfaces(args)}
}

func (this *Foo) String() string {
return fmt.Sprintf(this.format, this.args) // doesn't work
}

Given the above code, then

foo := NewFoo("%v * %v = %v", 1, 2, 3)
print(foo.String())

will print "[1 2 3] * %vmissing = %vmissing" which is not what is
intended.

Steven Blenkinsop

unread,
Feb 1, 2010, 7:23:04 PM2/1/10
to Esko Luontola, golang-nuts
On 2010-02-01, at 3:34 PM, Esko Luontola <esko.l...@gmail.com>
wrote:

you could have call Sprintf in NewFoo and store the result. If,
however, you want to have it done lazily, you can use a closure (func
variable). Here's an example:
http://ideone.com/jGsrpvbo

Here I have a func type, but you could have it as a member of a struct
type if you have more data to store:

type Box struct {
lazySprintf func() string
...
}

Russ Cox

unread,
Feb 1, 2010, 8:47:19 PM2/1/10
to Esko Luontola, golang-nuts
type Foo struct {
       format string
       args interface{}
}

func NewFoo(format string, args ...) *Foo {
       return &Foo{format, args}
}

func (this *Foo) String() string {
       return fmt.Sprintf(this.format, this.args) // doesn't work
}

There's no language mechanism for this, nor is there a Vsprintf func.
The special case that avoids re-wrapping arguments is tied to 
the parameter itself, not the type system.  But you can fake it:

package main
import "fmt"
func vsprintf(f string, args []interface{}, dummy ...interface{}) string {
dummy = args
return fmt.Sprintf(f, dummy)
}
func sprint(f string, arg interface{}) string {
return vsprintf(f, []interface{}{arg})
}
func main() {
fmt.Println(sprint("%d", 5))
}

It's hardly elegant, but it works, and the need is rare.

Russ

Esko Luontola

unread,
Feb 1, 2010, 9:03:47 PM2/1/10
to golang-nuts
Closure seems like the best fit for this situation. It works because
of the special case mentioned in http://golang.org/doc/go_spec.html#Passing_arguments_to_..._parameters

But for the general case, I would like to know that how to call a
varargs method using reflection or other means. For example, if I get
some values from a channel and store them in an array, how can I pass
each of those array elements as elements of the ... parameter?

Whether it's possible or not, at least I'll learn something new about
Go (and if it's not possible, it would be good to be fixed). At least
this code will not work - the Call method panics with "FuncValue:
wrong argument count".

f := reflect.NewValue(fmt.Sprintf).(*reflect.FuncValue)
args := make([]reflect.Value, 3)
args[0] = reflect.NewValue("%v x %v")
args[1] = reflect.NewValue(1)
args[2] = reflect.NewValue(2)
result := f.Call(args)
fmt.Println(result)

Esko Luontola

unread,
Feb 1, 2010, 9:27:35 PM2/1/10
to golang-nuts
On Feb 2, 3:47 am, Russ Cox <r...@golang.org> wrote:
>  func vsprintf(f string, args []interface{}, dummy ...interface{}) string {
> dummy = args
> return fmt.Sprintf(f, dummy)}

Looks interesting. Does the compiler generate there some conversion
code, or is the []interface{} passed to Sprintf as-is, or how does it
work?

Shame that I can't try that yet - the spec says: "Formal parameters
with specified type ... T are not implemented."

Rob 'Commander' Pike

unread,
Feb 1, 2010, 9:42:58 PM2/1/10
to Esko Luontola, golang-nuts

On Feb 2, 2010, at 1:27 PM, Esko Luontola wrote:

> Shame that I can't try that yet - the spec says: "Formal parameters
> with specified type ... T are not implemented."

They are at tip.

-rob

Rob 'Commander' Pike

unread,
Feb 1, 2010, 9:58:22 PM2/1/10
to Esko Luontola, golang-nuts
package main

import "reflect"

func x(fmt string, a ...interface{}) {
println(len(a))
println(fmt)
for i, v := range a {
println(i, ":", v.(int))
}
}

func main() {
x("hi", 1, 2, 3)
fv := reflect.NewValue(x).(*reflect.FuncValue)
varargs := make([]interface{}, 3)
varargs[0] = 4
varargs[1] = 5
varargs[2] = 6
args := make([]reflect.Value, 2)
args[0] = reflect.NewValue("ho")
args[1] = reflect.NewValue(varargs)
fv.Call(args)
}

prints

3
hi
0 : 1
1 : 2
2 : 3
3
ho
0 : 4
1 : 5
2 : 6


-rob

Message has been deleted

Steven

unread,
Feb 2, 2010, 12:11:31 AM2/2/10
to Rob 'Commander' Pike, Esko Luontola, golang-nuts
Or you could use Rob's hack that lets you take advantage of the special case:

package main

import "fmt"

func main() {
varargs := make([]interface{}, 3)

varargs[0] = 1
varargs[1] = "Two"
varargs[2] = 3

func(dummy ...interface{}) {
dummy = varargs
fmt.Println(dummy) // or any other ...interface{} function
}()
}

prints: 
1 Two 3

This avoids having to wrap the function in a FuncValue. Is the hack all that bad? Its intention is clear...

Steven

unread,
Feb 2, 2010, 1:20:36 AM2/2/10
to Rob 'Commander' Pike, Esko Luontola, golang-nuts
Whoops, meant Russ's hack. Sorry.

Esko Luontola

unread,
Feb 2, 2010, 6:55:44 AM2/2/10
to golang-nuts
On Feb 2, 4:58 am, "Rob 'Commander' Pike" <r...@google.com> wrote:
> func x(fmt string, a ...interface{}) {

Is there any way to call via reflection a method whose signature is
the following?

func x(fmt string, a ...)

If I understood it from the spec, that method will take a struct as
the ... parameter and not an array.

Ian Lance Taylor

unread,
Feb 2, 2010, 10:34:09 AM2/2/10
to Esko Luontola, golang-nuts
Esko Luontola <esko.l...@gmail.com> writes:

There currently is no way to do that, because there is currently no
way to build an arbitrary type at runtime.

If anybody wants to sort out the issues required to build types at
runtime, I think it would be a logical feature to add to the reflect
package.

Ian

Russ Cox

unread,
Feb 2, 2010, 1:29:45 PM2/2/10
to Esko Luontola, golang-nuts


On Tue, Feb 2, 2010 at 03:55, Esko Luontola <esko.l...@gmail.com> wrote:
Is there any way to call via reflection a method whose signature is
the following?

  func x(fmt string, a ...)

If I understood it from the spec, that method will take a struct as
the ... parameter and not an array.

For the purposes of reflect.Call, the latest changes make it
possible to treat the arg 'a' as having type interface{}.  
If you create a reflect.Value that has a type interface{}
that itself contains a struct, then you can pass it as the
second arg in a Call.  (It's pretty clunky, though - figuring
out how to get a reflect.Value that is a *reflect.InterfaceValue
is left as an exercise; read the tests for the answer - and the
unadorned ... may well go away.)  And as Ian said, you still
have to define the struct type itself at compile time.

reflect.Call will also let you pass a []T for a ...T argument.
If you pass []interface{} for a ...interface{} that lets you do
much more dynamic calls without needing to create struct
types at run time.

I don't think we've actually decided that reflect.Call should
handle the ... arguments this way, but the recent changes
made it just work, and I don't see much of a reason to
prohibit it.

Russ

MC.Spring

unread,
Feb 3, 2010, 10:31:42 AM2/3/10
to golang-nuts
There seems no way to do this.
According to go-spec you should not pass func's ... argument as
another func's ... argument, it's not wrapped again.

I just take a look at the fmt package source file of the Fprintf
(io.Writer, format, v...) method implamention, it takes the
*reflect.StructValue as possible. In func (p *pp) doprintf(format
string, v *reflect.StructValue),they just for loop and proccess every
format each time.

MC.Spring

unread,
Feb 3, 2010, 10:36:01 AM2/3/10
to golang-nuts
>
> There currently is no way to do that, because there is currently no
> way to build an arbitrary type at runtime.
>
> If anybody wants to sort out the issues required to build types at
> runtime, I think it would be a logical feature to add to the reflect
> package.
>
> Ian

I think it should support *unpack* operator? Just like the python's
*nkw or **kw style.

Russ Cox

unread,
Feb 3, 2010, 12:03:35 PM2/3/10
to MC.Spring, golang-nuts
On Wed, Feb 3, 2010 at 07:31, MC.Spring <here...@gmail.com> wrote:
> There seems no way to do this.

I posted code earlier in the thread that solves the original
post's problem, so there definitely is a way to do it.

> According to go-spec you should not pass func's ... argument as
> another func's ... argument, it's not wrapped again.

It's not wrapped again precisely so that you *can* pass
one ... argument to another.

Russ

Dave Miller

unread,
Feb 4, 2010, 3:22:34 PM2/4/10
to golang-nuts
This discussion might help:
http://groups.google.com/group/golang-nuts/browse_thread/thread/3386e1648c363bd3/146b8db8aeb4e159#146b8db8aeb4e159

make sure you are using tip, and looking at the distributed source,
not the source on golang.org, eg:

golang.org:
func Printf(format string, v ...) (n int, errno os.Error)
tip:
func Printf(format string, a ...interface{}) (n int, errno os.Error)

hope this helps

-dave

Daniel Jo

unread,
Mar 16, 2013, 12:41:54 PM3/16/13
to cfan...@gmail.com, golan...@googlegroups.com
reflect.Call automatically creates the variadic from the slice of parameters.


-Daniel

On Sat, Mar 16, 2013 at 4:10 AM, <cfan...@gmail.com> wrote:
 

I got code from log.Printf


		l.Output(2, fmt.Sprintf(format, v...))

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 



--
--Ostsol

http://cheesesun.blogspot.com/
Reply all
Reply to author
Forward
0 new messages