8
votes

C ++ Manipulation des erreurs - INDIQUES D'UTILISATION DE STD :: PAISE OU STD :: TUPLE POUR RETOUR DES CODES D'ERREUR ET DES RETOURS DE LA FONCTIONNEMENT

Sans entrer dans les exceptions générales VS Codes d'erreur Discussion, que pensez-vous sont les inconvénients de l'utilisation de std :: paire ou std: tuple pour renvoyer plusieurs valeurs, à savoir la valeur de retour de la fonction et le code d'erreur / de réussite, similaire au nombre de Go Les développeurs font apparemment la manutention d'erreurs ?

Cette approche présente évidemment l'avantage de ne pas avoir à utiliser des paramètres pour la valeur de retour de la fonction ou le code d'erreur (en fonction de la manière dont vous le préférez).


3 commentaires

Découvrez l'Old Barton / Nackman Fabble classe ou plus de version moderne comme Boost en option


@ Votes clés: Veuillez ne voter que pour fermer les questions que vous comprenez. Ne voterez pas pour fermer simplement parce que vous omettre de comprendre la question. L'incapacité de comprendre ne signifie pas que vous êtes compétent pour voter dessus: cela signifie que cela signifie le contraire


FAIT FUN - Les questions les plus utilisables, riches et éduquées sur CO sont fermées, "non constructives", "trop ​​large", etc. puisque le thread est maintenant fermé, un autre inconvénient de la tête de tuple-mémoire. TOUJOURS . Avec une exception, le mécanisme d'exception ne donne qu'un coup de pied s'il y a une exception.


4 Réponses :


2
votes

Cet indice "idiom" est bon car le type et l'indicateur de succès sont des valeurs de retour de la fonction. L'échec peut ne pas être exceptionnel, alors les exceptions sont parfois inappropriées.

L'inconvénient est cependant que vous devez diviser les deux types de retour. Cela peut être moche; Utilisation de std :: cravate code> aide mais vous ne pouvez pas construire à partir de plusieurs retours. P>

bool success;
std::string value;
std::tie(success, value)=try_my_func();


8 commentaires

Convenu que la gestion du retour est difficile à manier. Une alternative est résultat automatique = try_my_func (); if (résultat.first) {std :: string & valeur = résultat.seconde; ...} , et c'est un peu plus naturel dans certaines circonstances.


@Stevejessop Oui, en utilisant Auto aide un peu, mais je trouve la prise de référence très "bruyante", espérons pouvoir construire à partir d'un tuple sera ajouté à la langue un jour.


En pratique, je pourrais ne pas vous embêter avec la référence si la valeur n'est utilisée que plusieurs fois dans le code suivant. Il suffit d'écrire résultat.second quelques fois: c'est ennuyeux mais pas assez ennuyeux pour faire quoi que ce soit à ce sujet :-)


@ 111111, je suppose que vous pouvez le faire avec une macro, quelque chose comme #define déclend_tie (x, y, expr) résultat automatique = expr; auto & x = résultat.first; AUTO & Y = RÉSULTAT.SECOND SAUF Bien sûr, vous voudrez utiliser une sorte de gensym pour résultat ( concat (résultat __, __ ligne __) , par exemple)


@ici vous pourriez, je ne suis pas sûr de pouvoir m'apporter à utiliser une macro où lorsque une petite verbosité est requise, elle n'élimine toujours pas la construction par défaut.


@ 111111, pensez simplement à la macro comme une implémentation de modèle primitive :) et utilisez des macros simples et des modèles complexes. Vous avez raison sur la construction par défaut, qui est un argument en faveur de toujours concevoir des classes avec un constructeur par défaut vraiment bon marché. Cela doit être un principe de conception quelque part; Vous semblez toujours travailler autour d'une fonctionnalité de bibliothèque ou d'une autre lorsque vous essayez d'éviter la construction par défaut. Fwiw. (J'aimerais aussi voir la caractéristique linguistique, à la manière: a-t-elle été proposée?)


@ici n'est pas sûr, à moins qu'elles ajoutent que la macro, il faudrait être une caractéristique linguistique et "ils" semblent très réticents à le faire. Ce n'est pas la fin du monde, je serais bien loin d'être concentré sur des choses importantes comme le vrai soutien de l'UTF8.


Cela a depuis été ajouté en tant que caractéristique linguistique: en.cppreference.com/w/cpp/ Langue / structuré_binding par exemple Auto [Succès, valeur] = try_my_func ()



