Russian Qt Forum

Qt => Общие вопросы => Тема начата: 8Observer8 от Март 24, 2014, 12:12



Название: [РЕШЕНО] Обработка ошибок без исключений и с помощью исключений
Отправлено: 8Observer8 от Март 24, 2014, 12:12
10/18/2014 По итогу этой темы написал небольшую статью: Стратегия обработки ошибок пользователя с помощью собственных классов исключений (http://www.prog.org.ru/topic_27797_0.html)

Привет!

Расскажите, пожалуйста, о своём опыте, как вы на практике обрабатываете ошибки в ваших приложениях на Qt. Применяете исключения или нет? Чем чревато применение исключений в приложениях на Qt? В применении исключений больше пользы или вреда? Улучшают ли исключения архитектуру программного проекта?

У меня есть пример. В этом примере открывается текстовый файл, из него читаются данные и данные записываются в другой текстовый файл.

Как я понимаю, есть несколько способов возвращать ошибку и значение из функции:

1) Функция возвращает 0 при отсутствии ошибок и -1 в случае ошибки. Посмотреть код ошибки можно с помощью глобальной переменной errno (глобальной для потока). Значение функции возвращаем через ссылку-параметр функции.

2) Функция возвращает 0 в случае ошибки, так как 0 в C и C++ - это false. Функцию, тогда можно вызывать так: if ( func( ) ). Посмотреть код ошибки можно с помощью глобальной переменной errno. Значение функции возвращаем через ссылку-параметр функции.

3) Функция возвращает, как значение, так и код ошибки через структуру, либо класс, либо std::pair, либо std::tuple

4) Слышал, есть ещё способ с nullptr. Но так пример и не нашёл.

5) Ещё я слышал есть механизм setjmp()/longjmp(). Но его в C++ нельзя использовать, там какая-то замута с конструкторами, деструкторами и стеком. Да и в C его не используют (или не рекомендуют)

6) Функция возвращает 0 при отсутствии ошибок и код ошибки (errno не используется). Значение функции возвращаем через ссылку-параметр функции (можно сделать наоборот, что функция будет возвращать значение, а код ошибки через ссылку-параметр).

Пример без исключений:
Код
C++ (Qt)
#include <QCoreApplication>
#include <QFile>
#include <QTextStream>
#include <iostream>
 
//! Error codes
enum ErrorType {
   //! None errors
   errNone = 0,
 
   //! Error of the opening of a file
   errFileOpen = -1000,
 
   //! Error of the writing of a file
   errFileWrite = -1001,
 
   //! Wrong data
   errDataBad = -5000
};
 
ErrorType readData( const QString &fileName, QString &data );
ErrorType writeResult( const QString &fileName, QString &data );
void showData( const QString &data );
ErrorType showError( const QString &fileName, ErrorType errorCode );
 
/*!
* Working with a text file
*/

int main( int argc, char *argv[] ) {
   QCoreApplication a( argc, argv );
 
   QString data;
   ErrorType errorCode = ErrorType::errNone;
 
   // Read data from a file
   QString iFileName = QString( "input.txt" );
   errorCode = readData( iFileName, data );
   if ( errorCode != ErrorType::errNone ) {
       return showError( iFileName, errorCode );
   }
 
   // Show data on the screen
   showData( data );
 
   // Data processing
   data += QString( ", World" );
 
   // Write data to a file
   QString oFileName = QString( "output.txt" );
   writeResult( oFileName, data );
   if ( errorCode != ErrorType::errNone ) {
       return showError( oFileName, errorCode );
   }
 
   return a.exec( );
}
 
/*!
* Read data from the file
* \param fileName File name
* \param data data
* \return Error code
*/

ErrorType readData( const QString &fileName, QString &data ) {
   QFile file( fileName );
   if ( !file.open( QIODevice::ReadOnly ) ) {
       return ErrorType::errFileOpen;
   }
 
   data = file.readAll( );
 
   file.close( );
   return ErrorType::errNone;
}
 
/*!
* Write data to the file
* \param fileName Fleile name
* \param data Data
* \return Error code
*/

ErrorType writeResult( const QString &fileName, QString &data ) {
   QFile file( fileName );
 
   if ( !file.open( QIODevice::WriteOnly ) ) {
       return ErrorType::errFileOpen;
   }
 
   QTextStream stream( &file );
   stream << data;
   file.close( );
 
   if ( stream.status( ) != QTextStream::Ok ) {
       return ErrorType::errFileWrite;
   }
 
   return ErrorType::errNone;
}
 
/*!
* Show data on the screen
* \param data Data
*/

void showData( const QString &data ) {
   std::cout << data.toStdString( ) << std::endl;
}
 
/*!
* Write the error code to the file
* \param fileName File name
* \param errorCode Error code
* \return Error code
*/

ErrorType showError( const QString &fileName, ErrorType errorCode ) {
   switch ( errorCode ) {
       case ErrorType::errFileOpen:
           std::cerr << "Error: cannot open the file " << fileName.toStdString( ) << std::endl;
           break;
       case ErrorType::errFileWrite:
           std::cerr << "Error: cannot write to the file " << fileName.toStdString( ) << std::endl;
           break;
       case ErrorType::errDataBad:
           std::cerr << "Error: incorrect data in the file " << fileName.toStdString( ) << std::endl;
           break;
       default:
           std::cerr << "Error code: " << errorCode << "; file name: " << fileName.toStdString( ) << std::endl;
           break;
   }
 
   return errorCode;
}
 

Пример с исключениями

Код
C++ (Qt)
#include <QCoreApplication>
#include <QFile>
#include <QTextStream>
#include <QVector>
#include <iostream>
#include <exception>
 
void readData( const QString &fileName, QVector<int> &arr )
throw (std::invalid_argument, std::runtime_error);
 
void writeResult( const QString &fileName, QVector<int> &arr )
throw (std::invalid_argument, std::runtime_error);
 
void showData( const QVector<int> &arr );
 
/*!
* Working with a text file
*/

int main( int argc, char *argv[] ) {
   QCoreApplication a( argc, argv );
 
   // Read data from a file
   QString iFileName = QString( "input.txt" );
   QVector<int> arr;
   try {
       readData( iFileName, arr );
   } catch ( const std::exception &e ) {
       std::cerr << e.what( ) << std::endl;
       return 1;
   }
 
   // Show data on the screen
   showData( arr );
 
   // Data processing
   arr.push_back( 100 );
 
   // Write data to a file
   QString oFileName = QString( "output.txt" );
   try {
       writeResult( oFileName, arr );
   } catch ( const std::exception &e ) {
       std::cerr << e.what( ) << std::endl;
       return 1;
   }
 
   return a.exec( );
}
 
/*!
* Read data from the file
* \param fileName File name
* \param data data
*/

void readData( const QString &fileName, QVector<int> &dest )
throw (std::invalid_argument, std::runtime_error) {
   QFile file( fileName );
 
   if ( !file.open( QIODevice::ReadOnly ) ) {
       std::string error = "Unable to open the file " + fileName.toStdString( );
       throw std::invalid_argument( error );
   }
 
   QTextStream stream( &file );
 
   QString input;
   do {
       stream >> input;
       bool ok;
       int value = input.toInt( &ok )


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Bepec от Март 24, 2014, 13:10
Есть ещё подход аля winapi.

Выполняем функцию, запрашивает GetLastError().
Если 0 - значит всё хорошо. Если другое - ппц всё пропало.

Т.е. 1 глобальная функция, возвращающая состояние последней выполненной функции.


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: 8Observer8 от Март 24, 2014, 15:36
Спасибо! Я забыл про этот подход написать. Пользовался им когда-то. Здесь можно ошибки смотреть:
- http://msdn.microsoft.com/en-us/library/cc231199.aspx
- http://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx
но этот подход системно зависимый, в отличае от глобальной переменной errno.

Так что же по поводу вопросов в начале темы? Исключения в приложениях на Qt это благо? Или лучше их избегать?


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Bepec от Март 24, 2014, 15:58
Это С++. Тут можно что угодно во благо, если умеешь. Но Qt не использует исключения, по слухам это позиция разработчиков.


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: 8Observer8 от Март 24, 2014, 16:26
Спасибо! Этот ответ мне нравится :) Значит не зря я копаю в сторону исключений :)

Лучше всего об исключениях написано в этой книге (много примеров):
Название: Professional C++
Год: 2011
Автор: Marc Gregoire, Nicholas A. Solter, Scott J. Kleper
Количество страниц: 1104
Язык: английский
Скачать: http://kickass.to/wrox-professional-c-plus-plus-2nd-edition-2011-retail-ebook-debt-t7461950.html
Исходники: http://www.wrox.com/WileyCDA/WroxTitle/Professional-C-2nd-Edition.productCd-0470932449,descCd-DOWNLOAD.html

Я применил их подход к своей программке с чтением и записью в файл. Здесь создаются свои классы исключений, что позволяет, к примеру, указать строку, с некорректными данными: Error reading input.txt at line 2. Применяются наследование и полиморфизм.

Правда в консоль (Application Output) почему-то выводится сообщение: Cannot obtain a handle to the inferior: The parameter is incorrect.

И ещё одна проблема. В классе FileError мне пришлось применить std::string вместо QString здесь:
Код
C++ (Qt)
protected:
   QString mFile;
   std::string mMsg;
};

Потому что я не могу использовать QString тут:
Код
C++ (Qt)
virtual const char* what( ) const noexcept {
   return mMsg.c_str();
}
 

Заранее спасибо за помощь!

Вот весь код проекта:
text_file.pro
Код
C++ (Qt)
#-------------------------------------------------
#
# Project created by QtCreator 2014-03-16T19:25:22
#
#-------------------------------------------------
 
QT       += core
 
QT       -= gui
 
QMAKE_CXXFLAGS += -std=c++11
 
TARGET = text_file
CONFIG   += console
CONFIG   -= app_bundle
 
TEMPLATE = app
 
 
SOURCES += main.cpp
 
HEADERS += \
   FileError.h \
   FileOpenError.h \
   FileReadError.h \
   FileWriteError.h
 

FileError.h
Код
C++ (Qt)
#ifndef FILEERROR_H
#define FILEERROR_H
 
#include <QString>
#include <exception>
#include <string>
 
class FileError : public std::runtime_error {
public:
 
   FileError( const QString &fileIn ) : std::runtime_error( "" ), mFile( fileIn ) {
   }
 
   virtual const char* what( ) const noexcept {
       return mMsg.c_str( );
   }
 
   QString getFileName( ) const {
       return mFile;
   }
 
protected:
   QString mFile;
   std::string mMsg;
};
 
#endif // FILEERROR_H
 

FileOpenError.h
Код
C++ (Qt)
#ifndef FILEOPENERROR_H
#define FILEOPENERROR_H
 
#include <QString>
#include "FileError.h"
 
class FileOpenError : public FileError {
public:
 
   FileOpenError( const QString &fileNameIn ) : FileError( fileNameIn ) {
       //mMsg = QString("Unable to open ") + fileNameIn;
       mMsg = "Unable to open " + fileNameIn.toStdString( );
   }
};
 
#endif // FILEOPENERROR_H
 

FileReadError.h
Код
C++ (Qt)
#ifndef FILEREADERROR_H
#define FILEREADERROR_H
 
