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?
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
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.
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
...
}
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
}
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)
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."
> 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
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
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.
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
Is there any way to call via reflection a method whose signature isthe 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.
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.
I think it should support *unpack* operator? Just like the python's
*nkw or **kw style.
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
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
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.