Comment lancer std :: exceptions avec des messages variables?

Ceci est un exemple de ce que je fais souvent lorsque je veux append des informations à une exception:

std::ssortingngstream errMsg; errMsg << "Could not load config file '" << configfile << "'"; throw std::exception(errMsg.str().c_str()); 

Y a-t-il une meilleure façon de le faire?

Voici ma solution:

 class Formatter { public: Formatter() {} ~Formatter() {} template  Formatter & operator << (const Type & value) { stream_ << value; return *this; } std::string str() const { return stream_.str(); } operator std::string () const { return stream_.str(); } enum ConvertToString { to_str }; std::string operator >> (ConvertToSsortingng) { return stream_.str(); } private: std::ssortingngstream stream_; Formatter(const Formatter &); Formatter & operator = (Formatter &); }; 

Exemple:

 throw std::runtime_error(Formatter() << foo << 13 << ", bar" << myData); // implicitly cast to std::string throw std::runtime_error(Formatter() << foo << 13 << ", bar" << myData >> Formatter::to_str); // explicitly cast to std::ssortingng 

Les exceptions standard peuvent être construites à partir d’un std::ssortingng :

 #include  char const * configfile = "hardcode.cfg"; std::ssortingng const anotherfile = get_file(); throw std::runtime_error(std::ssortingng("Failed: ") + configfile); throw std::runtime_error("Error: " + anotherfile); 

Notez que la classe de base std::exception ne peut pas être construite ainsi; vous devez utiliser l’une des classes concrètes et dérivées.

Il existe différentes exceptions telles que runtime_error , range_error , overflow_error , logic_error , etc. Vous devez passer la chaîne dans son constructeur, et vous pouvez concaténer ce que vous voulez dans votre message. C’est juste une opération de chaîne.

 std::ssortingng errorMessage = std::ssortingng("Error: on file ")+fileName; throw std::runtime_error(errorMessage); 

Vous pouvez également utiliser boost::format comme ceci:

 throw std::runtime_error(boost::format("Error processing file %1") % fileName); 

La classe suivante est très utile:

 struct Error : std::exception { char text[1000]; Error(char const* fmt, ...) __atsortingbute__((format(printf,2,3))) { va_list ap; va_start(ap, fmt); vsnprintf(text, sizeof text, fmt, ap); va_end(ap); } char const* what() const throw() { return text; } }; 

Exemple d’utilisation:

 throw Error("Could not load config file '%s'", configfile.c_str()); 

Utilisez l’opérateur littéral de chaîne si C ++ 14 ( operator ""s )

 using namespace std::ssortingng_literals; throw std::exception("Could not load config file '"s + configfile + "'"s); 

ou définissez votre propre si en C ++ 11. Par exemple

 std::ssortingng operator ""_s(const char * str, std::size_t len) { return std::ssortingng(str, str + len); } 

Votre déclaration de lancer ressemblera alors à ceci

 throw std::exception("Could not load config file '"_s + configfile + "'"_s); 

qui a l’air bien et propre.

Une manière vraiment plus agréable serait de créer une classe (ou des classes) pour les exceptions.

Quelque chose comme:

 class ConfigurationError : public std::exception { public: ConfigurationError(); }; class ConfigurationLoadError : public ConfigurationError { public: ConfigurationLoadError(std::ssortingng & filename); }; 

La raison en est que les exceptions sont beaucoup plus que le simple transfert d’une chaîne. En fournissant des classes différentes pour les erreurs, vous donnez aux développeurs une chance de gérer une erreur particulière de manière correspondante (pas seulement afficher un message d’erreur). Les personnes qui attrapent votre exception peuvent être aussi spécifiques que nécessaire si vous utilisez une hiérarchie.

a) Il peut être nécessaire de connaître la raison spécifique

 } catch (const ConfigurationLoadError & ex) { // ... } catch (const ConfigurationError & ex) { 

a) un autre ne veut pas connaître les détails

 } catch (const std::exception & ex) { 

Vous pouvez trouver de l’inspiration sur ce sujet dans https://books.google.ru/books?id=6tjfmnKhT24C Chapitre 9

En outre, vous pouvez également fournir un message personnalisé, mais faites attention: il n’est pas prudent de composer un message avec std::ssortingng ou std::ssortingngstream ou tout autre moyen pouvant provoquer une exception .

Généralement, il n’y a pas de différence si vous allouez de la mémoire (travaillez avec des chaînes de manière C ++) dans le constructeur de l’exception ou juste avant de lancer – l’exception std::bad_alloc peut être lancée avant celle que vous voulez vraiment.

Ainsi, un tampon alloué sur la stack (comme dans la réponse de Maxim) est un moyen plus sûr.

Il est très bien expliqué à http://www.boost.org/community/error_handling.html

Ainsi, la manière la plus agréable serait un type spécifique de l’exception et d’éviter de composer la chaîne formatée (au moins lors du lancement).