web-dev-qa-db-fra.com

Le drapeau de Golang redéfini

J'ai des drapeaux qui peuvent être définis dans plusieurs composants. Il n'est pas possible de savoir si un composant a déjà défini un indicateur, par exemple si deux composants Foo et Bar nécessitent l'indicateur Zed, mais Foo et Bar doivent définir l'indicateur Zed. Dans ce cas, le drapeau de Go paniquera avec un drapeau d'erreur redéfini.

Existe-t-il un moyen d'initialiser éventuellement un indicateur uniquement s'il n'a pas été initialisé ailleurs? Ou bien vérifier si un indicateur a déjà été défini et s'il a ensuite recherché la valeur d'indicateur?

La seule façon que je vois de le faire est avec quelque chose comme ça:

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()
    ...
}

Mais c'est incroyablement laid (et je ne sais pas dans quelle mesure cela fonctionnera). Avez-vous des idées sur une meilleure façon de procéder?

7
ElfsЯUs

Malheureusement, il n'y a pas de moyen plus simple avec la bibliothèque standard, car chaque indicateur ne peut être enregistré qu'une seule fois. Nous verrons comment nous pouvons simplifier votre code, et aussi ce que nous pouvons faire pour éviter ce modèle.

Flag.FlagSet n'aide pas

L'utilisation de flag.FlagSet n'est pas une solution, elle a été conçue pour prendre en charge les sous-commandes (voir cette question pour un exemple: Définition d'indicateurs indépendants dans GoLang ).

Pour démontrer pourquoi cela n'aide pas: supposons que vous ayez 2 drapeaux (l'un peut être le détail du paquetage flag). Le premier jeu de drapeaux peut contenir les drapeaux "foo" Et "bar", Et le deuxième jeu de drapeaux peut contenir les drapeaux "foo" Et "baz". Que se passe-t-il si l'utilisateur fournit des valeurs pour les 3? L'appel à FlagSet.Parse() du premier jeu de drapeaux signalera une erreur:

indicateur fourni mais non défini: -baz

De même, la FlagSet.Parse() du deuxième jeu de drapeaux signalerait également une erreur pour -bar Non défini.

Simplifier votre code

Si vous souhaitez conserver votre approche, notez que vous pouvez simplifier votre code en utilisant les variables vars ( flag.StringVar() ), puis dans initFlags() vous pouvez simplement obtenir une valeur string et affectez à votre variable indicateur comme ceci:

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()
    ...
}

De plus, si vous souhaitez gérer plusieurs indicateurs comme celui-ci, vous devez créer des fonctions d'assistance pour ne pas répéter le code (maintenant moins "laid"):

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)
}

Et en utilisant les aides ci-dessus, l'enregistrement de 3 variables de drapeau est comme:

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()
    ...
}

Solution?

La solution évidente serait d'utiliser des noms différents. Si des noms communs peuvent entrer en collision, vous pouvez les préfixer, par exemple avec le nom du package ou quelque chose. Mais vous devez utiliser des noms différents, distincts et uniques.

Pensez-y. Si vous souhaitez utiliser le même drapeau (même par nom ), pourquoi essayez-vous de l'enregistrer deux fois, à 2 endroits différents?

Si vous souhaitez utiliser le même indicateur à partir de plusieurs emplacements, "externalisez" la variable d'indicateur et son enregistrement. Par exemple. créez un package qui s'en occupera et, aux deux endroits où cela est nécessaire, reportez-vous à ce package. Ou assurez-vous de distribuer les valeurs de la variable d'indicateur aux endroits où cela est nécessaire.

6
icza