在C ++中引发异常和处理某些异常类型的正确方法是什么

时间:2018-04-03 15:40:57

标签: c++ exception exception-handling

我知道理论上如何创建和处理异常。但是在一个大项目中,一个函数可以抛出很多不同的异常。

如何设法解析我可以收到的所有例外?

例如:

我目前正在实施数据库客户端。如果查询请求坏表或坏列,我会在低级驱动程序中抛出异常,如下所示:

throw MySQLException("message");

这会导致在客户端的顶层抛出std::runtime_error个例外。所以我添加了以下代码来捕获异常:

try {
  execute(query);
}
catch(std::exception &e) {
  // How do I parse exception content?
}

但有没有比从e.what()中删除信息更好的方法来找出它是什么样的例外? 目标是使每种异常对应于错误代码(不修改异常消息)。

链接到教程将是weclome。

2 个答案:

答案 0 :(得分:4)

您可以拥有多个catch语句,如下所示:

try {
    execute(query);
}
catch (const my_custom_exception_type& e) {
}
catch (const std::runtime_error& e) {
}
catch (const std::exception& e) {
}
catch (...) {
    // fallback - exception object is not any of the above types
}

控件将流入第一个catch块,其参数类型与异常对象类型兼容。如果没有匹配且没有... catch-all,则异常将从该try / catch块传播出去。可以找到here

的确切尝试/捕获行为的更精确的解释
  

当复合语句中的任何语句抛出类型E的异常时,它将与handler-seq中每个catch子句的形式参数T的类型匹配,其顺序为catch子句的列出顺序。如果满足以下任何条件,则例外是匹配:

     
      
  • E和T是相同的类型(忽略T上的顶级cv限定符)
  •   
  • T是对(可能是cv-qualified)E
  • 的左值引用   
  • T是E
  • 的明确公共基类   
  • T是对E
  • 的明确公共基类的引用   
  • T(可能是cv认证的)U或const U& (自C ++ 14开始),U是指向成员(自C ++ 17)类型的指针或指针,E也是指向成员(自C ++ 17)类型的指针或指针,可以隐式转换为U一个或多个      
        
    • 除了一个私有,受保护或不明确的基类之外的标准指针转换
    •   
    • 资格转换
    •   
    • 函数指针转换(自C ++ 17起)
    •   
  •   
  • T是指向成员的指针或指针或对const指针的引用(自C ++ 14起),而E是std :: nullptr_t。
  •   

您还可以使用RTTI来确定e的类型,但如果可能的话,请避免使用。

答案 1 :(得分:1)

我不是100%确定这是你正在寻找的,但是关于你所做的陈述:

  

But in a big project a function can throw a lot of different exception.

我在构建3D图形引擎时在大型项目中使用过这种设计。这确实需要一些课程。另外需要注意的是,我的代码是专门针对Windows而设计的。我评论了窗口特定代码的部分,可以用您的特定操作系统实现替换。另一件事是我在许多输出消息中使用了__FUNCTION__,你可以用__PRETTY_FUNCTION__替换它,因为某些原因windows和visual studio不喜欢它...你也可以替换它命名空间我使用您自己的命名空间名称。如果这不能直接帮助您,那么至少整体设计可能会有用。您可以创建一个新项目并将其用作查看生成的控制台消息和写入的日志文件。您可以从中获取一些概念,或者您可以扩展和集成您自己的特定错误消息和异常类型,这些消息和异常类型将被放入这些类中,以使其适合您自己的目的。

以下类集成在一起,其中一些可以单独使用或组合使用,并且应该是线程安全的,用于多线程目的。

  
      
  • ExceptionHandler - 它使用Logger类,用于处理抛出和/或发送到Logger的消息。
  •   
  • Logger - Singleton的派生类。它会记录到控制台,文本文件或两者。它使用TextFileWriter写入文本文件。
  •   
  • Singleton - 所有Singleton Type类派生自的基类。
  •   
  • TextFileWriter - 来自FileHandler的派生类。它写入文本文件。
  •   
  • FileHandler - 所有文件处理类型类派生自的基类。
  •   

<小时/> 使用上述类的示例程序:

#include "Logger.h"
#include "ExceptionHandler.h"

#include <sstream>
#include <iostream>

enum ReturnCode {
    ReturnError = -1,
    ReturnOkay = 0
};

// helper function
void quitMessage() {
    std::cout << "\nPress any key and enter to quit.\n";
    std::cin.get();
}

// Example Class that uses the Logger & ExceptionHandler.
class Foo {
public:
    Foo() = default;

    void printFooUsingExceptionHandler() {
        using namespace linx;
        throw ExceptionHandler( __FUNCTION__ + std::string( " using ExceptionHandler" ) );
        // Or can be used this way
        // std::ostringstream stream;
        // stream << __FUNCTION__ << " using ExceptionHandler";
        // throw ExceptionHandler( stream );
    }