#include "FileError.h"
#include <QString>
#include <QTextStream>
#include <sstream>
 
class FileReadError : public FileError {
public:
 
   FileReadError( const QString &fileNameIn, int lineNumIn ) :
   FileError( fileNameIn ), mLineNum( lineNumIn ) {
       //        QTextStream stream(stdout);
 
       //        //stream << QString("Error reading ") << fileNameIn << QString(" at line ") << lineNumIn;
       //        mMsg = QString("Error reading %1 at line %2").arg(fileNameIn).arg(lineNumIn);
       //        stream >> mMsg;
       std::ostringstream ostr;
 
       ostr << "Error reading " << fileNameIn.toStdString( ) << " at line " << lineNumIn;
       mMsg = ostr.str( );
   }
 
   int getLineNum( ) const {
       return mLineNum;
   }
 
protected:
   int mLineNum;
};
 
#endif // FILEREADERROR_H
 

FileWriteError.h
Код
C++ (Qt)
#ifndef FILEWRITEERROR_H
#define FILEWRITEERROR_H
 
#include <QString>
#include "FileError.h"
 
class FileWriteError : public FileError {
public:
 
   FileWriteError( const QString &fileNameIn ) : FileError( fileNameIn ) {
       //mMsg = QString( "Unable to write " ) + fileNameIn;
       mMsg = "Unable to write " + fileNameIn.toStdString( );
   }
};
 
#endif // FILEWRITEERROR_H
 

main.cpp
Код
C++ (Qt)
#include <QCoreApplication>
#include <QFile>
#include <QTextStream>
#include <QVector>
#include <iostream>
#include "FileOpenError.h"
#include "FileReadError.h"
#include "FileWriteError.h"
 
void readData( const QString &fileName, QVector<int> &arr )
throw (FileOpenError, FileReadError);
 
void writeResult( const QString &fileName, QVector<int> &arr )
throw (FileOpenError, FileWriteError);
 
void showData( const QVector<int> &arr );
 
/*!
* Working with a text file
*/

int main( int argc, char *argv[] ) {
   QCoreApplication a( argc, argv );
 
   // Read data from a file
   QString iFileName = QString( "input.txt" );
   QVector<int> arr;
   try {
       readData( iFileName, arr );
   } catch ( const FileError &e ) {
       std::cerr << e.what( ) << std::endl;
       return 1;
   }
 
   // Show data on the screen
   showData( arr );
 
   // Data processing
   arr.push_back( 100 );
 
   // Write data to a file
   QString oFileName = QString( "output.txt" );
   try {
       writeResult( oFileName, arr );
   } catch ( const FileError &e ) {
       std::cerr << e.what( ) << std::endl;
       return 1;
   }
 
   return a.exec( );
}
 
/*!
* Read data from the file
* \param fileName File name
* \param data data
*/

void readData( const QString &fileName, QVector<int> &dest )
throw (FileOpenError, FileReadError) {
   QFile file( fileName );
 
   if ( !file.open( QIODevice::ReadOnly ) ) {
       throw FileOpenError( fileName );
   }
 
   QTextStream stream( &file );
 
   QString input;
   int lineNumber = 0;
   do {
       lineNumber++;
       stream >> input;
       bool ok;
       int value = input.toInt( &ok );
       if ( (ok) && (stream.status( ) == QTextStream::Ok) ) {
           dest.push_back( value );
       } else if ( !stream.atEnd( ) ) {
           throw FileReadError( fileName, lineNumber );
       }
   } while ( !stream.atEnd( ) );
}
 
/*!
* Write data to the file
* \param fileName Fleile name
* \param data Data
* \return Error code
*/

void writeResult( const QString &fileName, QVector<int> &arr )
throw (FileOpenError, FileWriteError) {
   QFile file( fileName );
 
   if ( !file.open( QIODevice::WriteOnly ) ) {
       throw FileOpenError( fileName );
   }
 
   QTextStream stream( &file );
   for ( int i = 0; i < arr.size( ); ++i ) {
       stream << arr[i];
       if ( i != (arr.size( ) - 1) ) {
           stream << " ";
       } else {
           stream << endl;
       }
   }
   file.close( );
 
   if ( stream.status( ) != QTextStream::Ok ) {
       throw FileWriteError( fileName );
   }
}
 
/*!
* Show data on the screen
* \param data Data
*/

void showData( const QVector<int> &arr ) {
   for ( int i = 0; i < arr.size( ); ++i ) {
       std::cout << QString::number( arr[i] ).toStdString( ) << std::endl;
   }
}
 


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: OKTA от Март 24, 2014, 16:46
Лично мне кажется, что использовать исключения для обработки чего-то из разряда
Код:
if ( !file.open( QIODevice::ReadOnly ) ) {
        throw FileOpenError( fileName );
    }
крайне неудобно, затратно и бесполезно.. ???


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: 8Observer8 от Март 24, 2014, 17:29
Затратно по времени набора кода? Но ведь классы придумали, чтобы повторно использовать код. Достаточно скопипастить себе эти классы с исключениями в следующий проект. Ну конечно, можно скопипастить showError() и перечисления с кодами ошибок.
Да, в данном случае можно было и так поступить:
Код
C++ (Qt)
   string inFileName = "input.txt";
   ifstream in;
   in.open( inFileName.c_str( ) );
   if ( !in.is_open( ) ) {
       cerr << "Error: could not open the file " << inFileName.c_str( ) << endl;
       return 1;
   }
 

Но я поставил себе другую задачу. Мне предложили способ обработки без исключений с применением кодов ошибок и функции showError() Я перечислил сколько есть подходов для обработки ошибок в приложениях без исключений (может их намного больше). При использовании других классов со своим способом обработки ошибок, придётся выкручиваться (см. способы обработки ошибок) Исключения унифицируют подход и делают его единообразным. Поэтому это не настолько бесполезно, как может показаться на первый взгляд.

Пользователю функции readData достаточно будет написать следующий код для вывода разного рода ошибок:
Код
C++ (Qt)
   // Read data from a file
   QString iFileName = QString( "input.txt" );
   QVector<int> arr;
   try {
       readData( iFileName, arr );
   } catch ( const FileError &e ) {
       std::cerr << e.what( ) << std::endl;
       return 1;
   }
 



Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Bepec от Март 24, 2014, 18:46
Моё имхо - исключение нужно выбрасывать в случае ИСКЛЮЧИТЕЛЬНОЙ ситуации. Ситуаций исключительных в которых программа может что-то сделать - по пальцам пересчитать.

Потому в %80 программ они нафиг не нужны. А 20% сделаны по привычке.


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: 8Observer8 от Март 24, 2014, 18:48
Отсутствие файла и некорректные данные в нём - это как раз примеры исключительный ситуаций. В этом случае программа может вывести текст на экран.


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Bepec от Март 24, 2014, 19:22
Это стандартная ситуация. Такая же как отсутствие библиотек или некорректные действия пользователя. Исключительная ситуация - это ситуация, которая не предусмотрена программой.


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Old от Март 24, 2014, 19:24
Это стандартная ситуация. Такая же как отсутствие библиотек или некорректные действия пользователя. Исключительная ситуация - это ситуация, которая не предусмотрена программой.
Как можно выбросить исключение не предусмотренное программой? :)


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: _OLEGator_ от Март 24, 2014, 19:31
Проблема использования исключений по любому поводу - это потенциальная возможность не обработать какое-то одно исключение и программа упадет.


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Igors от Март 24, 2014, 19:36
Отсутствие файла и некорректные данные в нём - это как раз примеры исключительный ситуаций. В этом случае программа может вывести текст на экран.
Нет, эта ситуация не является исключительной. Общее правило: если у Вас не возникает никаких трудностей с обычной/нормальной обработкой ошибки - исключение ни к чему. А вот тоже чтение из файла,
Код
C++ (Qt)
void ReadSomeData( stream & strm, std::vector <MyData> & vec )
{
int count;
stream >> count;
if (stream.bad()) throw ReadException;  
for (int i = 0; i < count; ++i) {
  vec.push_back(MyData());
  stream >> vec.back();
}
}
Если ReadSomeData глубоко вложена - то скорее всего исключение уместно, т.к. слишком дорого обойдется передача ошибки "на самый верх"


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Old от Март 24, 2014, 19:36
Проблема использования исключений по любому поводу - это потенциальная возможность не обработать какое-то одно исключение и программа упадет.
Лучше пусть программа завершится с выводом информации о необработанном исключении, чем она будет падать с Segmentation fault. :)


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Old от Март 24, 2014, 19:40
Общее правило: если у Вас не возникает никаких трудностей с обычной/нормальной обработкой ошибки - исключение ни к чему.
Это что за "общее правило" такое?
Это предложение набор общих слов, ничего не выражающих. Прочтите его. :)


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Igors от Март 24, 2014, 19:50
Это что за "общее правило" такое?
Это предложение набор общих слов, ничего не выражающих. Прочтите его. :)
Уймитесь :) 


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Old от Март 24, 2014, 19:53
Уймитесь :) 
Это вы уймитесь нести ерунду и называть это "общими правилами". Таких правил нет и никогда не будет, потому что это бред. :)
Кстати ваш же пример с ReadSomeData это подтверждает. :)


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Igors от Март 24, 2014, 19:57
Это вы уймитесь нести ерунду и называть это "общими правилами". Таких правил нет и никогда не будет, потому что это бред. :)
Библию почитайте, мелкий скандалист  :)


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Old от Март 24, 2014, 19:59
Библию почитайте, мелкий скандалист  :)
Какую библию?
Я у вас уже несколько раз спрашивал, что вы под этим подразумеваете, вы всегда отмалчиваетесь. :)
Ну ка, говорите, опять хабра начитались? Или видеокурсы "C++ за пять занятий" смотрели? :)


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Bepec от Март 24, 2014, 21:12
Old, Igors дело говорит.

Исключение для чего нужно? Исключение это возможность проброса данных из глубины наверх. Каждый обработчик решает простое условие. "Я могу обработать данное исключение? Да - обрабатываю. Нет - пробрасываю дальше.".  Для обработки, для реакции на событие. Но если обработка возможна на уровне ошибки (открытие файла) тогда исключение не нужно.

Если же программа упала, значит эта ситуация не была предусмотрена разработчиками. Значит никакой информации об ошибке быть не может, ибо разработчики информации не оставили.  Так называемое необработанное исключение.

PS если вы мне подскажете документацию, в которой описывается как поймать "обращение по неинициилизированном указателю, выход за пределы массива, порчу памяти программы" и исправить, я буду благодарен. Для развития кругозора почитаю.

PPS Segmentation fault - исключение, насколько мне известно.
Цитировать
В Microsoft Windows, процесс, получающий доступ к недействительным участкам памяти, создаёт исключение STATUS_ACCESS_VIOLATION


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Old от Март 24, 2014, 21:46
Old, Igors дело говорит.
Это ерунда. Обычная. Набор слов выданных с важным видом. :)

Мы пишем функцию ReadSomeData, как нам решить использовать исключения или нет. Мы понятия не имеем, кто и когда эту функцию будет вызывать: кто-то ее позовет из main, а кто-то ее позовет из 100500 вложенной друг в друг функции.
Поэтому, это выдуманное "правило" обычный бред, выданный с важным видом что-бы произвести впечатление. :)

