重新定义Golang标志

Elf*_*ЯUs 6 flags go

我有可能在多个组件中定义的标志。无法知道某个组件是否已经定义了标志,例如两个组件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()
    ...
}
Run Code Online (Sandbox Code Playgroud)

但是,这非常丑陋(而且不确定其效果如何)。有什么更好的方法吗?

icz*_*cza 5

不幸的是,标准库没有一种更简单的方法,因为每个标志只能被注册一次。我们将看到如何简化您的代码,以及如何避免这种模式。

Flag.FlagSet没有帮助

的使用flag.FlagSet不是解决方案,它旨在支持子命令(请参阅此问题的示例:在GoLang中定义独立的FlagSet)。

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

提供了但未定义的标志:-baz

同样FlagSet.Parse(),第二个标记集的也将报告undefined错误-bar

简化代码

如果您想保持这种方法,请注意,您可以使用vars标志(flag.StringVar())简化代码,然后initFlags()可以简单地获取一个string值并分配给您的flag变量,如下所示:

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()
    ...
}
Run Code Online (Sandbox Code Playgroud)

另外,如果要处理这样的多个标志,则应创建辅助函数以不重复(现在少了“丑陋”)代码:

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)
}
Run Code Online (Sandbox Code Playgroud)

使用上述帮助器,注册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()
    ...
}
Run Code Online (Sandbox Code Playgroud)

解?

显而易见的解决方案是使用不同的名称。如果通用名称可能会冲突,则可以在它们前面加上包名称或其他名称。但是您应该使用不同,不同,唯一的名称。

想一想。如果要使用相同的标志(名称相同),为什么要尝试在2个不同的位置进行两次注册?

如果确实要在多个地方使用相同的标志,请“外包”标志变量及其注册。例如,创建一个可以解决此问题的程序包,并在需要的两个地方都引用该程序包。或确保将flag变量的值分布到需要的地方。

  • 至于为什么我需要用相同的名称注册两次标志,我有可能包含相同标志的可重用服务组件。服务可能包含零个或多个具有相同标志的组件,因此需要特定标志的各个组件不知道这些标志是否已被另一个组件注册。您建议为标志提供一个全局容器是一个很好的选择,但希望避免这种情况,因为标志包基本上已经做到了这一点。 (2认同)