Привет!
Об этой стратегии обработки ошибок пользователя я прочитал в книге
Professional C++ (2nd Edition) Скачать книгу (99МБ)+код к книге (464КБ) одним архивом (90МБ):
https://yadi.sk/d/VCvsky11c6wtP Подробное описание создания собственных классов исключений находится здесь: "Chapter 10. Handling Errors" -> "Exceptions and Polymorphism" -> "Writing Your Own Exception Classes"
Проверка: непустой ли входной аргументФункция printArray принимает массив и выбрасывает исключение EmptyError, если входной аргумент пуст:
Output:
Error: empty argument in the function printArray()
main.cpp
C++ (Qt)
#include <vector>
#include <iostream>
#include "freeFunctions.h"
int main()
{
std::vector<int> arr;
try {
printArray( arr );
} catch ( const LogicError &e ) {
std::cerr << e.what() << std::endl;
return 1;
} catch ( ... ) {
std::cerr << "Error: unknown expection" << std::endl;
return 1;
}
return 0;
}
freeFunctions.h
C++ (Qt)
#ifndef FREEFUNCTIONS_H
#define FREEFUNCTIONS_H
#include <vector>
#include <iostream>
#include "EmptyArgument.h"
void printArray( const std::vector<int> &arr )
throw ( EmptyArgument );
#endif // FREEFUNCTIONS_H
freeFunctions.cpp
C++ (Qt)
#include "freeFunctions.h"
void printArray( const std::vector<int> &arr )
throw ( EmptyArgument )
{
std::string functionName = "printArray()";
if ( arr.empty( ) ) {
throw EmptyArgument( functionName );
}
for ( std::size_t i = 0; i < arr.size(); ++i ) {
std::cout << arr[i] << std::endl;
}
}
EmptyArgument.h
C++ (Qt)
#ifndef EMPTYARGUMENT_H
#define EMPTYARGUMENT_H
#include <string>
#include "LogicError.h"
class EmptyArgument : public LogicError
{
public:
EmptyArgument( const std::string &functionName ) :
LogicError( functionName )
{
m_message = "Error: empty argument in the "
"function " + m_functionName;
}
};
#endif // EMPTYARGUMENT_H
LogicError.h
C++ (Qt)
#ifndef LOGICERROR_H
#define LOGICERROR_H
#include <string>
#include <stdexcept>
class LogicError : public std::logic_error
{
public:
LogicError( const std::string &functionName ) :
std::logic_error( "" ),
m_functionName( functionName ),
m_message( "" )
{
}
virtual ~LogicError( ) throw( )
{
}
virtual const char *what( ) const throw( )
{
return m_message.c_str( );
}
std::string message( ) const
{
return m_message;
}
protected:
std::string m_functionName;
std::string m_message;
};
#endif // LOGICERROR_H
Проверки: открыт ли файл, удачна ли запись, удачно ли чтениеЕсли файл не открывается, то выбрасывается исключение FileOpenError с текстом, к примеру:
Error: unable to open the file "input.txt" in the "readData()"
Если из файла невозможно прочитать, то - FileReadError с тектом:
Error: unable to read the file "input.txt" in the "readData()"
Если диск переполнен и невозможно записать, то - FileWriteError с текстом:
Error: unable to write to the file "input.txt" in the "readData()"
Все выше перечисленные исключения наследуются от FileError, поэтому мы можем все три исключения отлавливать так:
C++ (Qt)
try {
// ...
}
catch ( const FileError &e ) {
std::cerr << e.what( ) << std::endl;
return 1;
} catch ( ... ) {
std::cerr << "Error: unknown exception" << std::endl;
return 1;
}
Исключение EmptyArgument выбрасывается, когда входные данные пустые и наследуется от LogicError
Следующий пример читает имена и фамилии из файла, заполняет массив объектов типа Person и выводит имена на экран:
main.cpp
C++ (Qt)
#include <iostream>
#include <vector>
#include <QString>
#include "freeFunctions.h"
#include "Person.h"
int main( )
{
// Person array for saving
Person david( "David", "White");
Person ivan( "Ivan", "Green" );
std::vector<Person> persons;
persons.push_back( david );
persons.push_back( ivan );
try {
// Parse the person array to the string content
QString content;
parsePersonsToStrContent( persons, content );
// Save the string content to the file
QString fileName = "file.txt";
writeData( fileName, content );
// Read the string content from the file
QString readContent;
readData( fileName, readContent );
// Parse the string content to the person array
std::vector<Person> readPersons;
parseContentToPersons( readContent, readPersons );
// Print the person array on the screen
printData( readPersons );
} catch ( const LogicError &e ) {
std::cerr << e.what( ) << std::endl;
return 1;
} catch ( const FileError &e ) {
std::cerr << e.what( ) << std::endl;
return 1;
} catch ( ... ) {
std::cerr << "Error: unknown exception" << std::endl;
return 1;
}
return 0;
}
Person.h
C++ (Qt)
#ifndef PERSON_H
#define PERSON_H
#include <QString>
class Person {
public:
Person( const QString &firstName = "",
const QString &lastName = "" ) :
m_firstName( firstName ),
m_lastName( lastName )
{
}
QString firstName( ) const
{
return m_firstName;
}
QString lastName( ) const
{
return m_lastName;
}
void setFirstName( const QString &firstName )
{
m_firstName = firstName;
}
void setLastName( const QString &lastName )
{
m_lastName = lastName;
}
private:
QString m_firstName;
QString m_lastName;
};
#endif // PERSON_H
freeFunctions.h
C++ (Qt)
#ifndef FREEFUNCTIONS_H
#define FREEFUNCTIONS_H
#include <vector>
#include <QString>
#include "FileOpenError.h"
#include "FileReadError.h"
#include "FileWriteError.h"
#include "EmptyArgument.h"
#include "Person.h"
void readData( const QString &fileName, QString &content )
throw ( EmptyArgument, FileOpenError, FileReadError );
void parseContentToPersons( const QString &content,
std::vector<Person> &persons )
throw ( EmptyArgument );
void parsePersonsToStrContent( const std::vector<Person> &persons,
QString &content)
throw ( EmptyArgument );
void writeData( const QString &fileName,
const QString &content )
throw ( EmptyArgument, FileOpenError, FileWriteError );
void printData( const std::vector<Person> &persons )
throw ( EmptyArgument );
#endif // FREEFUNCTIONS_H
freeFunctions.cpp
C++ (Qt)
#include <iostream>
#include <string>
#include <QFile>
#include <QRegExp>
#include <QTextStream>
#include <QDebug>
#include "freeFunctions.h"
void readData(const QString &fileName, QString &content )
throw ( EmptyArgument, FileOpenError, FileReadError )
{
std::string functionName = "readData()";
// Check argument
if ( fileName.isEmpty( ) ) {
throw EmptyArgument( functionName );
}
// Open the input file for reading
QFile file( fileName );
if( !file.open( QIODevice::ReadOnly ) ) {
throw FileOpenError( fileName.toStdString( ), functionName );
}
// Read the content from the file
QByteArray data = file.readAll( );
if ( data.isEmpty( ) ) {
throw FileReadError( fileName.toStdString( ), functionName );
}
content = QString( data );
}
void parseContentToPersons( const QString &content, std::vector<Person> &persons )
throw ( EmptyArgument )
{
std::string functionName = "parseContentToPersons()";
// Check the input argument
if ( content.isEmpty( ) ) {
throw EmptyArgument( functionName );
}
QRegExp regExp("(\\w+) (\\w+)");
int pos = 0;
while ( ( pos = regExp.indexIn( content, pos ) ) != -1 ) {
QString firstName = regExp.cap( 1 );
QString lastName = regExp.cap( 2 );
Person person( firstName, lastName );
persons.push_back( person );
pos += regExp.matchedLength( );
}
}
void parsePersonsToStrContent( const std::vector<Person> &persons,
QString &content)
throw ( EmptyArgument )
{
std::string functionName = "parsePersonsToStrContent()";
// Check the input argument
if ( persons.empty( ) ) {
throw EmptyArgument( functionName );
}
for ( std::size_t i = 0; i < persons.size( ); ++i ) {
QString firstName = persons[i].firstName( );
QString lastName = persons[i].lastName( );
QString line = QString( "%1 %2\n" ).arg( firstName ).arg( lastName );
content.append( line );
}
}
void writeData( const QString &fileName, const QString &content )
throw ( EmptyArgument, FileOpenError, FileWriteError )
{
std::string functionName = "writeData()";
// Check arguments
if ( fileName.isEmpty( ) || content.isEmpty( ) ) {
throw EmptyArgument( functionName );
}
// Open the output file for writing
QFile file( fileName );
if ( !( file.open( QIODevice::WriteOnly ) ) ) {
throw FileOpenError( fileName.toStdString( ), functionName );
}
// Write data to the output file
QTextStream stream( &file );
stream << content;
if ( stream.status() != QTextStream::Ok ) {
throw FileWriteError( fileName.toStdString( ), functionName );
}
}
void printData( const std::vector<Person> &persons )
throw ( EmptyArgument )
{
std::string functionName = "printData()";
// Check the input argument
if ( persons.empty( ) ) {
throw EmptyArgument( functionName );
}
// Print data
for ( std::size_t i = 0; i < persons.size( ); ++i ) {
std::cout << "First Name: " << persons[i].firstName( ).toStdString( ) << std::endl;
std::cout << "Last Name: " << persons[i].lastName( ).toStdString( ) << std::endl;
std::cout << std::endl;
}
}
FileError.h
C++ (Qt)
#ifndef FILEERROR_H
#define FILEERROR_H
#include <string>
#include <stdexcept>
class FileError : public std::runtime_error
{
public:
FileError( const std::string &fileName,
const std::string &functionName) :
std::runtime_error( "" ),
m_message( "" ),
m_fileName( fileName ),
m_functionName( functionName )
{
}
virtual ~FileError( ) throw( )
{
}
virtual const char *what() const throw( )
{
return m_message.c_str( );
}
std::string message( ) const
{
return m_message;
}
protected:
std::string m_message;
std::string m_fileName;
std::string m_functionName;
};
#endif // FILEERROR_H
FileOpenError.h
C++ (Qt)
#ifndef FILEOPENERROR_H
#define FILEOPENERROR_H
#include <string>
#include "FileError.h"
class FileOpenError : public FileError {
public:
FileOpenError( const std::string &fileName,
const std::string &functionName) :
FileError( fileName, functionName )
{
m_message = "Error: unable to open the file \"" +
m_fileName + "\" in the function \"" +
m_functionName + "\"";
}
};
#endif // FILEOPENERROR_H
FileReadError.h
C++ (Qt)
#ifndef FILEREADERROR_H
#define FILEREADERROR_H
#include <string>
#include "FileError.h"
class FileReadError : public FileError {
public:
FileReadError( const std::string &fileName,
const std::string &functionName ) :
FileError( fileName, functionName )
{
m_message = "Error: unable to read the file \"" + m_fileName +
"\" in the function \"" + m_functionName + "\"";
}
};
#endif // FILEREADERROR_H
FileWriteError.h
C++ (Qt)
#ifndef FILEWRITEERROR_H
#define FILEWRITEERROR_H
#include <string>
#include "FileError.h"
class FileWriteError : public FileError {
public:
FileWriteError( const std::string &fileName,
const std::string &functionName ) :
FileError( fileName, functionName )
{
m_message = "Error: unable to write to the file " +
m_fileName + " in the function " + m_functionName;
}
};
#endif // FILEWRITEERROR_H
EmptyArgument.h
C++ (Qt)
#ifndef EMPTYARGUMENT_H
#define EMPTYARGUMENT_H
#include <string>
#include "LogicError.h"
class EmptyArgument : public LogicError {
public:
EmptyArgument( const std::string &functionName ) :
LogicError( functionName )
{
m_message = "Error: empty argument in the "
"function " + m_functionName;
}
};
#endif // EMPTYARGUMENT_H
LogicError.h
C++ (Qt)
#ifndef LOGICERROR_H
#define LOGICERROR_H
#include <string>
#include <stdexcept>
class LogicError : public std::logic_error
{
public:
LogicError( const std::string &functionName ) :
std::logic_error( "" ),
m_functionName( functionName ),
m_message( "" )
{
}
virtual ~LogicError( ) throw( )
{
}
virtual const char *what( ) const throw( )
{
return m_message.c_str( );
}
std::string message( ) const
{
return m_message;
}
protected:
std::string m_functionName;
std::string m_message;
};
#endif // LOGICERROR_H
Проверка деления на ноль и выхода из диапазонаВ моём примере есть функция divide(a, b), где a и b должны быть в диапазоне [-1000, 1000]. Если один из параметров превысил этот диапазон, то функция выбросит исключение с текстом:
Error: values must be from the range [-1000, 1000] in the function Calculator::divide()
Допустим в моём проекте 10 классов, и в каждой по несколько функций, которые должны следить за диапазоном своих аргументов. Мне при написании этих функций достаточно выбросить после проверки исключение:
C++ (Qt)
throw OutOfRange<int>( beginOfRange, endOfRange, functionName );
main.cpp
C++ (Qt)
#include <iostream>
#include "Calculator.h"
int main()
{
Calculator<float> calculator;
try {
float result = calculator.divide( 24.7f, 3.0f );
std::cout << "Result = " << result << std::endl;
} catch ( const LogicError &e ) {
std::cerr << e.what() << std::endl;
return 1;
} catch ( ... ) {
std::cerr << "Error: unknown expection" << std::endl;
return 1;
}
return 0;
}
Calculator.h
C++ (Qt)
#ifndef CALCULATOR_H
#define CALCULATOR_H
#include <string>
#include "DivideByZero.h"
#include "OutOfRange.h"
template <typename Type>
class Calculator
{
public:
// Divide nums from the range [-1000, 1000]
Type divide( Type a, Type b )
throw ( DivideByZero, OutOfRange<int> )
{
std::string functionName = "Calculator::divide()";
if ( b == 0 ) {
throw DivideByZero( functionName );
}
const int beginOfRange = -1000;
const int endOfRange = 1000;
if ( ( a < beginOfRange ) || ( a > endOfRange ) ||
( b < beginOfRange ) || ( b > endOfRange ) )
{
throw OutOfRange<int>( beginOfRange,
endOfRange,
functionName );
}
return a / b;
}
};
#endif // CALCULATOR_H
DivideByZero.h
C++ (Qt)
#ifndef DIVIDEBYZERO_H
#define DIVIDEBYZERO_H
#include <string>
#include "LogicError.h"
class DivideByZero : public LogicError
{
public:
DivideByZero( const std::string &functionName ) :
LogicError( functionName )
{
m_message = "Error: divide by zero in the "
"function " + m_functionName;
}
};
#endif // DIVIDEBYZERO_H
OutOfRange.h
C++ (Qt)
#ifndef OUTOFRANGE_H
#define OUTOFRANGE_H
#include <string>
#include "LogicError.h"
template <typename Type>
class OutOfRange : public LogicError
{
public:
OutOfRange( Type beginOfRange,
Type endOfRange,
const std::string &functionName ) :
LogicError( functionName )
{
m_message = "Error: values must be from the range "
"[" + std::to_string( beginOfRange ) +
", " + std::to_string( endOfRange ) + "]"
" in the function " + m_functionName;
}
};
#endif // OUTOFRANGE_H
LogicError.h
C++ (Qt)
#ifndef LOGICERROR_H
#define LOGICERROR_H
#include <string>
#include <stdexcept>
class LogicError : public std::logic_error
{
public:
LogicError( const std::string &functionName ) :
std::logic_error( "" ),
m_functionName( functionName ),
m_message( "" )
{
}
virtual ~LogicError( ) throw( )
{
}
virtual const char *what( ) const throw( )
{
return m_message.c_str( );
}
std::string message( ) const
{
return m_message;
}
protected:
std::string m_functionName;
std::string m_message;
};
#endif // LOGICERROR_H
Интересные ссылки:
Правило 8: Не позволяйте исключениям покидать деструкторыПравило 25: Подумайте о поддержке функции swap, не возбуждающей исключенийПравило 29: Стремитесь, чтобы программа была безопасна относительно исключенийException Safety | QtDoc 5.3