Если же программа упала, значит эта ситуация не была предусмотрена разработчиками. Значит никакой информации об ошибке быть не может, ибо разработчики информации не оставили.  Так называемое необработанное исключение.
Если программа упала, значит в ней содержится ошибка, а не исключительное состояние. Не надо их путать.
Если программа завершилась из-за необработанного исключения, то разработчик не предусмотрел его обработку, но вся информацию по исключению есть и она передается вместе с исключением.

PS если вы мне подскажете документацию, в которой описывается как поймать "обращение по неинициилизированном указателю, выход за пределы массива, порчу памяти программы" и исправить, я буду благодарен. Для развития кругозора почитаю.
Еще раз, не нужно путать ошибки в программе и исключительные ситуации, это совершенно разные вещи.

PPS Segmentation fault - исключение, насколько мне известно.
Нет.


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: 8Observer8 от Март 25, 2014, 07:10
PS если вы мне подскажете документацию, в которой описывается как поймать "обращение по неинициилизированном указателю, выход за пределы массива, порчу памяти программы" и исправить, я буду благодарен. Для развития кругозора почитаю.

> выход за пределы массива
Вот документация: http://www.cplusplus.com/reference/stdexcept/out_of_range/

Вот пример из документации:
Код
C++ (Qt)
// out_of_range example
#include <iostream>       // std::cerr
#include <stdexcept>      // std::out_of_range
#include <vector>         // std::vector
 
int main( void ) {
   std::vector<int> myvector( 10 );
   try {
       myvector.at( 20 ) = 100; // vector::at throws an out-of-range
   } catch ( const std::out_of_range& oor ) {
       std::cerr << "Out of Range error: " << oor.what( ) << '\n';
   }
   return 0;
}

По поводу обычных массивов и "обращения по неинициилизированном указателю" Это учли в современных языках программирования таких как C# и Java, в которых без исключений никуда. К сожалению, С++ не обязывает разработчиков применять исключения.


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: 8Observer8 от Март 25, 2014, 09:34
Нам всё равно придёться мириться с исключениями, так как их выбрасывают многие функции STD. Если мы их не будем отлавливать, то программы будут падать без всяких сообщений.

Я не вижу в чём принципиальная разница между двумя подходами по обработке ошибок. Чем же здесь плохи исключения? Сравните в чём разница для пользователя, который вызывает функцию readData( iFileName, arr ). Исключения выполняют, в данном случае, туже самую работу:
Код
C++ (Qt)
   // Read data from a file
   QString iFileName = QString( "input.txt" );
   QVector<int> arr;
   try {
       readData( iFileName, arr );
   } catch ( const std::exception &e ) {
       std::cerr << e.what( ) << std::endl;
       return 1;
   }
 

Код
C++ (Qt)
   // Read data from a file
   QString iFileName = QString( "input.txt" );
   errorCode = readData( iFileName, data );
   if ( errorCode != ErrorType::errNone ) {
       return showError( iFileName, errorCode );
   }
 


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Igors от Март 25, 2014, 10:42
Я не вижу в чём принципиальная разница между двумя подходами по обработке ошибок. Чем же здесь плохи исключения? Сравните в чём разница для пользователя, который вызывает функцию readData( iFileName, arr ). Исключения выполняют, в данном случае, туже самую работу:
Если сравнивать одно исключение с одной нормальной обработкой - то исключение куда дороже. Чего это Вы отделались печатью e.what(), где полный текст ошибки? Его надо добавлять и так просто как с ShowError не выйдет. Во-вторых, откуда предположение что только std::exception может быть испущено? Извольте ставить второй catch, если уж взялись.

И главное - а кто испустит exception? Оно применяется часто именно при чтении/записи.
Код
C++ (Qt)
strm >> a;
if (strm.error()) ...  // throw или возврат ошибки
...
strm >> b;
if (strm.error()) ...  
...
strm >> c;
if (strm.error()) ...  
....
Часто Вы не сможете читать "b" не проверив корректности "a" и.т.д. Общее число проверок становится невыносимым. Вот здесь разумно сделать так чтобы оператор >> испускал исключение и отловить все 1 раз наверху. Хотя и здесь некоторые предпочитают нормальную обработку.


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: 8Observer8 от Март 25, 2014, 10:52
По поводу текстов ошибок, то они внутри функции readData(). Я правильно понял замечание? Что здесь непросто?

Код
C++ (Qt)
void readData( const QString &fileName, QVector<int> &dest )
throw (std::invalid_argument, std::runtime_error) {
 
   // ...
   std::string error = "Unable to open the file " + fileName.toStdString( );
   throw std::invalid_argument( error );
   // ...
 
   // ...
   std::string error = "Unable to read file " + fileName.toStdString( );
   throw std::runtime_error( error );
   // ...
}
 


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Igors от Март 25, 2014, 11:04
По поводу текстов ошибок, то они внутри функции readData(). Я правильно понял замечание? Что здесь непросто?
Это просто плохо, одна ф-ция и цыплят жарит, и кофе готовит и еще много чего - вот только все делает плохо  :)


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Bepec от Март 25, 2014, 11:08
PPS Segmentation fault - исключение, насколько мне известно.
Нет.
Я вам специально цитату привёл :) В Windows это является исключением :D

Цитировать
Скот Мейерс в своей книге "Наиболее эффективное использование С++" утверждает, что повсеместное использование исключений в программе приведёт к снижению скорости выполнения программы на 5-10%.

Исключения возникают тогда, когда некоторая часть программы не смогла сделать то, что от неё требовалось. При этом другая часть программы может попытаться сделать что-нибудь иное.
Исключения позволяют логически разделить вычислительный процесс на 2 части - обнаружение аварийной ситуации и её обработка.
Примечание: В принципе, ничто не мешает рассматривать в качестве исключений не только ошибки, но и нормальные ситуации, возникающие при обработке данных, но это не имеет преимуществ перед другими решениями и не улучшает структуру и читаемость программы.

PS собственно и получается интересная деталь - при использовании "кода с исключениями" всегда может быть ситуация, что исключений будет типов 10-15. При использовании библиотек может доходить и до 20-30. И для обработки всех событий вам придётся писать 30 блоков обработки. А у 20 из 30 исключений внутри будут разные структуры, и это становится невыносимым :D


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: 8Observer8 от Март 25, 2014, 11:29
Это просто плохо, одна ф-ция и цыплят жарит, и кофе готовит и еще много чего - вот только все делает плохо  :)

С данным примером согласен. А вот если писать свои классы исключений, то проблема с тем кто передаёт тексты ошибок - уходит:
Код
C++ (Qt)
void readData( const QString &fileName, QVector<int> &dest )
throw (FileOpenError, FileReadError) {
 
   // ...
   throw FileOpenError( fileName );
   // ...
 
   // ...
   throw FileReadError( fileName, lineNumber );
   // ...
 
}
 

К тому же, как видите, мы можем теперь передавать номер строки, в которой некорректные данные. А Тексты ошибок у нас теперь в классах: FileOpenError и FileReadError:

FileOpenError.h
Код
C++ (Qt)
#ifndef FILEOPENERROR_H
#define FILEOPENERROR_H
 
#include <QString>
#include "FileError.h"
 
class FileOpenError : public FileError {
public:
 
   FileOpenError( const QString &fileNameIn ) : FileError( fileNameIn ) {
       mMsg = "Unable to open " + fileNameIn.toStdString( );
   }
};
 
#endif // FILEOPENERROR_H
 

FileReadError.h
Код
C++ (Qt)
#ifndef FILEREADERROR_H
#define FILEREADERROR_H
 
#include "FileError.h"
#include <QString>
#include <QTextStream>
#include <sstream>
 
class FileReadError : public FileError {
public:
 
   FileReadError( const QString &fileNameIn, int lineNumIn ) :
   FileError( fileNameIn ), mLineNum( lineNumIn ) {
       std::ostringstream ostr;
 
       ostr << "Error reading " << fileNameIn.toStdString( ) << " at line " << lineNumIn;
       mMsg = ostr.str( );
   }
 
   int getLineNum( ) const {
       return mLineNum;
   }
 
protected:
   int mLineNum;
};
 
#endif // FILEREADERROR_H
 

FileError.h
Код
C++ (Qt)
#ifndef FILEERROR_H
#define FILEERROR_H
 
#include <QString>
#include <exception>
#include <string>
 
class FileError : public std::runtime_error {
public:
 
   FileError( const QString &fileIn ) : std::runtime_error( "" ), mFile( fileIn ) {
   }
 
   virtual const char* what( ) const noexcept {
       return mMsg.c_str( );
   }
 
   QString getFileName( ) const {
       return mFile;
   }
 
protected:
   QString mFile;
   std::string mMsg;
};
 
#endif // FILEERROR_H
 

Пользователь теперь может вызывать функцию readData( iFileName, arr ); вот так (обрабатывая все исключения):
Код
C++ (Qt)
int main( int argc, char *argv[] ) {
 
   // ...
   // Read data from a file
   QString iFileName = QString( "input.txt" );
   QVector<int> arr;
   try {
       readData( iFileName, arr );
   } catch ( const FileError &e ) {
       std::cerr << e.what( ) << std::endl;
       return 1;
   }
   // ...
 
}
 


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Igors от Март 25, 2014, 12:07
А вот если писать свои классы исключений, то проблема с тем кто передаёт тексты ошибок - уходит:
"Вообще говоря" да, но все зависит от задачи. Для такого пионерского примерчика как у Вас исключения совершенно избыточны.

И главное - откуда возьмутся исключения, кто будет их генерить? Если каждый раз руками throw, то пользы немного. Обычно исключения испускают базовые ф-ции IO. Поскольку используются операторы >> и <<, то они и должны испускать. В std:: есть эта возможность, но ведь Вы работаете с Qt и вероятно будете использовать QDataStream. Как заставить его выбрасывать исключения?

Да, это может за рамками темы, но это реальный вопрос. А то в Вашем обзоре все слишком уж гладко   :)


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: 8Observer8 от Март 25, 2014, 13:15
Речь идёт о том, какой общий подход использовать. Понятно, что этот пример очень простой и достаточно было бы написать без всяких кодов ошибок. И в таком простом примере большинство ограничилось бы простым кодом:
Код
C++ (Qt)
   string outFileName = "output.txt";
   ofstream out;
   out.open( outFileName.c_str( ) );
   if ( !out.is_open( ) ) {
       cerr << "Error: could not open the file " << outFileName.c_str( ) << endl;
       in.close( );
       return 1;
   }
 

Я же хочу проанализировать разные подходы и выбрать один. Если не применять исключений, то нужно придерживаться одного из десятков способов обработки ошибок. Один из них Вы предложили. А вот функции из других библиотек с сишным подходом будут по-другому обрабатывать ошибки. Исключения же дают единообразный подход.

