web-dev-qa-db-ja.com

パニック:反映:ゼロ値でのreflect.Value.Callの呼び出し

ユーザー入力に基づいて動的な関数呼び出しを行うために、リフレクションを使用しようとしています。私はそのようなユーザー入力を収集しています:

func main() {
    for {
        reader := bufio.NewReader(os.Stdin)
        fmt.Print("> ")
        command, _ := reader.ReadString('\n')
        runCommandFromString(command)
    }
}

これは、コマンドが解析される方法です

func stringCommandParser(cmd string) *Command {
    cmdParts := strings.Split(
        strings.TrimSpace(cmd),
        " ",
    )
    return &Command{
        Name: cmdParts[0],
        Args: cmdParts[1:],
    }
}

func runCommandFromString(cmd string) {
    command := stringCommandParser(cmd)

    c := &Commander{}
    f := reflect.ValueOf(&c).MethodByName(command.Name)
    inputs := []reflect.Value{reflect.ValueOf(command)}
    f.Call(inputs)
}

Commands.goファイルは次のようになります

type Command struct {
    Name string
    Args []string
}

type Commander struct {}

func (c Commander) Hello(cmd Command) {
    fmt.Println("Meow", cmd.Args)
}

プログラムを実行すると、プロンプトが表示され、「Hello」というコマンドを「world」というパラメーターで実行します。 「ニャー[ワールド]」みたいなものが出てくると思います。このような:

> Hello world
Meow world

代わりに、私が得るのは次のようなパニックです:

> Hello world
panic: reflect: call of reflect.Value.Call on zero Value

goroutine 1 [running]:
panic(0x123360, 0xc420014360)
    /Users/parris/.gvm/gos/go1.7.4/src/runtime/panic.go:500 +0x1a1
reflect.flag.mustBe(0x0, 0x13)
    /Users/parris/.gvm/gos/go1.7.4/src/reflect/value.go:201 +0xae
reflect.Value.Call(0x0, 0x0, 0x0, 0xc420055e00, 0x1, 0x1, 0x0, 0x0, 0x0)
    /Users/parris/.gvm/gos/go1.7.4/src/reflect/value.go:300 +0x38
main.runCommandFromString(0xc42000c8a0, 0xc)
    /Users/parris/Projects/distro/main/utils.go:26 +0x1a0
main.main()
    /Users/parris/Projects/distro/main/main.go:14 +0x149
exit status 2

どうすれば修正できますか?また、何が起こっていますか?

7
Parris

使用する前に、_reflect.ValueOf_が返すものを確認することをお勧めします。

この場合、reflectはエクスポートされていない関数を見ることができないため、nil値を取得しています。 helloは小文字で始まるため、エクスポートされません。

また、ここではポインタの数が多すぎます。reflect.ValueOf(c)を実行する必要があると思います。同時に多くのことを実行してデバッグしています。簡単な作業例を作成し、そこからデバッグを続行します。

これは私にとってはうまくいきます: https://play.golang.org/p/Yd-WDRzura

4
Art

コードに2つの変更を加えるだけです。最初にreflect.ValueOf(&c)をreflect.ValueOf(c)に変更します。次にreflect.ValueOf(command)をreflect.ValueOf(* command)に変更します。

これはコードです workingcode

3
legacy_shield