2
votes

À cette fin, dans la plupart des cas, j'utilise un propre type d'emballage qui introduit un sucre syntaxique. Voyons un exemple: xxx pré>

puis, utilisez simplement cette classe comme valeur de retour dans vos méthodes pouvant renvoyer des erreurs: p> xxx pré>

de Bien sûr, cette mise en œuvre de la fonction factorielle est horrible, mais démontre la conversion sans gêner le type de retour étendu à l'erreur. p>

Exemple d'utilisation: P>

Result<int> fac(int n) {
    if(n < 0)
        return Result<int>::error("n has to be greater or equal zero!");
    if(n == 0)
        return 1;
    if(n > 0) {
        Result<int> r = fac(n - 1);
        if(r.isError()) return r;  // propagate error (similar to exceptions)
        return n * r;              // r gets automatically converted to int
    }
}


2 commentaires

v doit encore être construit (un inconvénient majeur à des multi-retours), en utilisant quelque chose comme std :: aligné_storage et le remplacement newing serait un meilleur pari, ou simplement utiliser < Code> Boost.Optional qui le fait déjà et que le message d'erreur semble repousser les limites dans un territoire d'exception.


Parfois, je ne veux pas utiliser boost. Et parfois (sinon 99% des cas), l'inconvénient de la performance n'a pas d'importance. Cependant, merci d'avoir mentionné cela.



0
votes

C'est mieux que les codes d'erreur ordinaires car vous n'avez pas à perdre votre vie avec des paramètres. Mais il conserve toujours tous les inconvénients très graves.

Vraiment, c'est juste un micro changement, c'est toujours des codes d'erreur - il n'y a pas d'avantage significatif.


2 commentaires

en utilisant E.G. Boost :: Facultatif Permet à une fonction de "aucun résultat produit" dans le cadre de son contrat d'invocation normal, tout en conservant la sécurité des exceptions. L'utilisation d'un rendement normal également pour le cas «sans résultat produit» peut être important pour la clarté du code d'appel. Mais cela dépend ...


Pour ceux qui ne veulent pas ajouter une dépendance à boost: facultatif est trivial pour mettre en œuvre pour le type de pod, et pour la non-POD, on peut simplement utiliser un seul élément std :: vecteur pour transférer la valeur possible. Dans le cas d'aucune valeur que le vecteur est simplement vide (le fait essentiellement de cette façon, on évite une dépendance et obtient un code simple au prix d'une éventuelle allocation dynamique).



1
votes

"Que pensez-vous sont les inconvénients de l'utilisation de STD: paire ou STD: tuple pour renvoyer plusieurs valeurs, à savoir la valeur de retour de la fonction et le code d'erreur / de réussite"

L'opportunité principale de cette approche simpliste (niveau C) en matière de manipulation de défaillance est un

  • Perte de sécurité.

    I.e. Il y a plus qui peut aller mal , comme l'accès à une valeur de résultat indéterminé. Ou simplement utiliser une valeur de retour lorsque la fonction n'a pas produit de significatif.

    L'Old Barton & Nackman Fachow La classe a résolu ce problème de sécurité en limitant l'accès à la valeur de résultat. Essentiellement, le code d'appel doit vérifier s'il y a est une valeur de résultat, avant de l'utiliser et l'utilisation d'une valeur de résultat logiquement inexistante entraîne une exception ou une terminaison. Le boost :: facultatif classe la même chose.

    Si vous ne voulez pas une dépendance sur Boost, une classe en option en option est triviale à la mise en œuvre pour le type de résultat de la POD et à un coût d'une petite inefficacité possible (allocation dynamique), vous pouvez simplement Utilisez un std :: vecteur pour porter un résultat possible non-pod.

    Le défi consiste à conserver la clarté du code d'appel, qui est tout le point de l'exercice ...


1 commentaires

@Anonymous Downvoter: S'il vous plaît faire Expliquer Votre Downvote. Le vote n'est pas un bon moyen d'exprimer son désaccord depuis peu ou personne ne peut lire votre esprit et votre faim ce qui a déclenché votre réaction. Quelle aperçu avez-vous eu? Ou quelles connaissances possédez-vous que vous avez choisi de ne pas partager? Ou, alternativement, et beaucoup plus probablement, qu'avez-vous pas compris? Sans explication, les lecteurs ne peuvent pas savoir. La seule chose qu'ils savent, c'est que vous n'étiez pas capable de communiquer ou de partager.