Если уж мы использовали подход с обработкой кодов ошибок с помощью функции showError(), то почему бы нам не использовать для этого примера свои классы исключений? Кода писать меньше, эти классы можно использовать повторно, код легко читать и изменять, а главное что это даёт единообразный подход к обработке пользовательских ошибок, как это сделано в C#, возможно и в Java (Java только начал изучать, поэтому не знаю, как там обстоят дела с исключениями)



Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Igors от Март 25, 2014, 14:20
Я же хочу проанализировать разные подходы и выбрать один.
Это принципиальная и тяжелая ошибка - использовать один подход для любой задачи. Любой прием может быть очень хорош в одной ситуации, но совершенно неуместен в другой.

Полагаю что исключения Вам нравятся тем что код выглядит весьма солидно (типа "грамотно" :)). Однако если за этим нет никакой содержательной части, никаких резонов - такой "дутый" код легко распознается даже программистом с небольшим опытом. Стремясь показать свою технику в отрыве от задачи Вы попадаете в неудобное положение. "догонят и еще дадут" - и правильно  :)

И еще: анализировать различные подходы легко и приятно. Но всегда есть противные мелочи, напр
Вы работаете с Qt и вероятно будете использовать QDataStream. Как заставить его выбрасывать исключения?
И я вот смотрю - Вы не горите желанием такими мелочами заниматься. А это значит что у такого работника надо стоять на душой, указывать, направлять, а он еще будет брыкаться, возражать... Это часто оказывается важнее обширных познаний и.т.п.


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: 8Observer8 от Март 25, 2014, 14:33
Я боюсь, что если буду везде использовать подход с showError(), то будет таже самая ситуация. Опять же не вижу конкретных проблем в примере с исключениями при работе с файлами. Чем подход с showError() лучше чем подход с классами исключений?


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Old от Март 25, 2014, 14:36
Я вам специально цитату привёл :) В Windows это является исключением :D
Это не является исключением C++, хотя SEH и очень похож (__try, __except).:)

Цитировать
Скот Мейерс в своей книге "Наиболее эффективное использование С++" утверждает, что повсеместное использование исключений в программе приведёт к снижению скорости выполнения программы на 5-10%.
А не надо повсеместно его использовать. :) Исключения нужно использовать при исключительных ситуациях.

но это не имеет преимуществ перед другими решениями и не улучшает структуру и читаемость программы.
Да ну ладно. Этот пример с выходом из глубокой функции с возвратом результата хорошо это подтверждает. :)

PS собственно и получается интересная деталь - при использовании "кода с исключениями" всегда может быть ситуация, что исключений будет типов 10-15. При использовании библиотек может доходить и до 20-30. И для обработки всех событий вам придётся писать 30 блоков обработки. А у 20 из 30 исключений внутри будут разные структуры, и это становится невыносимым :D
Скажите честно, вы никогда не писали программ с исключениями. :)
Все что вы написали выше, мягко говоря, ерунда. ;)


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Old от Март 25, 2014, 14:44
Я боюсь, что если буду везде использовать подход с showError(), то будет таже самая ситуация.
Конечно. Ненужно зацикливаться. :)

Опять же не вижу конкретных проблем в примере с исключениями при работе с файлами.
Потому, что их нет.

Чем подход с showError() лучше чем подход с классами исключений?
Ничем, а часто он хуже.


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Bepec от Март 25, 2014, 15:22

но это не имеет преимуществ перед другими решениями и не улучшает структуру и читаемость программы.
Да ну ладно. Этот пример с выходом из глубокой функции с возвратом результата хорошо это подтверждает. :)

Скажите честно, вы никогда не писали программ с исключениями. :)
Все что вы написали выше, мягко говоря, ерунда. ;)

1) Читайте внимательнее. Там написано что обработка не исключительных ситуаций при помощи исключений и далее текст цитаты. Т.е. применение исключений везде даёт худший результат,чем совмещение разных подходов.