    void printFooNotUsingExceptionHandlerButUsingLogger() {
        using namespace linx;
        std::ostringstream stream;
        stream << __FUNCTION__ << " logging to logger, but not using ExceptionHandler";
        Logger::log( stream, Logger::TypeWarning ); // Can use TypeInfo, TypeWarning or TypeError pending on the use case.
    }
};

int main() {
    namespace lx = linx;
    using namespace lx;

    try {
        Logger logger( "logger.txt" );

        // last param is ommited as it is defaults to TypeInfo
        Logger::log( "This is basic info" ); 
        Logger::log( "This is a warning", Logger::TypeWarning );
        Logger::log( "This is an error", Logger::TypeError );
        Logger::log( "Basic console message", Logger::TypeConsole );    

        // Example of a class using the ExceptionHandler & Logger classes
        Foo f;
        // call this first; log to message but don't throw excpetion
        f.printFooNotUsingExceptionHandlerButUsingLogger();
        // call this to throw exception
        f.printFooUsingExceptionHandler();

    } catch( ExceptionHandler& e  ) {
        std::cout << "Exception Thrown: " << e.getMessage() << std::endl;
        quitMessage();
        return ReturnError;
    } catch( ... ) {
        std::cout << __FUNCTION__ << " Caught Unknown Exception" << std::endl;
        quitMessage();
        return ReturnError;
    }

    quitMessage();
    return ReturnOkay;
}

课程:

ExceptionHandler.h

#ifndef EXCEPTION_HANDLER_H
#define EXCEPTION_HANDLER_H

#include <string>
#include <sstream>

namespace linx {

class ExceptionHandler final {
private:
    std::string _message;

public:
    explicit ExceptionHandler( const std::string& message, bool saveInLog = true );
    explicit ExceptionHandler( const std::ostringstream& streamMessage, bool saveInLog = true );

    ~ExceptionHandler() = default;
    ExceptionHandler( const ExceptionHandler& c ) = default;
    ExceptionHandler& operator=( const ExceptionHandler& c ) = delete;

    const std::string& getMessage() const;    
};

} // namespace linx

#endif // !EXCEPTION_HANDLER_H

ExceptionHandler.cpp

#include "ExceptionHandler.h"
#include "Logger.h"

namespace linx {

ExceptionHandler::ExceptionHandler( const std::string& message, bool saveInLog ) : 
_message( message ) {
    if( saveInLog ) {
        Logger::log( _message, Logger::TypeError );
    }
}

ExceptionHandler::ExceptionHandler( const std::ostringstream& streamMessage, bool saveInLog ) :
_message( streamMessage.str() ) {
    if( saveInLog ) {
        Logger::log( _message, Logger::TypeError );
    }
}

const std::string& ExceptionHandler::getMessage() const {
    return _message;
}

} // namespace linx

Logger.h

#include <sstream>
#include <array>

// For Windows Console Output
#define VC_EXTRALEAN 
#include <Windows.h>

namespace linx {

class Logger final : public Singleton {
public:
    enum LoggerType {
        TypeInfo = 0,
        TypeWarning,
        TypeError,
        TypeConsole
    };

private:
    std::string _logFilename;
    unsigned    _maxCharLength;

    std::array<std::string, 4> _logTypes;
    const std::string _unknownLogType;

    // This is for Windows Console Output - Can substitue with OS type
    HANDLE _hConsoleOutput;
    WORD   _consoleDefualtColor;
    // --------------------------------------------------------------- 

public:
    explicit Logger( const std::string& logFilename );
    virtual ~Logger();

    static void log( const std::string& text, LoggerType logType = TypeInfo );
    static void log( const std::ostringstream& text, LoggerType logType = TypeInfo );
    static void log( const char* text, LoggerType logType = TypeInfo );
};

} // namespace linx

#endif // !LOGGER_H

Logger.cpp

#include "Logger.h"
#include "TextFileWriter.h"

#include <iostream>
#include <iomanip>
#include <mutex>
// #include <shared_mutex>

