C++ classe d'exception de conception
Qu'est ce qu'un bon design pour un ensemble de classes d'exception?
Je vois toutes sortes de choses autour de quelles classes d'exception qui devrait et ne devrait pas le faire, mais pas une conception simple qui est facile à utiliser et d'étendre choses.
- Les classes d'exception ne devrait pas lancer des exceptions, car cela pourrait l'amener tout droit à la résiliation de ce processus sans aucune possibilité de se connecter à l'erreur, etc.
- Il doit être possible d'obtenir une interface utilisateur conviviale de la chaîne, de préférence localisée à leur langue, de sorte qu'il ya quelque chose à leur dire avant de fermer l'application elle-même si elle ne peut pas récupérer d'une erreur.
- Il doit être possible d'ajouter des informations comme la pile se déroule, par exemple, si un analyseur XML ne parvient pas à analyser un flux d'entrée, pour être en mesure d'ajouter que la source était à partir d'un fichier, ou sur le réseau, etc.
- Des gestionnaires d'Exception besoin d'accéder facilement à l'information dont ils ont besoin pour gérer l'exception.
- Écrire formaté à l'exception des informations dans un fichier journal (en anglais, pas de traduction ici).
De 1 et 4, de travailler ensemble est le plus gros problème que je vais avoir, depuis la mise en forme et de sortie de fichier méthodes pourraient éventuellement échouer.
EDIT:
Donc, après avoir regardé des classes d'exception en plusieurs classes, et aussi dans la question Neil liés à, il semble être de pratique courante, à juste complètement ignorer le point 1 (et donc le coup de pouce des recommandations), ce qui semble être une assez mauvaise idée de moi.
De toute façon, j'ai pensé que je pourrais aussi poster l'exception de la classe, je suis en train de penser à l'aide.
class Exception : public std::exception
{
public:
//Enum for each exception type, which can also be used
//to determine the exception class, useful for logging
//or other localisation methods for generating a
//message of some sort.
enum ExceptionType
{
//Shouldn't ever be thrown
UNKNOWN_EXCEPTION = 0,
//The same as above, but it has a string that
//may provide some information
UNKNOWN_EXCEPTION_STR,
//For example, file not found
FILE_OPEN_ERROR,
//Lexical cast type error
TYPE_PARSE_ERROR,
//NOTE: in many cases functions only check and
// throw this in debug
INVALID_ARG,
//An error occured while trying to parse
//data from a file
FILE_PARSE_ERROR,
}
virtual ExceptionType getExceptionType()const throw()
{
return UNKNOWN_EXCEPTION;
}
virtual const char* what()throw(){return "UNKNOWN_EXCEPTION";}
};
class FileOpenError : public Exception
{
public:
enum Reason
{
FILE_NOT_FOUND,
LOCKED,
DOES_NOT_EXIST,
ACCESS_DENIED
};
FileOpenError(Reason reason, const char *file, const char *dir)throw();
Reason getReason()const throw();
const char* getFile()const throw();
const char* getDir ()const throw();
private:
Reason reason;
static const unsigned FILE_LEN = 256;
static const unsigned DIR_LEN = 256;
char file[FILE_LEN], dir[DIR_LEN];
};
Point 1 est réglé, étant donné que toutes les chaînes sont gérées par la copie d'un interne, fixe la taille de la mémoire tampon (troncature si nécessaire, mais toujours null).
Bien que ce n'est pas l'adresse de point 3, mais je pense que le point est plus que probable de l'utilisation limitée dans le monde réel, de toute façon, et ils sont les plus susceptibles d'être abordés en lançant une nouvelle exception en cas de besoin.
Vous devez vous connecter pour publier un commentaire.
Utiliser une faible profondeur de la hiérarchie de classes d'exception. Prise de la hiérarchie trop profond ajoute plus de complexité que de valeur.
Déduire l'exception des classes de std::exception (ou l'une de l'autre, les exceptions standard comme std::runtime_error). Cela permet génériques, les gestionnaires d'exception au plus haut niveau pour traiter toutes les exceptions que vous n'avez pas. Par exemple, il pourrait y avoir un gestionnaire d'exception que les journaux d'erreurs.
Si c'est pour une bibliothèque ou d'un module, vous pouvez choisir une base spécifiques à votre module (dérivées de l'une des classes d'exception). Les appelants peuvent décider d'attraper quoi que ce soit à partir de votre module de cette façon.
Je ne voudrais pas faire trop de classes d'exception. Vous pouvez emballer beaucoup de détails à propos de l'exception dans la classe, de sorte que vous n'avez pas nécessairement besoin de faire une exception de la classe pour chaque type d'erreur. De l'autre côté, vous ne voulez classes uniques pour les erreurs que vous attendez pour gérer. Si vous faites un analyseur, vous pourriez avoir un seul syntax_error exception avec des membres qui décrivent les détails du problème plutôt que d'un tas de spécialité pour différents types d'erreurs de syntaxe.
Les chaînes dans les exceptions sont là pour le débogage. Vous ne devriez pas les utiliser dans l'interface utilisateur. Vous voulez garder de l'INTERFACE utilisateur et la logique distincte que possible, pour permettre à des choses comme la traduction vers d'autres langues.
Vos classes d'exception peuvent avoir des champs supplémentaires avec des détails sur le problème. Par exemple, un syntax_error exception pourrait avoir le nom du fichier source, le numéro de ligne, etc. Autant que possible, s'en tenir à des types de base de ces champs pour réduire les risques de la construction ou de la copie de l'exception pour déclencher une autre exception. Par exemple, si vous devez stocker un nom de fichier dans l'exception, vous pourriez vouloir un simple tableau de caractères de longueur fixe, plutôt qu'un std::string. Typique des implémentations de std::exception d'allouer dynamiquement de la chaîne de motif à l'aide de malloc. Si le malloc échoue, ils sont prêts à sacrifier la chaîne de raison plutôt que de jeter une exception imbriquée ou de s'écraser.
Exceptions en C++ doit être pour "exceptionnelle" des conditions. Donc, l'analyse des exemples pourraient ne pas être les bonnes. Une erreur de syntaxe rencontré lors de l'analyse d'un fichier ne peut pas être spécial suffisamment pour justifier d'être manipulés par des exceptions. Je dirais quelque chose qui est exceptionnel, si le programme ne peut probablement pas continuer à moins que la condition est explicitement traitée. Ainsi, la plupart des échecs d'allocation de mémoire sont exceptionnelles, mais la mauvaise qualité des entrées de l'utilisateur n'est probablement pas.
catch (const syntax_error &ex) { std::cerr << ex.filename() << "(" << ex.line_number() << "): " << GetText(IDS_SYNTAXERROR) << std::endl; }
, oùGetText()
charge une chaîne à partir de votre programme de ressources.constexpr
ou de déplacer des constructeurs et de se déplacer-l'affectation dans une classe d'exception afin d'éviter de heurter d'autres problèmes lors de la propagation d'une exception, mais si vous avez réellement besoin de ces, je me demande si votre classe d'exception est trop compliqué.Utiliser l'héritage virtuel. Cette idée est due à Andrew Koenig. À l'aide de l'héritage virtuel à partir de votre exception de la classe de base(es) empêche l'ambiguïté des problèmes, à la prise de site au cas où quelqu'un déclenche une exception dérivée à partir de plusieurs bases qui ont une classe de base commune.
D'autres qui sont également des conseils utiles sur la boost site
2: Non, vous ne devez pas mélanger de l'interface utilisateur (=messages localisés) avec la logique du programme.
La Communication de l'utilisateur doit être fait au niveau externe lors de l'application
on sait qu'il ne peut pas traiter la question. La plupart des informations dans un
exception est trop d'un détail d'implémentation pour montrer à l'utilisateur de toute façon.
3: Utiliser boost.exception pour ce
5: Pas de ne pas le faire. Voir les 2. La décision de journal doit toujours être une erreur de manipulation de site.
Ne pas utiliser un seul type d'exception. Utiliser suffisamment de types, de sorte que l'application peut
utiliser un gestionnaire catch pour chaque type d'erreur de récupération nécessaire
Ne sont pas directement liés à la conception d'une classe d'exception de la hiérarchie, mais important (et liés à l'utilisation de ces exceptions), c'est que vous devez généralement jeter par la valeur et de l'attraper par référence.
Cela évite les problèmes liés à la gestion de la mémoire de la levée d'une exception (si vous avez jeté les pointeurs) et avec le potentiel pour objet de découpage (si vous intercepter des exceptions en valeur).
Depuis
std::nested_exception
etstd::throw_with_nested
sont devenus disponibles avec C++11, je voudrais des réponses sur StackOverflow ici et iciCes réponses décrivent comment vous pouvez obtenir une trace sur vos exceptions à l'intérieur de votre code sans avoir besoin d'un débogueur ou la lourdeur de la journalisation, en écrivant un bon gestionnaire d'exception qui va renvoyer exceptions imbriquées.
L'exception de la conception il y a, à mon avis, suggère également de ne pas créer d'exception hiérarchies de classe, mais à créer une seule classe d'exception par la bibliothèque (comme l'a déjà souligné dans une réponse à cette question).
Un bon design n'est pas de créer un ensemble de classes d'exception -
il suffit de créer un par bibliothèque, basé sur std::exception.
Ajouter de l'information est assez facile:
Et des gestionnaires d'exception ont l'information dont ils ont besoin parce qu'ils sont des gestionnaires d'exception - seulement les fonctions dans les blocs try et peuvent causer des exceptions, de sorte que les gestionnaires qui ont seulement besoin de tenir compte de ces erreurs. Et non vous ne devrait pas vraiment être en utilisant les exceptions générales de gestion d'erreur.
Fondamentalement, des exceptions doivent être aussi simple que possible - un peu comme les fichiers de log, ce qu'ils ne devraient pas avoir de connexion directe.
Cela a été demandé avant, je pense, mais je ne peux pas le trouver dès maintenant.
std::exception::what()
retourneconst char *
, doncex.what() + " more text "
n'est pas concaténer le texte comme il le ferait avec un objet de type string. Vous ne voulez pas de véritable chaîne objets dans vos classes d'exception, car ils pourraient conduire à des exceptions imbriquées.