2) писал. Qt/C++/Boost/*** (секретная разработка )/OPC. Примерно 8 блоков для обработки исключений. Нет, конечно они все стандартны и можно было бы вывести строку ошибки, но... но нужно ещё кучи доп информации.


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Old от Март 25, 2014, 15:37
1) Читайте внимательнее. Там написано что обработка не исключительных ситуаций при помощи исключений и далее текст цитаты. Т.е. применение исключений везде даёт худший результат,чем совмещение разных подходов.
Не вижу обоснований из чего это следует. :)

2) писал. Qt/C++/Boost/*** (секретная разработка )/OPC. Примерно 8 блоков для обработки исключений. Нет, конечно они все стандартны и можно было бы вывести строку ошибки, но... но нужно ещё кучи доп информации.
Покажите на примере и посмотрим, а было ли все так ужасно и что можно было сделать, что бы было лучше. :)
Пока я вижу только попытки убедить самого себе в том, что исключения это плохо. :)


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Bepec от Март 25, 2014, 15:50
Из логики следует. За сим отписываться по этой теме прекращаю, просто у вас начинается холи вар :)

PS код привести не могу, ком.тайна. Да и уволился с того места работы.


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Old от Март 25, 2014, 15:54
Из логики следует. За сим отписываться по этой теме прекращаю, просто у вас начинается холи вар :)
Скорее это из-за того, что по теме сказать нечего из-за не владения предметом. А отписаться в теме захотелось.

PS код привести не могу, ком.тайна. Да и уволился с того места работы.
Другого ответа я и не ожидал. ;)
Хотя я код приводить не просил, просто вымышленный пример. Ну да ладно.


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: OKTA от Март 25, 2014, 17:39
А на сколько человечно делать грубо говоря try на весь main, чтобы не плодить код? При условии, что есть нормальная система обработки всех сгенерированных исключений? И на сколько человечно использовать исключения в конструкторах и деструкторах. Знаю, что все настоятельно рекомендуют не делать этого, т.к. исключение прерывает нормальных ход выполнения, но если использовать исключения в конце конструкторов-деструкторов после всех выполненных методов, когда мы точно уверены, что все уже ок и просто хотим получить уведомление об этом?

Хотя насчет первого понял - удобно конечно, но тогда никакой попытки обработки исключительных ситуаций на месте не будет.


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Old от Март 25, 2014, 18:08
А на сколько человечно делать грубо говоря try на весь main, чтобы не плодить код? При условии, что есть нормальная система обработки всех сгенерированных исключений?
В main catch это последняя линия, если сюда попали, то сделать уже ничего нельзя и остается вывести диагностику и остановиться. Это ничем не отличается от вываливания в main из 100500 вложенной функции постоянно проверяю ошибку и делая return вверх.
Если мы можем как то повлиять на ситуацию, то обрабатываем это в локальной секции catch.

И на сколько человечно использовать исключения в конструкторах и деструкторах.
В конструкторах вполне "человечно", если правильно инициализировать члены класса.
В деструкторах не стоит использовать, но если понимать в чем могут быть проблемы и организационно не допускать таких случаев, то можно и в деструторах. :)


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: 8Observer8 от Март 26, 2014, 19:13
Проблема использования исключений по любому поводу - это потенциальная возможность не обработать какое-то одно исключение и программа упадет.

Долго думал по поводу этого тезиса, но так его и не понял. Если я написал функцию и в объявлении написал, какие исключения она выбрасывает:
Код
C++ (Qt)
void readData( const QString &fileName, QVector<int> &arr ) throw (FileOpenError, FileReadError);
 
и ещё знаю, что оба эти исключения и FileOpenError и FileReadError наследуются от FileError, то могу отлавливать оба этих исключения одним блоком try/catch:
Код
C++ (Qt)
   try {
       readData( iFileName, arr );
   } catch ( const FileError &e ) {
       std::cerr << e.what( ) << std::endl;
       return 1;
   }
 

Таким образом исключение от меня ускользнуть не может. Большинство функции STD генерируют исключения и я так понимаю, что надо учиться все их обрабатывать. К примеру, мы выделяем память из кучи и слишком много запросили (или может случайно в цикле это начали делать). Вот такой пример из книги, когда памяти не хватает, то new выбрасывает исключение std::bad_alloc. И нам обязательно надо его обработать, чтобы программа не завершилась без всяких сообщений:
Код
C++ (Qt)
#include <iostream>
 
int main( ) {
   int* ptr;
   int numInts = 10;
 
   try {
       ptr = new int[numInts];
   } catch ( const std::bad_alloc& e ) {
       std::cerr << __FILE__ << "(" << __LINE__ << "): Unable to allocate memory!" << std::endl;
       // Handle memory allocation failure
       return 1;
   }
   // Proceed with function that assumes memory has been allocated
 
   return 0;
}

А если мы не хотим в данном случае получать исключения, то надо написать: new(nothrow), тогда будет возвращено: nullptr:
Код
C++ (Qt)
#include <iostream>
 
int main( ) {
   int* ptr;
   int numInts = 10;
 
   ptr = new(std::nothrow) int[numInts];
   if ( ptr == nullptr ) {
       std::cerr << __FILE__ << "(" << __LINE__ << "): Unable to allocate memory!" << std::endl;
       // Handle memory allocation failure
       return 1;
   }
   // Proceed with function that assumes memory has been allocated
 
   return 0;
}
 

Вот только nullptr - это из C++11. А что написать вместо nullptr для C++98?


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Old от Март 26, 2014, 19:16
Вот только nullptr - это из C++11. А что написать вместо nullptr для C++98?
0


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: 8Observer8 от Март 26, 2014, 19:17
Спасибо :)


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Bepec от Март 26, 2014, 19:17
Всегда есть шанс не вспомнить какое нибудь исключение из далёкого класса. Да, когда их 5-10-30 достаточно их все помнить. А когда их станет больше, можно и забыть. И будет "вылет без всяких сообщений".


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Old от Март 26, 2014, 19:19
И будет "вылет без всяких сообщений".
Неправда. В нормальных системах рантайм всегда показывает информацию о необработанных исключениях при завершении приложения.
И в main нужно последним catch писать такой, который обработает любое исключение.


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: 8Observer8 от Март 27, 2014, 08:58
С исключениями в C++ связано много ловушек. Одна из главных причин, по-моему, это совместимость с языком Си. Таких ловушек нет в C# и Java (а может они тоже есть, но их меньше). Чтобы избежать ловушек надо попробовать моделировать C# и Java.

Буду по мере познания выкладывать здесь примеры :)

Пример. Ликвидация утечки памяти (из-за исключений) с помощью shared_ptr (или unique_ptr)

Вот пример плохого кода, в котором из-за исключения не освобождаются ресурсы и происходит утечка памяти. Пример из книги "Professional C++", глава 10, называется пример "StackUnwinding\BadCode". В этом примере команда delete str2; не выполняется из-за того, что функция funcTwo(); выбрасывает исключение и дело до освобождения ресурса не доходит:
Код
C++ (Qt)
#include <fstream>
#include <iostream>
#include <stdexcept>
using namespace std;
 
void funcOne( ) throw (exception);
void funcTwo( ) throw (exception);
 
int main( ) {
   try {
       funcOne( );
   } catch ( const exception& e ) {
       cerr << "Exception caught!" << endl;
       return 1;
   }
 
   return 0;
}
 
void funcOne( ) throw (exception) {
   string str1;
   string* str2 = new string( );
   funcTwo( );
   delete str2;
}
 
void funcTwo( ) throw (exception) {
   ifstream istr;
   istr.open( "filename" );
   throw exception( );
   istr.close( );
}
 

Чтобы избежать этого, можно использовать технику: Catch, Cleanup, и Rethrow (пример называется StackUnwinding\CatchAndRethrow). То есть отлавлить исключение, которое выбрасывает funcTwo(), в функции funcOne() - освободить ресурсы (delete str2;) и передать исключение дальше:
Код
C++ (Qt)
#include <fstream>
#include <iostream>
#include <stdexcept>
using namespace std;
 
void funcOne( ) throw (exception);
void funcTwo( ) throw (exception);
 
int main( ) {
   try {
       funcOne( );
   } catch ( const exception& e ) {
       cerr << "Exception caught!" << endl;
       return 1;
   }
 
   return 0;
}
 
void funcOne( ) throw (exception) {
   string str1;
   string* str2 = new string( );
   try {
       funcTwo( );
   } catch ( ... ) {
       delete str2;
       throw; // rethrow the exception
   }
   delete str2;
}
 
void funcTwo( ) throw (exception) {
   ifstream istr;
   istr.open( "filename" );
   throw exception( );
   istr.close( );
}

В Java есть сборщик мусора (как и в C#). А в C++ можно смоделировать сборщик мусора с помощью умных указателей. Писать кода меньше, его легче сопровождать, меньше вероятность допустить ошибку (забыв освободить ресурсы или забыв обработать исключение до освобождения), а так же не надо дублировать следующий код по освобождению ресурсов (delete str2;), как это было в предыдущем примере:
Код
C++ (Qt)
   try {
       funcTwo( );
   } catch ( ... ) {
       delete str2;
       throw; // rethrow the exception
   }
   delete str2;
 

Пример с shared_ptr (или unique_ptr):
Код
C++ (Qt)
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <memory>
 
using namespace std;
 
void funcOne( ) throw (exception);
void funcTwo( ) throw (exception);
 
int main( ) {
   try {
       funcOne( );
   } catch ( const exception& e ) {
       cerr << "Exception caught!" << endl;
       return 1;
   }
 
   return 0;
}
 
void funcOne( ) throw (exception) {
   string str1;
   shared_ptr<string> str2( new string( "hello" ) );
   //unique_ptr<string> str2( new string( "hello" ) );
   funcTwo( );
}
 
void funcTwo( ) throw (exception) {
   ifstream istr;
   istr.open( "filename" );
   throw exception( );
   istr.close( );
}
 


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: gil9red от Март 27, 2014, 09:19
А можно было не выделять память в куче в функциях, а использовать стэковую :)


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: 8Observer8 от Март 27, 2014, 09:27
Под строкой: "string* str2 = new string( );" надо подразумевать захват любого ресурса, а под строкой: "delete str2;" - освобождение любого ресурса :)


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: OKTA от Март 27, 2014, 09:37
А при чем тут совместимость с Си?


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: 8Observer8 от Март 27, 2014, 09:45
Си пробивает бреши в C++, в частности, в работе с исключениями (в отличае от более продуманных C# и Java): см. сборщик мусора в Java (C#) и необходимость использовать технику "Catch, Cleanup, и Rethrow" в C++ (слава Богу есть shared_ptr и unique_ptr).


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Old от Март 27, 2014, 09:49
(слава Богу есть shared_ptr и unique_ptr).
Это частности техники RAII.


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Igors от Март 27, 2014, 10:04
Си пробивает бреши в C++, в частности, в работе с исключениями (в отличае от более продуманных C# и Java): см. сборщик мусора в Java (C#) и необходимость использовать технику "Catch, Cleanup, и Rethrow" в C++ (слава Богу есть shared_ptr и unique_ptr).
Так может Вам переключиться на C# и Java? Они же "более продуманы" :)
Вот Вы шпарите по книжкам вещи которые большинство форумчан давно знают. Конечно Вы не делаете ничего плохого, но и смысла пережевывать известное немного. 

От практического программирования все это далековато. То что Вам известны приемы/методы совсем не значит что Вы их с успехом примените. "Вот я побольше выучу, и тогда..." - это ошибка многих. Программирование не было и не будет эксплуатацией памяти (и это хорошо и правильно  :)


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: OKTA от Март 27, 2014, 10:10
Си пробивает бреши в C++, в частности, в работе с исключениями (в отличае от более продуманных C# и Java): см. сборщик мусора в Java (C#) и необходимость использовать технику "Catch, Cleanup, и Rethrow" в C++ (слава Богу есть shared_ptr и unique_ptr).


В си вообще как таковых исключений нет. А насчет сборщика мусора в С++ вот
Цитировать
Надо отметить, что автоматическая сборка мусора не включена в стандарт C++ по той простой причине, что программа, её использующая, будет всегда работать медленнее, чем если бы сборка мусора не использовалась вообще. Поэтому Бьёрном Страуструпом было предложено перепоручить обязанности сборки мусора внешним библиотекам, не затрагивая самого C++, что может позитивно сказаться на производительности приложений, поскольку программист сам может решить, где и когда ему стоит использовать автоматическое управление памятью. Это и является серьёзным отличием С++ от Java – при использовании Java у программистов просто нет выбора.

А вот и пример сборщика мусора на С++ http://www.rsdn.ru/article/cpp/GCcpp.xml


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: 8Observer8 от Март 27, 2014, 10:29
Цитировать
Надо отметить, что автоматическая сборка мусора не включена в стандарт C++ по той простой причине, что программа, её использующая, будет всегда работать медленнее...
Это было актуально в те далёкие времена. Сейчас и копьюторы стали намного мощнее и сам Java сравнялся с C++ в плане скорости.

Вопрос не в этом, чтобы переходить на другой язык, где есть сборщик мусора, а чтобы отработать полезную привычку писать осознанно и правильно и создавать надёжные программные системы. Использовать техники доступные в C++, чтобы приблизиться к тому идеалу, к которому подошли C# и Java (в плане безопасности программирования).

P.S. Вроде C# обязан языку "Оберон/Компонентный Паскаль" (в плане безопасности программирования). Вот статейка про выход за пределы границ массива: http://www.inr.ac.ru/~info21/blackbox/disciplina/arr_bounds_checks.htm


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Bepec от Март 27, 2014, 10:36
Кхм. Выражу сомнения в возможности Java обрабатывать данные с 4 оптических интерфейсов без 100% загрузки процессора :D

PS не стоит думать "да немного медленнее и что с того"? Этих немного медленнее много. Сотни можно сказать :D


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: OKTA от Март 27, 2014, 10:48
Thank God I've never seen operating systems written on Java or C#  ;D ;D ;D


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Igors от Март 27, 2014, 11:14
Надо отметить, что автоматическая сборка мусора не включена в стандарт C++ по той простой причине, что программа, её использующая, будет всегда работать медленнее...
Дело не в быстрее/медленнее (это "простое" объяснение для широкого круга). Автоматическая сборка мусора противоречит духу/концепциям С/С++.

Вопрос не в этом, чтобы переходить на другой язык, где есть сборщик мусора, а чтобы отработать полезную привычку писать осознанно и правильно и создавать надёжные программные системы. Использовать техники доступные в C++, чтобы приблизиться к тому идеалу, к которому подошли C# и Java (в плане безопасности программирования).
Пока что НИ РАЗУ выученное не было применено удачно, к месту :) Ладно, будем надеяться что "и это пройдет"


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: 8Observer8 от Март 27, 2014, 11:37
Пока что НИ РАЗУ выученное не было применено удачно, к месту :) Ладно, будем надеяться что "и это пройдет"

Как это ни разу? А я же заменил showError() с помощью своих классов исключений :)


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Igors от Март 27, 2014, 12:02
Как это ни разу? А я же заменил showError() с помощью своих классов исключений :)
Так это классический анти-паттерн - как НЕ НАДО использовать исключения  :)
С книжками/изучением у Вас все отлично, но как только начинаете это тулить куда ни попадя - смех и грех. Без практики теория довольно беспомощна  :)


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: 8Observer8 от Март 27, 2014, 12:16
Почему же их не использовать для обработки ошибок? Хотя бы на примере чтения из файла.


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Igors от Март 27, 2014, 13:16
Почему же их не использовать для обработки ошибок? Хотя бы на примере чтения из файла.
Использовать, и для чтения из файла - часто. Только Вы их неправильно используете, о чем я в этой теме уже говорил дважды - и даже рядом тему создал.


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Old от Март 27, 2014, 13:30
Почему же их не использовать для обработки ошибок?
Зачем вы спрашиваете чье то мнение? У вас сейчас пока еще есть возможность изучать и исследовать все, на что падает ваш взгляд. Воспользуйтесь этим. Скоро вас загонят в корпоративные рамки, где скажут использовать это и это, а это не использовать, а с этим делать так и никак иначе. :)
В эти свободные времена, вам нужно постараться максимально расширить ваш технических кругозор. В дальнейшем, вы сможете сразу определять, в какие стороны можно посмотреть, что бы решить задачу.
Не слушайте никого: вам говорят так не делать - сделайте так, почувствуйте проблему или поймите что ее там нет и все это предрассудки.
Пробуйте, творите, выкладывайте здесь, будем обсуждать. :)


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: 8Observer8 от Март 27, 2014, 13:47
Спасибо огромное! Согласен! Отлично сказано, что надо сделать и самому почувствовать проблему или её отсутствие :)


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Old от Март 27, 2014, 13:59
Как это ни разу? А я же заменил showError() с помощью своих классов исключений :)
А где на это можно посмотреть?


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: 8Observer8 от Март 27, 2014, 14:07
Как это ни разу? А я же заменил showError() с помощью своих классов исключений :)
А где на это можно посмотреть?

Приведу сразу все примеры:

Пример без исключений:
Код
C++ (Qt)
#include <QCoreApplication>
#include <QFile>
#include <QTextStream>
#include <iostream>
 
//! Error codes
enum ErrorType {
   //! None errors
   errNone = 0,
 
   //! Error of the opening of a file
   errFileOpen = -1000,
 
   //! Error of the writing of a file
   errFileWrite = -1001,
 
   //! Wrong data
   errDataBad = -5000
};
 
ErrorType readData( const QString &fileName, QString &data );
ErrorType writeResult( const QString &fileName, QString &data );
void showData( const QString &data );
ErrorType showError( const QString &fileName, ErrorType errorCode );
 
/*!
* Working with a text file
*/

int main( int argc, char *argv[] ) {
   QCoreApplication a( argc, argv );
 
   QString data;
   ErrorType errorCode = ErrorType::errNone;
 
   // Read data from a file
   QString iFileName = QString( "input.txt" );
   errorCode = readData( iFileName, data );
   if ( errorCode != ErrorType::errNone ) {
       return showError( iFileName, errorCode );
   }
 
   // Show data on the screen
   showData( data );
 
   // Data processing
   data += QString( ", World" );
 
   // Write data to a file
   QString oFileName = QString( "output.txt" );
   writeResult( oFileName, data );
   if ( errorCode != ErrorType::errNone ) {
       return showError( oFileName, errorCode );
   }
 
   return a.exec( );
}
 
/*!
* Read data from the file
* \param fileName File name
* \param data data
* \return Error code
*/

