1
0
mirror of https://github.com/golang/go synced 2024-11-20 05:34:40 -07:00

net/rpc: give hint to pass a pointer to Register

Fixes #4325.

R=golang-dev, r
CC=golang-dev
https://golang.org/cl/6819081
This commit is contained in:
Shenghou Ma 2012-11-07 05:03:16 +08:00
parent 91651c1844
commit 0e3f4fdb52
2 changed files with 58 additions and 20 deletions

View File

@ -261,8 +261,30 @@ func (server *Server) register(rcvr interface{}, name string, useName bool) erro
s.method = make(map[string]*methodType) s.method = make(map[string]*methodType)
// Install the methods // Install the methods
for m := 0; m < s.typ.NumMethod(); m++ { s.method = suitableMethods(s.typ, true)
method := s.typ.Method(m)
if len(s.method) == 0 {
str := ""
// To help the user, see if a pointer receiver would work.
method := suitableMethods(reflect.PtrTo(s.typ), false)
if len(method) != 0 {
str = "rpc.Register: type " + sname + " has no exported methods of suitable type (hint: pass a pointer to value of that type)"
} else {
str = "rpc.Register: type " + sname + " has no exported methods of suitable type"
}
log.Print(str)
return errors.New(str)
}
server.serviceMap[s.name] = s
return nil
}
// suitableMethods returns suitable Rpc methods of typ, it will report
// error using log if reportErr is true.
func suitableMethods(typ reflect.Type, reportErr bool) map[string]*methodType {
methods := make(map[string]*methodType)
for m := 0; m < typ.NumMethod(); m++ {
method := typ.Method(m)
mtype := method.Type mtype := method.Type
mname := method.Name mname := method.Name
// Method must be exported. // Method must be exported.
@ -271,46 +293,51 @@ func (server *Server) register(rcvr interface{}, name string, useName bool) erro
} }
// Method needs three ins: receiver, *args, *reply. // Method needs three ins: receiver, *args, *reply.
if mtype.NumIn() != 3 { if mtype.NumIn() != 3 {
log.Println("method", mname, "has wrong number of ins:", mtype.NumIn()) if reportErr {
log.Println("method", mname, "has wrong number of ins:", mtype.NumIn())
}
continue continue
} }
// First arg need not be a pointer. // First arg need not be a pointer.
argType := mtype.In(1) argType := mtype.In(1)
if !isExportedOrBuiltinType(argType) { if !isExportedOrBuiltinType(argType) {
log.Println(mname, "argument type not exported:", argType) if reportErr {
log.Println(mname, "argument type not exported:", argType)
}
continue continue
} }
// Second arg must be a pointer. // Second arg must be a pointer.
replyType := mtype.In(2) replyType := mtype.In(2)
if replyType.Kind() != reflect.Ptr { if replyType.Kind() != reflect.Ptr {
log.Println("method", mname, "reply type not a pointer:", replyType) if reportErr {
log.Println("method", mname, "reply type not a pointer:", replyType)
}
continue continue
} }
// Reply type must be exported. // Reply type must be exported.
if !isExportedOrBuiltinType(replyType) { if !isExportedOrBuiltinType(replyType) {
log.Println("method", mname, "reply type not exported:", replyType) if reportErr {
log.Println("method", mname, "reply type not exported:", replyType)
}
continue continue
} }
// Method needs one out. // Method needs one out.
if mtype.NumOut() != 1 { if mtype.NumOut() != 1 {
log.Println("method", mname, "has wrong number of outs:", mtype.NumOut()) if reportErr {
log.Println("method", mname, "has wrong number of outs:", mtype.NumOut())
}
continue continue
} }
// The return type of the method must be error. // The return type of the method must be error.
if returnType := mtype.Out(0); returnType != typeOfError { if returnType := mtype.Out(0); returnType != typeOfError {
log.Println("method", mname, "returns", returnType.String(), "not error") if reportErr {
log.Println("method", mname, "returns", returnType.String(), "not error")
}
continue continue
} }
s.method[mname] = &methodType{method: method, ArgType: argType, ReplyType: replyType} methods[mname] = &methodType{method: method, ArgType: argType, ReplyType: replyType}
} }
return methods
if len(s.method) == 0 {
s := "rpc Register: type " + sname + " has no exported methods of suitable type"
log.Print(s)
return errors.New(s)
}
server.serviceMap[s.name] = s
return nil
} }
// A value sent as a placeholder for the server's response value when the server // A value sent as a placeholder for the server's response value when the server

View File

@ -349,6 +349,7 @@ func testServeRequest(t *testing.T, server *Server) {
type ReplyNotPointer int type ReplyNotPointer int
type ArgNotPublic int type ArgNotPublic int
type ReplyNotPublic int type ReplyNotPublic int
type NeedsPtrType int
type local struct{} type local struct{}
func (t *ReplyNotPointer) ReplyNotPointer(args *Args, reply Reply) error { func (t *ReplyNotPointer) ReplyNotPointer(args *Args, reply Reply) error {
@ -363,19 +364,29 @@ func (t *ReplyNotPublic) ReplyNotPublic(args *Args, reply *local) error {
return nil return nil
} }
func (t *NeedsPtrType) NeedsPtrType(args *Args, reply *Reply) error {
return nil
}
// Check that registration handles lots of bad methods and a type with no suitable methods. // Check that registration handles lots of bad methods and a type with no suitable methods.
func TestRegistrationError(t *testing.T) { func TestRegistrationError(t *testing.T) {
err := Register(new(ReplyNotPointer)) err := Register(new(ReplyNotPointer))
if err == nil { if err == nil {
t.Errorf("expected error registering ReplyNotPointer") t.Error("expected error registering ReplyNotPointer")
} }
err = Register(new(ArgNotPublic)) err = Register(new(ArgNotPublic))
if err == nil { if err == nil {
t.Errorf("expected error registering ArgNotPublic") t.Error("expected error registering ArgNotPublic")
} }
err = Register(new(ReplyNotPublic)) err = Register(new(ReplyNotPublic))
if err == nil { if err == nil {
t.Errorf("expected error registering ReplyNotPublic") t.Error("expected error registering ReplyNotPublic")
}
err = Register(NeedsPtrType(0))
if err == nil {
t.Error("expected error registering NeedsPtrType")
} else if !strings.Contains(err.Error(), "pointer") {
t.Error("expected hint when registering NeedsPtrType")
} }
} }