2010-09-10 25 views
4

Donc, j'essaie d'écrire une classe d'Exception de base simple pour C++, basée sur la classe Java Exception. Je suis sûr qu'il existe déjà de bonnes bibliothèques, mais je le fais pour la pratique, pas pour le code de production, et je suis curieux et je cherche toujours à apprendre. L'une des choses que fait l'Exception de Java, que je voudrais aussi implémenter, est le concept de «cause». En Java, une nouvelle exception à une cause ressemble à:Implémentation de la classe Exception en C++

Exception cause = new Exception(); 
Exception newExcept = new Exception(cause); 

Cependant, en C++, en passant une exception comme argument du constructeur est de savoir comment le constructeur de copie est appelée. Donc, il y a la déconnexion conceptuelle entre copier l'Exception et créer une nouvelle Exception avec une cause. Ce n'est pas un problème en Java, évidemment. Je suppose que je me demande juste quelle serait la meilleure façon de gérer cela. Quelques idées que j'avais été:

  • Différencier avec une variable fictive
  • il suffit de créer une nouvelle exception, et appelé méthode setCause()
  • Quelque chose comme constructeur de copie est Exception(Exception &) et constructeur avec la cause est Exception(Exception *)

Merci

Répondre

5

L'exception - lorsqu'elle est allouée sur la pile (je le recommande fortement) - est libérée après la clause catch. Vous devez donc créer une copie de l'exception "interne" dans l'exception nouvellement créée. Si vous attrapez une classe de base de votre exception, elle perdra son type correct à moins que vous ne donniez à votre exception une méthode clone.

#include <string> 
#include <exception> 

class MyBaseException : public std::exception 
{ 
public: 
    MyBaseException(const std::string& what = std::string("MyBaseException")) 
     : m_BaseException(0), m_What(what) {} //Constructor without inner exception 

    MyBaseException(const MyBaseException& innerException, const std::string& what = std::string("MyBaseException")) 
     : m_BaseException(innerException.clone()), m_What(what) {} //Constructor with inner exception 

    template <class T> // valid for all subclasses of std::exception 
    MyBaseException(const T& innerException, const std::string& what = std::string("MyBaseException")) 
     : m_BaseException(new T(innerException)), m_What(what) {} 

    virtual ~MyBaseException() throw() 
     { if(m_BaseException) { delete m_BaseException; } } //don't forget to free the copy of the inner exception 
    const std::exception* base_exception() { return m_BaseException; } 
    virtual const char* what() const throw() 
     { return m_What.c_str(); } //add formated output for your inner exception here 
private: 
    const std::exception* m_BaseException; 
    const std::string m_What; 
    virtual const std::exception* clone() const 
     { return new MyBaseException(); } // do what ever is necesary to copy yourselve 
}; 

int main(int argc, char *argv[]) 
{ 
    try { 
     try { 
      throw std::exception(); 
     } 
     catch(const std::exception& e) { 
      throw MyBaseException(e, "bad"); 
     } 
    } 
    catch (const MyBaseException& e) { 
     throw MyBaseException(e, "even worse"); 
    } 
    //throw MyBaseException(1, "will not compile"); 
} 
+0

Les exceptions levées IIRC ne sont pas allouées sur la pile, mais dans une zone de mémoire «spéciale» utilisée uniquement pour contenir des exceptions levées, car lorsqu'une exception se propage, la pile est déroulée. –

+0

@matteo Vous avez probablement raison. Ils sont "spéciaux" car le déroulement de la pile a déjà eu lieu lorsque le bloc catch est appelé. Probablement c'est spécifique au compilateur. –

+0

Vous devez utiliser un pointeur intelligent pour gérer m_BaseException car les objets d'exception sont copiés partout. Actuellement, vous allez potentiellement supprimer votre pointeur d'exception de base (et récursivement sa chaîne) plusieurs fois. –

2

Vous pouvez utiliser le modèle d'usine:

Exception cause = Exception.Create(); 
Exception newExcept = Exception.Create(Exception cause); 
+0

En fait, j'aime vraiment cette solution aussi. C'est assez élégant et latéral, mais ma réponse choisie est probablement un peu proche de ce que je cherchais. –

1

il suffit d'ajouter la chaîne de l'exception de cause à l'exception actuelle:

try 
{ 
    throw std::runtime_error("Failed to work"); 
} 
catch(std::exception const& e) 
{ 
    // New exception (add origianl exception text). 
    throw std::runtime_error(std::string("We were doing poobar when: ") + e.what()); 
} 
+0

Juste pour être pointilleux qui ne ressemble pas à une déclaration de carch correcte, mais il ressemble à une bonne déclaration catch. – HandyGandy

0

vous êtes question ne sait pas, et ne semble même pas être Java correctement, mais un langage Java qui est pris en charge par une bibliothèque. Je devine que l'idiome que vous décrivez est en passant l'exception originale comme un argument à une exception que vous créez en réécrit.

La solution est de créer un library_exception (ou tout ce que vous voulez l'appeler)

class library_exception: public std::exception 
{ 
... 
public: 
    library_exception(const std::exception &e) 
... 
} 
... 
catch(const std::exception &e) 
{ 
    ... 
    throw library_exception(e); 
} 

Différentes classes pas de constructeur de copie appelée.