ErrorType readData( const QString &fileName, QString &data ) {
   QFile file( fileName );
   if ( !file.open( QIODevice::ReadOnly ) ) {
       return ErrorType::errFileOpen;
   }
 
   data = file.readAll( );
 
   file.close( );
   return ErrorType::errNone;
}
 
/*!
* Write data to the file
* \param fileName Fleile name
* \param data Data
* \return Error code
*/

ErrorType writeResult( const QString &fileName, QString &data ) {
   QFile file( fileName );
 
   if ( !file.open( QIODevice::WriteOnly ) ) {
       return ErrorType::errFileOpen;
   }
 
   QTextStream stream( &file );
   stream << data;
   file.close( );
 
   if ( stream.status( ) != QTextStream::Ok ) {
       return ErrorType::errFileWrite;
   }
 
   return ErrorType::errNone;
}
 
/*!
* Show data on the screen
* \param data Data
*/

void showData( const QString &data ) {
   std::cout << data.toStdString( ) << std::endl;
}
 
/*!
* Write the error code to the file
* \param fileName File name
* \param errorCode Error code
* \return Error code
*/

ErrorType showError( const QString &fileName, ErrorType errorCode ) {
   switch ( errorCode ) {
       case ErrorType::errFileOpen:
           std::cerr << "Error: cannot open the file " << fileName.toStdString( ) << std::endl;
           break;
       case ErrorType::errFileWrite:
           std::cerr << "Error: cannot write to the file " << fileName.toStdString( ) << std::endl;
           break;
       case ErrorType::errDataBad:
           std::cerr << "Error: incorrect data in the file " << fileName.toStdString( ) << std::endl;
           break;
       default:
           std::cerr << "Error code: " << errorCode << "; file name: " << fileName.toStdString( ) << std::endl;
           break;
   }
 
   return errorCode;
}
 

Пример с исключениями

Код
C++ (Qt)
#include <QCoreApplication>
#include <QFile>
#include <QTextStream>
#include <QVector>
#include <iostream>
#include <exception>
 
void readData( const QString &fileName, QVector<int> &arr )
throw (std::invalid_argument, std::runtime_error);
 
void writeResult( const QString &fileName, QVector<int> &arr )
throw (std::invalid_argument, std::runtime_error);
 
void showData( const QVector<int> &arr );
 
/*!
* Working with a text file
*/

int main( int argc, char *argv[] ) {
   QCoreApplication a( argc, argv );
 
   // Read data from a file
   QString iFileName = QString( "input.txt" );
   QVector<int> arr;
   try {
       readData( iFileName, arr );
   } catch ( const std::exception &e ) {
       std::cerr << e.what( ) << std::endl;
       return 1;
   }
 
   // Show data on the screen
   showData( arr );
 
   // Data processing
   arr.push_back( 100 );
 
   // Write data to a file
   QString oFileName = QString( "output.txt" );
   try {
       writeResult( oFileName, arr );
   } catch ( const std::exception &e ) {
       std::cerr << e.what( ) << std::endl;
       return 1;
   }
 
   return a.exec( );
}
 
/*!
* Read data from the file
* \param fileName File name
* \param data data
*/

void readData( const QString &fileName, QVector<int> &dest )
throw (std::invalid_argument, std::runtime_error) {
   QFile file( fileName );
 
   if ( !file.open( QIODevice::ReadOnly ) ) {
       std::string error = "Unable to open the file " + fileName.toStdString( );
       throw std::invalid_argument( error );
   }
 
   QTextStream stream( &file );
 
   QString input;
   do {
       stream >> input;
       bool ok;
       int value = input.toInt( &ok );
       if ( (ok) && (stream.status( ) == QTextStream::Ok) ) {
           dest.push_back( value );
       } else if ( !stream.atEnd( ) ) {
           std::string error = "Unable to read file " + fileName.toStdString( );
           throw std::runtime_error( error );
       }
   } while ( !stream.atEnd( ) );
}
 
/*!
* Write data to the file
* \param fileName Fleile name
* \param data Data
* \return Error code
*/

void writeResult( const QString &fileName, QVector<int> &arr )
throw (std::invalid_argument, std::runtime_error) {
   QFile file( fileName );
 
   if ( !file.open( QIODevice::WriteOnly ) ) {
       std::string error = "Unable to open the file " + fileName.toStdString( );
       throw std::invalid_argument( error );
   }
 
   QTextStream stream( &file );
   for ( int i = 0; i < arr.size( ); ++i ) {
       stream << arr[i];
       if ( i != (arr.size( ) - 1) ) {
           stream << " ";
       } else {
           stream << endl;
       }
   }
   file.close( );
 
   if ( stream.status( ) != QTextStream::Ok ) {
       std::string error = "Unable to write to the file " + fileName.toStdString( );
       throw std::runtime_error( error );
   }
}
 
/*!
* Show data on the screen
* \param data Data
*/

void showData( const QVector<int> &arr ) {
   for ( int i = 0; i < arr.size( ); ++i ) {
       std::cout << QString::number( arr[i] ).toStdString( ) << std::endl;
   }
}
 

Пример со своими классами исключений
text_file.pro
Код
C++ (Qt)
#-------------------------------------------------
#
# Project created by QtCreator 2014-03-16T19:25:22
#
#-------------------------------------------------
 
QT       += core
 
QT       -= gui
 
QMAKE_CXXFLAGS += -std=c++11
 
TARGET = text_file
CONFIG   += console
CONFIG   -= app_bundle
 
TEMPLATE = app
 
 
SOURCES += main.cpp
 
HEADERS += \
   FileError.h \
   FileOpenError.h \
   FileReadError.h \
   FileWriteError.h
 

FileError.h
Код
C++ (Qt)
#ifndef FILEERROR_H
#define FILEERROR_H
 
#include <QString>
#include <exception>
#include <string>
 
class FileError : public std::runtime_error {
public:
 
   FileError( const QString &fileIn ) : std::runtime_error( "" ), mFile( fileIn ) {
   }
 
   virtual const char* what( ) const noexcept {
       return mMsg.c_str( );
   }
 
   QString getFileName( ) const {
       return mFile;
   }
 
protected:
   QString mFile;
   std::string mMsg;
};
 
#endif // FILEERROR_H
 

FileOpenError.h
Код
C++ (Qt)
#ifndef FILEOPENERROR_H
#define FILEOPENERROR_H
 
#include <QString>
#include "FileError.h"
 
class FileOpenError : public FileError {
public:
 
   FileOpenError( const QString &fileNameIn ) : FileError( fileNameIn ) {
       //mMsg = QString("Unable to open ") + fileNameIn;
       mMsg = "Unable to open " + fileNameIn.toStdString( );
   }
};
 
#endif // FILEOPENERROR_H
 

FileReadError.h
Код
C++ (Qt)
#ifndef FILEREADERROR_H
#define FILEREADERROR_H
 
#include "FileError.h"
#include <QString>
#include <QTextStream>
#include <sstream>
 
class FileReadError : public FileError {
public:
 
   FileReadError( const QString &fileNameIn, int lineNumIn ) :
   FileError( fileNameIn ), mLineNum( lineNumIn ) {
       //        QTextStream stream(stdout);
 
       //        //stream << QString("Error reading ") << fileNameIn << QString(" at line ") << lineNumIn;
       //        mMsg = QString("Error reading %1 at line %2").arg(fileNameIn).arg(lineNumIn);
       //        stream >> mMsg;
       std::ostringstream ostr;
 
       ostr << "Error reading " << fileNameIn.toStdString( ) << " at line " << lineNumIn;
       mMsg = ostr.str( );
   }
 
   int getLineNum( ) const {
       return mLineNum;
   }
 
protected:
   int mLineNum;
};
 
#endif // FILEREADERROR_H
 

FileWriteError.h
Код
C++ (Qt)
#ifndef FILEWRITEERROR_H
#define FILEWRITEERROR_H
 
#include <QString>
#include "FileError.h"
 
class FileWriteError : public FileError {
public:
 
   FileWriteError( const QString &fileNameIn ) : FileError( fileNameIn ) {
       //mMsg = QString( "Unable to write " ) + fileNameIn;
       mMsg = "Unable to write " + fileNameIn.toStdString( );
   }
};
 
#endif // FILEWRITEERROR_H
 

main.cpp
Код
C++ (Qt)
#include <QCoreApplication>
#include <QFile>
#include <QTextStream>
#include <QVector>
#include <iostream>
#include "FileOpenError.h"
#include "FileReadError.h"
#include "FileWriteError.h"
 
void readData( const QString &fileName, QVector<int> &arr )
throw (FileOpenError, FileReadError);
 
void writeResult( const QString &fileName, QVector<int> &arr )
throw (FileOpenError, FileWriteError);
 
void showData( const QVector<int> &arr );
 
/*!
* Working with a text file
*/

int main( int argc, char *argv[] ) {
   QCoreApplication a( argc, argv );
 
   // Read data from a file
   QString iFileName = QString( "input.txt" );
   QVector<int> arr;
   try {
       readData( iFileName, arr );
   } catch ( const FileError &e ) {
       std::cerr << e.what( ) << std::endl;
       return 1;
   }
 
   // Show data on the screen
   showData( arr );
 
   // Data processing
   arr.push_back( 100 );
 
   // Write data to a file
   QString oFileName = QString( "output.txt" );
   try {
       writeResult( oFileName, arr );
   } catch ( const FileError &e ) {
       std::cerr << e.what( ) << std::endl;
       return 1;
   }
 
   return a.exec( );
}
 
/*!
* Read data from the file
* \param fileName File name
* \param data data
*/

void readData( const QString &fileName, QVector<int> &dest )
throw (FileOpenError, FileReadError) {
   QFile file( fileName );
 
   if ( !file.open( QIODevice::ReadOnly ) ) {
       throw FileOpenError( fileName );
   }
 
   QTextStream stream( &file );
 
   QString input;
   int lineNumber = 0;
   do {
       lineNumber++;
       stream >> input;
       bool ok;
       int value = input.toInt( &ok );
       if ( (ok) && (stream.status( ) == QTextStream::Ok) ) {
           dest.push_back( value );
       } else if ( !stream.atEnd( ) ) {
           throw FileReadError( fileName, lineNumber );
       }
   } while ( !stream.atEnd( ) );
}
 
/*!
* Write data to the file
* \param fileName Fleile name
* \param data Data
* \return Error code
*/

void writeResult( const QString &fileName, QVector<int> &arr )
throw (FileOpenError, FileWriteError) {
   QFile file( fileName );
 
   if ( !file.open( QIODevice::WriteOnly ) ) {
       throw FileOpenError( fileName );
   }
 
   QTextStream stream( &file );
   for ( int i = 0; i < arr.size( ); ++i ) {
       stream << arr[i];
       if ( i != (arr.size( ) - 1) ) {
           stream << " ";
       } else {
           stream << endl;
       }
   }
   file.close( );
 
   if ( stream.status( ) != QTextStream::Ok ) {
       throw FileWriteError( fileName );
   }
}
 
/*!
* Show data on the screen
* \param data Data
*/

