diff options
author | Yorhel <git@yorhel.nl> | 2012-04-09 09:56:03 +0200 |
---|---|---|
committer | Yorhel <git@yorhel.nl> | 2012-04-09 09:56:03 +0200 |
commit | 7bdb9d948bf07f56fedd6305ae146de59f51610b (patch) | |
tree | 972f9e3c4dacd6c9d8c9a68dd0afd3d3f71d178d | |
parent | 89cb48caf91a8abe6d483e85e94bc18a64926855 (diff) |
rpc: Allow capturing remaining elements using variadic methods
-rw-r--r-- | rpc.go | 29 | ||||
-rw-r--r-- | rpc_test.go | 38 |
2 files changed, 58 insertions, 9 deletions
@@ -13,10 +13,10 @@ import "reflect" // // The method type must conform to the following rules: // - It should be exported (name must start with an uppercase letter) -// - It should not have any out parameters (return values) +// - It should not have any return values // - It may have any number of arguments, as long as their types are allowed. -// - The method may not be variadic // - The first argument may be of type *tanja.Message +// - If the method is variadic, its last argument must be of type ...Element // - Any other arguments must be of a type accepted by El() // // Methods that do not conform to these rules can not be registered and f() @@ -35,7 +35,13 @@ import "reflect" // exist in the received tuple or if the element can not be converted into the // type that the method accepts, the method will not be called. // +// If the method is variadic, any remaining elements in the tuple (after those +// captured by any previous arguments) will be passed through to the last +// argument. +// // Returns the number of patters registered. +// +// TODO: Examples are required to really understand the use of this method. func (s *Session) RegRPC(obj interface{}, f func(string) Tuple) int { objv := reflect.ValueOf(obj) objt := objv.Type() @@ -58,7 +64,11 @@ func (s *Session) RegRPC(obj interface{}, f func(string) Tuple) int { willReply = true j++ } - for ; j < mt.NumIn(); j++ { + end := mt.NumIn() + if mt.IsVariadic() { + end-- + } + for ; j < end; j++ { pat = append(pat, El(nil)) } // Generate callback and register @@ -122,7 +132,11 @@ func regRPCGenCallback(mt reflect.Type, mv reflect.Value, offset int, willReply arg++ } off := offset - for arg < mt.NumIn() { + end := mt.NumIn() + if mt.IsVariadic() { + end-- + } + for arg < end { args[arg] = regRPCValue(mt.In(arg), msg.Tup[off]) if !args[arg].IsValid() { msg.Close() @@ -131,6 +145,11 @@ func regRPCGenCallback(mt reflect.Type, mv reflect.Value, offset int, willReply off++ arg++ } - mv.Call(args) + if mt.IsVariadic() { + args[arg] = reflect.ValueOf(msg.Tup[off:]) + mv.CallSlice(args) + } else { + mv.Call(args) + } } } diff --git a/rpc_test.go b/rpc_test.go index 1a06b9e..b26751b 100644 --- a/rpc_test.go +++ b/rpc_test.go @@ -12,6 +12,7 @@ type sT struct { thiscalled int replycalled int closecalled int + varcalled int } var testmap map[string]Element = map[string]Element{ @@ -43,6 +44,22 @@ func (t *sT) ExportThis(a float32, b uint8, c string, d bool, e map[string]Eleme t.thiscalled++ } +func (t *sT) ExportVariadic(a int, e ...Element) { + t.varcalled++ + err := true + switch t.varcalled { + case 1: + err = a != 10 || len(e) != 1 || e[0].String() != "extra" + case 2: + err = a != 2 || len(e) != 0 + case 3: + err = a != 3 || !reflect.DeepEqual(e, []Element{El(testmap)}) + } + if err { + t.tst.Errorf("Unexpected variadic argument(%d): %d, %#v", t.varcalled, a, e) + } +} + func (t *sT) ExportReply(m *Message, s string) { if s != "10" { t.tst.Errorf("ExportReply string argument is '%s', expected '%s'", s, "argument") @@ -77,8 +94,8 @@ func TestRPC(tst *testing.T) { } return nil }) - if n != 3 { - tst.Fatalf("Number of exported methods is %d, expected %d", n, 3) + if n != 4 { + tst.Fatalf("Number of exported methods is %d, expected %d", n, 4) } go func() { @@ -89,7 +106,7 @@ func TestRPC(tst *testing.T) { ses.Send(false, "prefix", 2, "close", 1) ses.Send(false, "prefix", 1, "Close", 1) - // Try replying + // Try replying (tuple should be received by This, Close, Reply and Variadic) r := ses.Send(true, "prefix", 1, nil, 10, "extra") if t := <-r.Chan(); !reflect.DeepEqual(t, Tup(2)) || <-r.Chan() != nil { tst.Error("Received invalid reply") @@ -103,9 +120,19 @@ func TestRPC(tst *testing.T) { } r.Close() + // Variadic test + ses.Send(false, "prefix", 1, "variadic", 2) + ses.Send(false, "prefix", 1, "variadic", 3, testmap) + // Testing various types ses.Send(false, "prefix", 1, "this", 10.5, "42", "str", "some_true_value", testmap, testslice) + // Shouldn't match + ses.Send(false, "prefix", 1, "this", []Element{}, "42", "str", "some_true_value", testmap, testslice) + ses.Send(false, "prefix", 1, "this", 10.5, nil, "str", "some_true_value", testmap, testslice) + ses.Send(false, "prefix", 1, "this", 10.5, "42", "str", "some_true_value", testmap, 0) + ses.Send(false, "prefix", 1, "this", 10.5, "42", "str", "some_true_value", 1, testslice) + // Actual close ses.Send(false, "prefix", 1, "close", "really!") // Shouldn't be received @@ -120,6 +147,9 @@ func TestRPC(tst *testing.T) { tst.Errorf("Close is called %d times, expected %d", obj.closecalled, 3) } if obj.replycalled != 1 { - tst.Errorf("Close is called %d times, expected %d", obj.replycalled, 1) + tst.Errorf("Reply is called %d times, expected %d", obj.replycalled, 1) + } + if obj.varcalled != 3 { + tst.Errorf("Variadic is called %d times, expected %d", obj.varcalled, 3) } } |