namespace linx {

static Logger* s_pLogger = nullptr;

std::mutex  g_mutex;  // also removed the static storage qualifier.
//static std::shared_mutex s_mutex; // this was a wrong implementation

// White Text On Red Background
static const WORD WHITE_ON_RED = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY | BACKGROUND_RED; 

Logger::Logger( const std::string& logFilename ) :
Singleton( TypeLogger ),
_logFilename( logFilename ),
_maxCharLength( 0 ),
_unknownLogType( "UNKNOWN" ) {
    // Order must match types defined in Logger::LoggerType enum
    _logTypes[0] = "Info";
    _logTypes[1] = "Warning";
    _logTypes[2] = "Error";
    _logTypes[3] = ""; // Console

    // Find widest log type string
    _maxCharLength = _unknownLogType.size();
    for ( const std::string& logType : _logTypes ) {
        if( _maxCharLength < logType.size() ) {
            _maxCharLength = logType.size();
        }
    }

    // this was wrong
    //std::shared_lock<std::shared_mutex> lock( s_mutex );

    { // scope for lock_guard
        std::lock_guard<std::mutex> lock( g_mutex );

        // Start Log File
        TextFileWriter file( _logFilename, false, false );

        // Prepare Console  - Windows Console (can substitute with your OS)
        _hConsoleOutput = GetStdHandle( STD_OUTPUT_HANDLE );

        CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
        GetConsoleScreenBufferInfo( _hConsoleOutput, &consoleInfo );
        _consoleDefualtColor = consoleInfo.wAttributes;
        // End - Windows Console specific

        s_pLogger = this;
    } // end scope: lock_guard must be destroyed here 

    // Must destroy lock_guard to unlock mutex before calling this function
    // this function is derived from Singleton but it calls Logger::log() static method which in turn uses the same mutex to lock.
    logMemoryAllocation( true );
}

Logger::~Logger() {
    logMemoryAllocation( false );
    s_pLogger = nullptr;
}

void Logger::log( const std::string& text, LoggerType logType ) {
    log( text.c_str(), logType );
}

void Logger::log( const std::ostringstream& text, LoggerType logType ) {
    log( text.str().c_str(), logType );
}

void Logger::log( const char* text, LoggerType logType ) {
    if( nullptr == s_pLogger ) {
        std::cout << "Logger has not been initialized, can not log " << text << '\n';
        return;
    }

    // this is wrong
    //std::shared_lock<std::shared_mutex> lock( s_mutex );

    std::lock_guard<std::mutex> lock( g_mutex );

    std::ostringstream stream;

    // Default White Text On Red Background
    WORD textColor = WHITE_ON_RED;

    // Choose log type text string, display "UNKNOWN" if logType is out of range
    stream << std::setfill( ' ' ) << std::setw( s_pLogger->_maxCharLength );

    try {
        if( TypeConsole != logType ) {
            stream << s_pLogger->_logTypes.at( logType );
        }
        if( TypeWarning == logType ) {
            // Yellow
            textColor = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY | BACKGROUND_RED | BACKGROUND_GREEN;
        } else if( TypeInfo == logType ) {
            // Green
            textColor = FOREGROUND_GREEN;
        } else if( TypeConsole == logType ) {
            // Cyan
            textColor = FOREGROUND_GREEN | FOREGROUND_BLUE;
        }
    } catch( ... ) {
        stream << s_pLogger->_unknownLogType;
    }

    // Date & Time
    if( TypeConsole != logType ) {
        SYSTEMTIME time;
        GetLocalTime( &time );

        stream << " [" << time.wYear << '.'
            << std::setfill( '0' ) << std::setw( 2 ) << time.wMonth << '.'
            << std::setfill( '0' ) << std::setw( 2 ) << time.wDay << ' '
            << std::setfill( ' ' ) << std::setw( 2 ) << time.wHour << ':'
            << std::setfill( '0' ) << std::setw( 2 ) << time.wMinute << ':'
            << std::setfill( '0' ) << std::setw( 2 ) << time.wSecond << '.'
            << std::setfill( '0' ) << std::setw( 3 ) << time.wMilliseconds << "] ";
    }
    stream << text << '\n';

    // Log Message
    SetConsoleTextAttribute( s_pLogger->_hConsoleOutput, textColor );
    std::cout << stream.str();

    // Save same message to file
    try {
        TextFileWriter file( s_pLogger->_logFilename, true, false );
        file.write( stream.str() );
    } catch( ... ) {
        // Ignore, not saved in log file
        std::cout << __FUNCTION__ << " failed to write to file: " << stream.str() << '\n';
    }

    // Reset to default color
    SetConsoleTextAttribute( s_pLogger->_hConsoleOutput, s_pLogger->_consoleDefualtColor );
}

} // namespace linx

Singleton.h

#ifndef SINGLETON_H
#define SINGLETON_H

namespace linx {

class Singleton {
public:
    enum SingletonType {
        TypeLogger = 0, // MUST BE FIRST!
    };

private:
    SingletonType _type;

public:
    Singleton( const Singleton& c ) = delete;
    Singleton& operator=( const Singleton& c ) = delete;
    virtual ~Singleton();

protected:
    explicit Singleton( SingletonType type );
    void logMemoryAllocation( bool isAllocated ) const;
};

} // namespace linx