void showData( const QVector<int> &arr ) {
   for ( int i = 0; i < arr.size( ); ++i ) {
       std::cout << QString::number( arr[i] ).toStdString( ) << std::endl;
   }
}
 


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Igors от Март 27, 2014, 14:15
Код
C++ (Qt)
   do {
       stream >> input;
       bool ok;
       int value = input.toInt( &ok );
       if ( (ok) && (stream.status( ) == QTextStream::Ok) ) {
           dest.push_back( value );
       } else if ( !stream.atEnd( ) ) {
           std::string error = "Unable to read file " + fileName.toStdString( );
           throw std::runtime_error( error );
       }
   } while ( !stream.atEnd( ) );
}
 
Такое использование исключений не дает выгод, "зато" засоряет код капитально. A вот пример силы исключений
Код
C++ (Qt)
stream >> dest;  // вместо всей бадяги выше
 
Конечно это надо подготовить (возможно классами) - но оно того стоит


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: 8Observer8 от Март 27, 2014, 14:23
Вы не могли бы подробнее написать. Набросками. Как этот код в классы убрать?


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Bepec от Март 27, 2014, 14:26
Можно проще написать - так как в Qt разработчики отказались от исключений, то для их внедрения вам придётся переписать большую часть Qt. :D А это бессмысленно.


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Old от Март 27, 2014, 14:27
Вот функция main из одного моего небольшого проекта
Код
C++ (Qt)
using namespace xxx;
 
int main( int argc, char *argv[] )
{
       try
       {
               Application app;
               app.parseCommandLine( argc, argv );
               app.isRunning();
               app.daemonize();
               app.checkCfgFile();
               app.exec();
       }
       catch( const std::exception &x )
       {
               Log( "%s", x.what() );
               return 1;
       }
       catch( ... )        // Специальная ветка для Вереса, что бы ничего не падало тихо. :)
       {
               Log( "Shit! Unknow exception. :)" );
               return -1;
       }
 
       return 0;
}
 

Если где-то внутри я могу повлиять на работу программы при возникновении исключения, то там имеем локальный блок try-catch для интересующих в данный момент исключений.
Выпали в main, значит все что мы можем, это сохранить в лог всю доступную информацию и выйти.


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Old от Март 27, 2014, 14:30
Можно проще написать - так как в Qt разработчики отказались от исключений, то для их внедрения вам придётся переписать большую часть Qt. :D А это бессмысленно.
Да, очень жаль, что когда Qt начинали разрабатывать не было ни одного компилятора, умеющего их нормально. Вот теперь пожинаем. :(


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Bepec от Март 27, 2014, 14:34
to Old:
У вас всё сводится к логированию и выходу из программы. Я же имел в виду восстановле ние работоспособности программы :)  Но фу фу - не буду спорить. В качестве "падает тихо" пойдёт :)


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Old от Март 27, 2014, 14:37
Вы не могли бы подробнее написать. Набросками. Как этот код в классы убрать?
Смысл в том, что бы было возможно писать так:
Код
C++ (Qt)
try
{
   Stream stream( ... );
 
   stream >> id;
   stream >> data1;
   stream >> data2;
}
catch( const ErrorTypeOne &ex )
{
}
catch( const ErrorTypeTwo &ex )
{
}
 
В этом случае, логика чтения/записи не захламляется проверками.


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: 8Observer8 от Март 27, 2014, 14:42
Можно проще написать - так как в Qt разработчики отказались от исключений, то для их внедрения вам придётся переписать большую часть Qt. :D А это бессмысленно.

Зато разработчики STD и Boost активно их используют. Надо отлавливать все исключения, как и проверять коды функций на случай если они отработали неверно.


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Old от Март 27, 2014, 14:43
Я же имел в виду восстановле ние работоспособности программы :)
Плохо, что вы не читаете сообщения, там в последнем предложении все написано.
Хотите и знаете как восстановиться, используете локальный блок.

Или вам нужен пример, в каких случаях программа может восстановиться при исключении? Так их есть у меня. :)


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Igors от Март 27, 2014, 14:56
Вы не могли бы подробнее написать. Набросками. Как этот код в классы убрать?
Для чтения int используйте http://www.prog.org.ru/index.php?topic=26733.msg192575#msg192575 (http://www.prog.org.ru/index.php?topic=26733.msg192575#msg192575) Только замените QDataStream на QTextStream. Чтение вектора допишете (наск помню его в QTextStream нет)


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Old от Март 27, 2014, 15:45
Я же имел в виду восстановле ние работоспособности программы :)
Появилось немного времени, хочу поделится своими мыслями про исключения и восстановления. :)

C++ промышленный язык и сделан для создания больших и сложных систем. Любая большая система состоит из кучи маленьких, в идеале независимых подсистем.
Возьмем для примера одну из таких подсистем, пусть это будет менеджер ресурсов. В задачи этой подсистемы может входить загрузка и распределение ресурсов. Пусть на входе, она будет получать путь к директории с файлами ресурсов.
Код
C++ (Qt)
class ResourceManager
{
public:
   explicit ResourceManager( const std::string &dataDir ) throw( BadDataDir );
};
 
При конструировании менеджера проверяется указанная директория и если там нет данных программы (или вообще нет такой директории), то выбрасывается исключение BadDataDir.

А теперь давайте посмотрим: фатальная ли это ситуация, когда в указанной директории нет файлов с данными? Для менеджера ресурсов несомненно да, его существование бессмысленно с неверным директорием. А для всей системы в целом? Да это ерунда.
Система может искать данные в директории пользователя, в общих директориях, короче, в куче разных мест. И если система начала создавать менеджер ресурсов для одного источника и там не оказалось данных (получили исключение), то она может попробовать создать еще 100500 таких менеджеров, пробуя все известные ей источники.

Код
C++ (Qt)
std::shared_ptr<ResourceManager> Core::initResourceManager()
{
   std::vector< std:string > dataDirs = { "~/mysystem/data", "/usr/share/mysystem/data", "/opt/mysystem/data" };
 
   for( auto dir : dataDirs )
   {
       try
       {
           return std::make_shared<ResourceManager>( dir );
       }
       catch( const BadDataDir & )
       {
             log << "Directory " << dir.c_str() << " does not contain system data."
       }
   }
 
   // Вышли сюда, значит не в одной директории нет данных, сообщаем об этом выше, может кто-то и будет знать что с этим делать...
   throw DataNotFound;
}
 

Поэтому, исключение в программе это вовсе не какая то трагедия, а штатная ситуация, которую можно легко обрабатывать.


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Igors от Март 27, 2014, 16:28
то она может попробовать создать еще 100500 таких менеджеров, пробуя все известные ей источники.
Весьма вероятно что такой класс будет синглтоном. Если же хотите иметь 2 или более менеджера ресурсов - то расскажите порядок загрузки ресурса, какой менеджер должен юзаться?
А смысла в исключениях здесь не вижу никакого - просто проверить существование фолдера/файла и не подавать его в конструктор, а сделать метод напр SetDir


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Old от Март 27, 2014, 16:37
Весьма вероятно что такой класс будет синглтоном. Если же хотите иметь 2 или более менеджера ресурсов - то расскажите порядок загрузки ресурса, какой менеджер должен юзаться?
Упаси Бог. Никаких синлтонов. Тем более для менеджеров ресурсов.
Я могу иметь 100500 источников с данными и для каждого источника иметь свой менеджер. В одном хранить картинки, в другом звуки. Могу иметь несколько источников с картинками.
Порядок поиска ресурсов может быть любой, хоть случайный. :) Или сделать приоритеты для менеджеров и искать сначала в более приоритетных источниках (главный системный и addon'ый). Это детали реализации конкретной системы, причем очень мелкие детали.

просто проверить существование фолдера/файла и не подавать его в конструктор, а сделать метод напр SetDir
Не вижу в этом никакого смысла. Кто то еще должен знать как это проверять, для чего? Пусть этим занимается менеджер ресурсов. Только он должен знать как проверить наличие и целостность данных.


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: OKTA от Март 27, 2014, 16:40
Сегодня наткнулся на ситуацию, когда попытка delete вызывает исключение, но оно не ловится даже если стоит catch(...), а ловится в дебаггере. Почему так?


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Old от Март 27, 2014, 16:49
Сегодня наткнулся на ситуацию, когда попытка delete вызывает исключение, но оно не ловится даже если стоит catch(...), а ловится в дебаггере. Почему так?
Скорее всего дебагер имеет ввиду не C++ исключение, а системное. Скорее всего где то бьете память.


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: OKTA от Март 27, 2014, 16:53
Да там просто память была заблокирована.
А как же ловить системные исключения?


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: Bepec от Март 27, 2014, 16:54
Вызывается системное исключение. Оно ловится __try __еxcept, но зачастую его ловля бессмысленна :D Стек бьётся в 50% случаев. 


Название: Re: Обработка ошибок без исключений и с помощью исключений
Отправлено: OKTA от Март 27, 2014, 16:58
Вызывается системное исключение. Оно ловится __try __еxcept, но зачастую его ловля бессмысленна :D Стек бьётся в 50% случаев. 
thanks)


Название: Re: [РЕШЕНО] Обработка ошибок без исключений и с помощью исключений
Отправлено: 8Observer8 от Апрель 12, 2014, 08:19
В начало темы добавил итог темы (с датой), чтобы если кто-то случай зайдёт, то ему не пришлось бы долго выискивать этот итог.

Итог темы. Добавил: Апрель 12, 2014, 09.10
Долго для себя выбирал стратегию ловли ошибок. Остановился на исключениях. Точнее даже на создании своих классов исключений, которые наследуют стандартные: http://i.pixs.ru/storage/2/3/2/171png_9973242_11655232.png

Настоятельно рекомендую прочитать главу "Chapter 10. Handling Errors" (особенно параграф "Writing Your Own Exception Classes") из книги:
Название: Professional C++
Год: 2011
Автор: Marc Gregoire, Nicholas A. Solter, Scott J. Kleper
Количество страниц: 1104
Язык: английский
Скачать: http://kickass.to/wrox-professional-c-plus-plus-2nd-edition-2011-retail-ebook-debt-t7461950.html
Исходники: http://www.wrox.com/WileyCDA/WroxTitle/Professional-C-2nd-Edition.productCd-0470932449,descCd-DOWNLOAD.html

Специально для демонстрации сделал два примера со стратегиями:
- с исключениями (как в книге выше): https://github.com/8Observer8/FiveAndFive
- без исключений: https://github.com/8Observer8/text_file

Стратегию "без исключений" мне подсказал "Igors".

P.S. Здесь проект как тестировать класс с исключениями FiveAndFive (выше). Только надо чтобы папки с проектами FiveAndFive и FiveAndFiveTests лежали в одном каталоге. Этот проект создаётся в Qt 5.2.1 так: File -> New File or Project... -> Other Project -> Qt Unit Test: https://github.com/8Observer8/FiveAndFiveTests (https://github.com/8Observer8/FiveAndFiveTests)


Название: Re: [РЕШЕНО] Обработка ошибок без исключений и с помощью исключений
Отправлено: OKTA от Апрель 14, 2014, 10:16
Спасибо, 8Observer8, за информацию!
Я понял причину неприязни многих людей к исключениям и свою в том числе)
Проблема исключений не в том, что лень писать лишний код, а в том, что методология return гораздо проще и понятнее логически, чем исключения. Я ради интереса сейчас намерен включить исключения в свой текущий проект и столкнулся с тем, что вот так просто не перепишешь под исключения имеющийся код, особенно когда хочешь использовать исключения не только как замену Return и удобный вывод ошибок пользователю, но и как средство для возможного решения проблем на месте. Немного ломается привычный подход) Но ничего, сделаю)


