Il y a deux points à répondre en ce qui concerne ce que vous voulez :
1.
Le premier point est que la manière la plus agréable est de créer des types spéciaux (classes) pour les exceptions personnalisées et de passer les paramètres comme champs des classes.
Quelque chose comme le suivant :
class BaseFor_Exceptions : public std::exception {
protected:
BaseFor_Exceptions();
};
class Exception1 : public BaseFor_Exceptions {
public:
Exception1(uint32_t value1);
private:
uint32_t value1;
};
throw Exception1(0);
Le deuxième point est que vous effectuez des allocations de mémoire lors de la préparation de l'objet d'exception parce que vous essayez de passer une valeur de taille variable (nom de fichier).
Il est possible (lors du changement d'objets de std::string et std::stringstream) que l'exception std::bad_alloc soit lancée au cours du processus, de sorte que si vous ne préparez pas ou ne lancez pas (*) votre exception, vous perdrez l'information et l'état.
Dans un programme bien conçu, il est facile d'éviter l'allocation de mémoire lors de la préparation ou du traitement d'une exception. Il suffit de le faire :
- soit garantir que la valeur est toujours vivante lors du traitement de l'exception et transmettre une sorte de lien vers la valeur dans le cadre de l'exception - soit une référence, soit une sorte de pointeur (le plus souvent intelligent),
-
ou obtenir la valeur lors du traitement de l'exception en utilisant l'information sur le type d'exception ou/et les valeurs à taille fixe ; par exemple,
} catch (const ConfigurationLoadError & ex) {
std::cerr
<< “Some message 1 ”
<< serviceLocator1.SomeGetMethod1().Get_ConfigurationFileName();
} catch (const SomeException & ex) {
std::cerr
<< “Some message 2 ”
<< serviceLocator1.SomeGetMethod2().GetEventDetailsString(ex.Get_Value1());
}
Bien entendu, vous avez toujours la possibilité d'accepter les limites de taille de la mémoire tampon et d'utiliser une mémoire tampon pré-allouée.
Veuillez également noter que les types (classes) utilisés pour les exceptions ne sont pas autorisés à lancer des exceptions à partir de leurs constructeurs de copie car, si l'exception initiale est tentée d'être attrapée par valeur, un appel du constructeur de copie est possible (dans le cas où il n'est pas élidé par le compilateur) et cette exception supplémentaire interrompra le traitement de l'exception initiale avant que celle-ci ne soit attrapée, ce qui entraîne l'appel de std::terminate. Depuis C++11, les compilateurs sont autorisés à éliminer la copie dans certains cas lors de la capture, mais l'élision n'est pas toujours judicieuse et, si elle l'est, ce n'est qu'une permission et non une obligation (cf. https://en.cppreference.com/w/cpp/language/copy_elision pour plus de détails ; avant C++11, les normes du langage ne réglementaient pas la question).
De plus, vous devriez éviter que les exceptions (nous les appellerons les supplémentaires) soient lancées par les constructeurs et les constructeurs de déplacement de vos types (classes) utilisés pour les exceptions (nous les appellerons les initiales) car les constructeurs et les constructeurs de déplacement pourraient être appelés lors du lancement d'objets des types en tant qu'exceptions initiales, puis le lancement d'une exception supplémentaire empêcherait la création d'un objet d'exception initiale, et l'initiale serait simplement perdue. De même qu'une exception supplémentaire provenant d'un constructeur de copie, lors du lancement d'une exception initiale, provoquerait la même chose.