#endif // !SINGLETON_H

Single.cpp

#include "Singleton.h"
#include "Logger.h"
#include "ExceptionHandler.h"

#include <string>
#include <array>

namespace linx {

struct SingletonInfo {
    const std::string singletonName;
    bool  isConstructed;

    SingletonInfo( const std::string& singletonNameIn ) :
        singletonName( singletonNameIn ),
        isConstructed( false ) {}
};

// Order must match types defined in Singleton::SingletonType
static std::array<SingletonInfo, 1> s_aSingletons = { SingletonInfo( "Logger" ) };

Singleton::Singleton( SingletonType type ) :
_type( type ) {
    bool saveInLog = s_aSingletons.at( TypeLogger ).isConstructed;

    try {
        if( !s_aSingletons.at( type ).isConstructed ) {
            // Test Initialize Order
            for( int i = 0; i < type; ++i ) {
                if( !s_aSingletons.at( i ).isConstructed ) {
                    throw ExceptionHandler( s_aSingletons.at( i ).singletonName +
                                            " must be constructed before constructing " +
                                            s_aSingletons.at( type ).singletonName,
                                            saveInLog );
                }
            }
            s_aSingletons.at( type ).isConstructed = true;
        } else {
            throw ExceptionHandler( s_aSingletons.at( type ).singletonName +
                                    " can only be constructed once.",
                                    saveInLog );
        }
    } catch( std::exception& ) {
        // type is out of range
        std::ostringstream stream;
        stream << __FUNCTION__ << " Invalid Singleton Type specified: " << type;
        throw ExceptionHandler( stream, saveInLog );
    }
}

Singleton::~Singleton() {
    s_aSingletons.at( _type ).isConstructed = false;
}

void Singleton::logMemoryAllocation( bool isAllocated ) const {
    if( isAllocated ) {
        Logger::log( "Created " + s_aSingletons.at( _type ).singletonName );
    }  else {
        Logger::log( "Destroyed " + s_aSingletons.at( _type ).singletonName );
    }   
}

} // namespace linx

TextFileWriter

#ifndef TEXT_FILE_WRITER_H
#define TEXT_FILE_WRITER_H

#include "FileHandler.h"

namespace linx {

class TextFileWriter : public FileHandler {
public:
    explicit TextFileWriter( const std::string& filename, bool appendToFile, bool saveExceptionInLog = true );
    virtual ~TextFileWriter() = default;

    void write( const std::string& str );

    TextFileWriter( const TextFileWriter& ) = delete;
    TextFileWriter& operator=( const TextFileWriter& ) = delete;
};

} // namespace linx

#endif // !TEXT_FILE_WRITER_H

TextFileWriter.cpp

#include "TextFileWriter.h"

namespace linx {

TextFileWriter::TextFileWriter( const std::string& filename, bool appendToFile, bool saveExceptionInLog ) :
    FileHandler( filename, saveExceptionInLog ) {
    _file.open( _filenameWithPath.c_str(),
                std::ios_base::out | (appendToFile ? std::ios_base::app : std::ios_base::trunc) );
    if( !_file.is_open() ) {
        throwError( __FUNCTION__ + std::string( " can not open file for writing" ) );
    }
}

void TextFileWriter::write( const std::string& str ) {
    _file << str;
}

} // namespace linx

FileHandler.h

#ifndef FILE_HANDLER_H
#define FILE_HANDLER_H

#include <string>
#include <sstream>
#include <fstream>

namespace linx {

class FileHandler {
private:
    bool _saveExceptionInLog;

protected:
    std::fstream _file;
    std::string  _filePath;
    std::string  _filenameWithPath;

public:
    virtual ~FileHandler();

    FileHandler( const FileHandler& c ) = delete;
    FileHandler& operator=( const FileHandler& c ) = delete;

protected:
    FileHandler( const std::string& filename, bool saveExceptionInLog );
    void throwError( const std::string& message ) const;
    void throwError( const std::ostringstream& message ) const;

    bool getString( std::string& str, bool appendPath );
}; 

} // namespace linx

#endif // !FILE_HANDLER_H

FileHandler.cpp

#include "ExceptionHandler.h"
#include "Logger.h"

namespace linx {

ExceptionHandler::ExceptionHandler( const std::string& message, bool saveInLog ) : 
_message( message ) {
    if( saveInLog ) {
        Logger::log( _message, Logger::TypeError );
    }
}

ExceptionHandler::ExceptionHandler( const std::ostringstream& streamMessage, bool saveInLog ) :
_message( streamMessage.str() ) {
    if( saveInLog ) {
        Logger::log( _message, Logger::TypeError );
    }
}

const std::string& ExceptionHandler::getMessage() const {
    return _message;
}

} // namespace linx