2010-03-17 39 views
2

Je veux dériver un train de chaînes afin que je puisse utiliser l'opérateur < < pour construire un message qui sera ensuite lancé. L'API ressemblerait à ceci:Dérivant streambuf ou basic_ostringstream?

error("some text") << " more text " << 42 << std::endl; 

Cela devrait faire un

throw "some text more text 42" 

Alors ce que je faisais est faire une errorbuf (héritant de streambuf) qui sature la méthode du « débordement », puis créer un ostream (& errorbuf). Je me demande si je ne devrais pas hériter de la place basic_ostringstream ou quelque chose ...

Répondre

2

Je vais trot ma macro préférée à nouveau ici:

#define ATHROW(msg)            \ 
{                 \ 
    std::ostringstream os;           \ 
    os << msg;              \ 
    throw ALib::Exception(os.str(), __LINE__, __FILE__);   \ 
}                 \ 

En utilisation:

ATHROW("Invalid value: " << x << " should be " << 42); 

le type d'exception est de ma propre bibliothèque, mais je pense que vous avez l'idée. C'est beaucoup plus simple que dériver votre propre classe de flux, et évite beaucoup de complications désagréables avec op < <().

+0

Parfois, les macros sont la meilleure solution. –

3

Vous pourriez probablement plus facile en faisant quelque chose comme:

class error_builder 
{ 
public: 
    error_builder(const std::string& pMsg = "") 
    { 
     mMsg << pMsg; 
    } 

    ~error_builder(void) 
    { 
     throw std::runtime_error(mMsg.str()); 
    } 

    template <typename T> 
    error_builder& operator<<(const T& pX) 
    { 
     mMsg << pX; 

     return *this; 
    } 

private: 
    std::stringstream mMsg;  
}; 


error_builder("some text") << " more text " << 42 << std::endl; 

Notez que vous ne devez pas jeter des chaînes comme vous êtes, par conséquent, j'ai utilisé std::runtime_error. Toutes les exceptions doivent dériver de std::exception, ce que runtime_error fait, de sorte que toutes les exceptions significatives peuvent être attrapées avec const std::exception&.

Cela fonctionne parce que le temporaire vit jusqu'à la fin de l'expression complète.

+0

Je ne vraiment jeter const char * s . C'était juste un concept. –

+0

Vous ne devriez pas lancer d'exceptions dans le destructeur (http://www.parashift.com/c++-faq-lite/exceptions.html#faq-17.3). –

+0

@Helltone: Ceci est une exception. Comprenez la raison pour laquelle vous ne devriez pas * généralement * (mot clé ici) jeter: lors du déroulement de la pile si deux exceptions deviennent actives, l'application est terminée. Ce n'est évidemment pas un cas ici, car il est prévu de le lancer immédiatement. (Pour être juste, le flux * pourrait * échouer et lancer, mais meh.) EDIT: Eh bien, réparé de toute façon, alors voilà. :) – GManNickG

2

Certains opérateurs manquent dans la solution de GMan.

class error { 
    public: 
    explicit error(const std::string& m = "") : 
      msg(m, std::ios_base::out | std::ios_base::ate) 
    {} 

    ~error() { 
     if(!std::uncaught_exception()) { 
     throw std::runtime_error(msg.str()); 
     } 
    } 

    template<typename T> 
    error& operator<<(const T& t) { 
     msg << t; 
     return *this; 
    } 

    error& operator<<(std::ostream& (*t)(std::ostream&)) { 
     msg << t; 
     return *this; 
    } 
    error& operator<<(std::ios& (*t)(std::ios&)) { 
     msg << t; 
     return *this; 
    } 
    error& operator<<(std::ios_base& (*t)(std::ios_base&)) { 
     msg << t; 
     return *this; 
    } 
    private: 
    std::ostringstream msg; 
}; 
0

Je crée simplement mes propres classes d'exception. Il vous suffit de surcharger what() et de fournir autant de constructeurs que vous le souhaitez. Pour construire le message d'erreur, utilisez vasprintf (si disponible) ou std :: ostringstream comme ci-dessus.

Voici un exemple:

class CustomException : public std::exception { 
private: 
    const std::string message; 
public: 
    CustomException(const std::string &format, ...) { 
     va_list args; 
     va_start(args, format); 
     char *formatted = 0; 
     int len = vasprintf(&formatted, format.c_str(), args); 
     if (len != -1) { 
      message = std::string(formatted); 
      free(formatted); 
     } else { 
      message = format; 
     } 
     va_end(args); 
    } 
    const char *what() const { 
     return message.c_str(); 
    } 
}; 

Si vous n'avez pas vasprintf, vous pouvez également utiliser vsnprintf avec un tampon sur la pile ...