Название: Re: [РЕШЕНО] Обработка ошибок без исключений и с помощью исключений
Отправлено: 8Observer8 от Апрель 14, 2014, 11:19
Рад, что пригодилось! Разведка сработала нормально :)

Отмечу ещё такую очень важную особенность, которую я прочувствовал до костей. Если выбрасывать стандартные исключения, то текст вывода будет дублироваться и такой подход очень НЕгибкий, так как ничего кроме текста не выбросишь. Вот пример:
Код
C++ (Qt)
void doSomething1( int a, int b ) throw(std::out_of_range, std::invalid_argument) {
   // ...
   throw ( std::out_of_range( "...out of range..." ) );
   // ...
 
   // ...
   throw ( std::invalid_argument( "...invalid argument..." ) );
   // ...
}
 
void doSomething2( int a, int b ) throw(std::out_of_range, std::invalid_argument) {
   // ...
   throw ( std::out_of_range( "...out of range..." ) );
   // ...
 
   // ...
   throw ( std::invalid_argument( "...invalid argument..." ) );
   // ...
}
 

"std::out_of_range" и "std::invalid_argument" наследуют от std::logic_error, поэтому надо делать свой класс LogicError и наследовать его от std::logic_error. Текст ошибки будет формироваться в пользовательских классах OutOfRange и InvalidArgument, которые наследуют от LogicError. То есть текст ошибки только в одном месте. К примеру, если мы выбрасываем OutOfRange, то можем указать сам аргумент и в какой диапазон он не попал.

Вот вызов программы сложения двух целых чисел. Мы можем отлавливать оба пользовательских исключения (OutOfRange и InvalidArgument) одним блоком catch ( const LogicError &e ):
Код
C++ (Qt)
   int result = 0;
   try {
       result = sum( firstNumber, secondNumber );
   } catch ( const LogicError &e ) {
       std::cerr << e.what( ) << std::endl;
       return 1;
   } catch ( ... ) {
       std::cerr << "Uncaught exception." << std::endl;
       return 1;
   }
 

Реализация:
Код
C++ (Qt)
int sum( int firstNumber, int secondNumber ) throw (OutOfRange) {
   int const beginOfRange = -1000000000;
   int const endOfRange = 1000000000;
 
   if ( (firstNumber < beginOfRange) || (endOfRange < firstNumber) ) {
       throw ( OutOfRange( firstNumber, beginOfRange, endOfRange ));
   }
 
   if ( (secondNumber < beginOfRange) || (endOfRange < secondNumber) ) {
       throw ( OutOfRange( secondNumber, beginOfRange, endOfRange ));
   }
 
   int result = firstNumber + secondNumber;
   return result;
}
 

Я к тому, что обязательно надо писать свои классы исключений, так как они гибкие.

P.S. На всякий случай, в моём примере из предыдущего сообщения (FiveAndFive) - я это реализовал.


Название: Re: [РЕШЕНО] Обработка ошибок без исключений и с помощью исключений
Отправлено: 8Observer8 от Октябрь 15, 2014, 07:25
Использую стратегию из этой книги: http://www.amazon.com/Professional-C-Marc-Gregoire/dp/0470932449

Output
Цитировать
Error: divide by zero in the function func()

main.cpp
Код
C++ (Qt)
#include <iostream>
#include "AnotherError.h"
#include "DivideByZero.h"
 
void func( int a, int b )
throw( DivideByZero, AnotherError );
 
int main()
{
   try {
       func( 28, 0 );
   } catch ( const LogicError &e ) {
       std::cerr << e.what() << std::endl;
       return 1;
   } catch ( ... ) {
       std::cerr << "Error: unknown expection" << std::endl;
       return 1;
   }
 
   return 0;
}
 
void func( int a, int b )
throw( DivideByZero, AnotherError )
{
   std::string functionName = "func()";
 
   if ( b == 0 ) {
       throw DivideByZero( functionName );
   }
 
   // ...
 
   if ( a == 5 ) {
       throw AnotherError( functionName );
   }
}

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
 

AnotherError.h
Код
C++ (Qt)
#ifndef ANOTHERERROR_H
#define ANOTHERERROR_H
 
#include <string>
#include "LogicError.h"
 
class AnotherError : public LogicError
{
public:
   AnotherError( const std::string &functionName ) :
       LogicError( functionName )
   {
       m_message = "Error: some error in the "
               "function " + m_functionName;
   }
};
 
#endif // ANOTHERERROR_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
 


Название: Re: [РЕШЕНО] Обработка ошибок без исключений и с помощью исключений
Отправлено: Авварон от Октябрь 17, 2014, 09:10
Позанудствую - использовать исключения в программах на Qt чревато, так как Qt не только не использует исключения в своём API, но еще и некорректно обрабатывает пользовательские исключения, пролетевшие через кутешный код. К примеру, будет утечка в QVector, если конструктор Т кинет исключение. Писать правильный код с исключениями очень и очень сложно (std контейнеры - тому пример), поэтому, имхо, лучше их не использовать.
В свете с++11 мне очень нравится подход с maybe-типом (ака std::experimental::optional) - когда ф-ия возвращает либо значение, либо пустоту. Этот подход расширяется введением класса Error/Result (T value + bool ok + StringType errorString). В отличие от исключений, нам приходится учитывать, что ф-ия может вернуть ошибку и нет возможности пробросить её наверх, что, кмк, весьма удобно (вот если бы в с++ был жавовский спецификатор throws...). Ну и нет проблемы что у нас что-то пролетело из нижележащего кода, что нас поломало.
Upd: Вернее, возможность пробросить ошибку наверх как раз есть, только это приходится делать явно.


Название: Re: [РЕШЕНО] Обработка ошибок без исключений и с помощью исключений
Отправлено: 8Observer8 от Октябрь 17, 2014, 20:23
Цитировать
К примеру, будет утечка в QVector, если конструктор Т кинет исключение.
Ничего не понял. А в std::vector может быть аналогичная ситуация? Что такое T?

Цитировать
Писать правильный код с исключениями очень и очень сложно (std контейнеры - тому пример)
Не уверен, что правильно понимаю. То есть std контейнеры неудачны, так как в определённых ситуациях выбрасывают исключения, а Qt контейнеры не выбрасывает? Отсюда следует, что для того чтобы писать правильный код нужно полностью отказаться от std контейнеров? Qt контейнеры на 100 % заменяют std контейнеры?

Допустим есть такой код на std::vector, который позволяет контролировать выход за пределы массива:

Код
C++ (Qt)
#include <iostream>
#include <vector>
#include <stdexcept>
 
int main()
{
   std::vector<int> arr = { 1, 2, 3 };
 
   try {
       std::cout << arr.at( 3 ) << std::endl;
   } catch( std::logic_error &e ) {
       std::cerr << "Error: out of range" << std::endl;
       return 1;
   } catch( ... ) {
       std::cerr << "Error: " << std::endl;
       return 1;
   }
 
   return 0;
}
 

Если я использую этот код в Qt программе, что мне за это будет? Почему я не могу использовать исключения аккуратно? Почему именно "лучше их не использовать"?

Извините за возможно глупые вопросы, но я начал изучать профессионально C++ и Qt только в этом году


Название: Re: [РЕШЕНО] Обработка ошибок без исключений и с помощью исключений
Отправлено: Bepec от Октябрь 17, 2014, 21:29
Qt написан без исключений и без возможности их применения.
Т.е. могут возникнуть десятки и сотни утечек/ошибок в внутренностях Qt.

Собственно надо следовать мудрости Страуструпа - "Всегда писать с исключениями или без них, не смешивая".


Название: Re: [РЕШЕНО] Обработка ошибок без исключений и с помощью исключений
Отправлено: 8Observer8 от Октябрь 18, 2014, 08:32
Qt написан без исключений и без возможности их применения.
Т.е. могут возникнуть десятки и сотни утечек/ошибок в внутренностях Qt.
Не понимаю. Причём тут "Qt написан без исключений"

"без возможности их применения. " Как так? Я же могу применять. Позволяет

Я выбросил исключение из функции и я же его обработал. О чём вы говорите? Вообще не понимаю

Код
C++ (Qt)
void func( int a, int b )
throw( DivideByZero, AnotherError );
 

Код
C++ (Qt)
   try {
       func( 28, 0 );
   } catch ( const LogicError &e ) {
       std::cerr << e.what() << std::endl;
       return 1;
   } catch ( ... ) {
       std::cerr << "Error: unknown expection" << std::endl;
       return 1;
   }
 


Название: Re: [РЕШЕНО] Обработка ошибок без исключений и с помощью исключений
Отправлено: 8Observer8 от Октябрь 18, 2014, 10:05
У меня ещё такой вопрос. Можно ли считать огромной оплошностью разработчиков Qt, что они разрешили использовать в программах на Qt функции из C++11 для перевода из строки в число? Может надо было вообще запретить ключевые слова try, catch, throw? Или хотя бы выдавать предупреждения?

Вот пример кода на C++11. Здесь я обязан отлавливать исключения, так как получу крэш, если пользователь введёт буквы вместо цифр (в этом случае функция std::stod() выбрасывает исключения std::invalid_argument и std::out_of_range)

Код
C++ (Qt)
#include <string>
#include <stdexcept>
#include <iostream>
 
int main()
{
   try {
       std::cout << "\nEnter a real number:\n> ";
       std::string str;
       std::getline( std::cin, str );
       double n = stod( str );
       std::cout << n << std::endl;
   } catch ( const std::logic_error ) {
       std::cerr << "Error: you wrote the incorrect number." << std::endl;
       return 1;
   } catch ( ... ) {
       std::cerr << "Error: unknown exception" << std::endl;
       return 1;
   }
 
   return 0;
}
 


Название: Re: [РЕШЕНО] Обработка ошибок без исключений и с помощью исключений
Отправлено: Bepec от Октябрь 18, 2014, 11:12
Они рекомендуют не использовать исключения.
Если вы их используете - вы берёте на себя ответственность за поведение программы :)

Так то если не мешать Qt классы и исключения, то вполне всё работает и не сбоит.

Вы можете спрыгнуть с 130 этажа, но создатель объекта "человек" не предусматривал такую ситуацию и поведение будет непредсказуемым :D

offtop: были случаи выживших при падении с 10к метров. Так что это действительно неопределённое поведение :)


Название: Re: [РЕШЕНО] Обработка ошибок без исключений и с помощью исключений
Отправлено: 8Observer8 от Октябрь 18, 2014, 11:43
Исключения - это большая ответственность, но при этом собственные классы исключений очень облегчают разработку!

Написал небольшую статью: Стратегия обработки ошибок пользователя с помощью собственных классов исключений (http://www.prog.org.ru/topic_27797_0.html)


Название: Re: [РЕШЕНО] Обработка ошибок без исключений и с помощью исключений
Отправлено: Bepec от Октябрь 18, 2014, 16:50
Вы не написали статью. Вы написали сообщение с ссылками. Почитайте на досуге чем они отличаются :D