Golang标志重新定义

时间:2018-03-09 12:06:53

标签: go flags

我有可以在多个组件中定义的标志。不可能知道组件是否已经定义了一个标志,如果两个组件Foo和Bar需要标志Zed,但是Foo和Bar必须定义标志Zed。在这种情况下,Go的标志将发生恐慌并重新定义错误标志。

有没有办法可选择初始化一个标志,只要它还没有在其他地方初始化?或者,检查是否已经设置了一个标志,然后检查是否有标志值?

我认为这样做的唯一方法就是这样:

var fooFlag *string

func init() {
    if flag.Lookup("foo") == nil {
        fooFlag = flag.String("foo", "", "some flag foo")
    }
}

func initFlags() {
    if fooFlag == nil && flag.Lookup("foo") != nil {
        temp := (*flag.Lookup("foo")).Value.(flag.Getter).Get().(string)
        fooFlag = &temp
    }
}

func main() {
    flag.Parse()
    initFlags()
    ...
}

但这非常难看(而且不确定这会有多好用)。有关更好的方法的想法吗?

1 个答案:

答案 0 :(得分:3)

不幸的是,标准库没有更简单的方法,因为每个标志只能注册一次。我们将了解如何简化代码,以及我们可以采取哪些措施来避免这种模式。

Flag.FlagSet没有帮助

flag.FlagSet的使用不是解决方案,它旨在支持子命令(请参阅此问题以获取示例:Defining Independent FlagSets in GoLang)。

要说明它没有帮助的原因:让我们假设您有2个标志集(一个可能是flag包的细分)。第一个标志集可以包含"foo""bar"标志,第二个标志集可以包含"foo""baz"标志。如果用户为所有3提供值,会发生什么?对第一个标志集的FlagSet.Parse()的调用将报告错误:

  提供了

标志但未定义:-baz

同样,第二个flagset的FlagSet.Parse()也会报告未定义-bar的错误。

简化您的代码

如果您想保留自己的方法,请注意您可以使用flag vars(flag.StringVar())来简化代码,然后在initFlags()中,您只需获得string值即可像这样分配你的标志变量:

var foo string

func init() {
    if flag.Lookup("foo") == nil {
        flag.StringVar(&foo, "foo", "", "this is foo")
    }
}

func initFlags() {
    // "foo" does exist: if noone else registered it, then we did
    foo = flag.Lookup("foo").Value.(flag.Getter).Get().(string)
}

func main() {
    flag.Parse()
    initFlags()
    ...
}

此外,如果你想处理这样的多个标志,那么你应该创建帮助函数,不要重复(现在更少"丑陋")代码:

func regStringVar(p *string, name string, value string, usage string) {
    if flag.Lookup(name) == nil {
        flag.StringVar(p, name, value, usage)
    }
}

func getStringFlag(name string) string {
    return flag.Lookup(name).Value.(flag.Getter).Get().(string)
}

使用上面的助手,注册3个标志变量就像:

var foo, bar, baz string

func init() {
    regStringVar(&foo, "foo", "", "this is foo")
    regStringVar(&bar, "bar", "", "this is bar")
    regStringVar(&baz, "baz", "", "this is baz")
}

func initFlags() {
    foo = getStringFlag("foo")
    bar = getStringFlag("bar")
    baz = getStringFlag("baz")
}

func main() {
    flag.Parse()
    initFlags()
    ...
}

解?

显而易见的解决方案是使用不同的名称。如果常见名称可能会发生冲突,您可以为它们添加前缀用包名或其他东西。但是你应该使用不同的,独特的,独特的名称。

想一想。如果你想使用相同的标志(同样是 name ),你为什么要在两个不同的地方注册两次?

如果你想从多个地方使用相同的标志,那么"外包"标志变量及其注册。例如。创建一个可以解决这个问题的软件包,并且从需要它的两个地方开始,参考这个软件包。或者确保将flag变量的值分发到需要它的地方。