Russian Qt Forum
Сентябрь 23, 2024, 18:26 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: 1 ... 10 11 [12]   Вниз
  Печать  
Автор Тема: Тренировка навыков быстрого программирования  (Прочитано 79877 раз)
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #165 : Март 18, 2014, 16:40 »

Могу ли я так написать (то есть вернуть ErrorType::errFileOpen):
Код
C++ (Qt)
 
   if ( errorCode != ErrorType::errNone ) {
       return showError( iFileName, errorCode );
   }
}
 
Вот без errorCode ф-ция showError точно не живет, а iFileName - хз, может и пустой быть. Так зачем ставить первостепенный аргумент вторым? Вообще незатейливый вариант
Код
C++ (Qt)
   if (error)
       return showError(error, iFileName);
 
мне нравится куда больше. Связавшись с ErrorType:: Вы конечно поступили "академически" правильно. Но выгода с той академичности когда еще будет, а таскать везде квалифицированное имя уже приходится  Улыбающийся
Записан
8Observer8
Гость
« Ответ #166 : Март 18, 2014, 17:13 »

С очерёдностью согласен. Имя бы ещё аргументом по умолчанию сделать. Ну, а ErrorType:: я всё же потаскаю. Выглядит солидно Улыбающийся Да и хочеться к хорошему привыкать. Когда привыкаешь, то уже легче становится Улыбающийся
Записан
8Observer8
Гость
« Ответ #167 : Апрель 03, 2014, 18:37 »

Спасибо вам огромное, форумчане! C вашей помощью я нашёл подход к оформлению задач. Этот подход использую во всех задачах здесь: www.acmp.ru Просто копирую код из задачи в задачу и немного меняю исходник (это, конечно, не касается содержательной части Улыбающийся )

Поясню подход на примере самой первой задачи: http://acmp.ru/?main=task&id_task=1

Благодаря классам исключений, в main простой и аккуратный код:
Код
C++ (Qt)
int main() {
   std::string fileNameIn = "input.txt";
   int firstNumber = 0;
   int secondNumber = 0;
 
   try {
       readData( fileNameIn, firstNumber, secondNumber );
   } catch ( const FileError &e ) {
       std::cerr << e.what( ) << std::endl;
       return 1;
   }
 
   int result = sum(firstNumber, secondNumber);
 
   std::string fileNameOut = "output.txt";
   try {
       writeResult( fileNameOut, result );
   } catch ( const FileError &e ) {
       std::cerr << e.what( ) << std::endl;
       return 1;
   }
 
   return 0;
}

Я теперь сначала пишу заглушку, которая решает задачу, к примеру:
Код
C++ (Qt)
int sum(int firstNumber, int secondNumber) {
   return 0;
}

А потом тесты c помощью "Qt Unit Test". Всё согласно методологии TDD (сначала тесты, которые учитывают (описывают) всю функциональность, а после написания тестов - пишу саму функциональность):
Код
C++ (Qt)
   QTest::addColumn<int>("firstNumber");
   QTest::addColumn<int>("secondNumber");
   QTest::addColumn<int>("expected");
 
   QTest::newRow("sum_01") << 1 << 2 << 3;
   QTest::newRow("sum_02") << -1 << -2 << -3;
   QTest::newRow("sum_03") << -3 << 5 << 2;
   QTest::newRow("sum_04") << 5 << -3 << 2;
   QTest::newRow("sum_05") << -5 << 3 << -2;
 

В файле .pro проекта  "Qt Unit Test" надо писать путь к main.cpp SOURCES += ../acmp_0001_sum/main.cpp

А в main.cpp определить директиву #define TESTING чтобы можно было тестировать, а перед отправкой на сервер www.acmp.ru надо закомментировать эту строку #define TESTING. Вот как это выглядит:
Код
C++ (Qt)
//#define TESTING
#ifndef TESTING
// ...
int main() {
   // ...
}
#endif // TESTING
 
int sum(int firstNumber, int secondNumber) {
   int result = firstNumber + secondNumber;
   return result;
}
 

Вот весь код двух проектов (в одном решается задача, а в другом тесты для задачи)
acmp_0001_sum.pro
Код
C++ (Qt)
SOURCES += \
   main.cpp
 

main.cpp
Код
C++ (Qt)
//#define TESTING
#ifndef TESTING
 
#include <iostream>
#include <stdexcept>
#include <string>
#include <sstream>
#include <fstream>
 
class FileError : public std::runtime_error {
public:
 
   FileError( const std::string &fileIn ) : std::runtime_error( "" ), m_file( fileIn ) {
   }
 
   virtual const char* what( ) const throw() {
       return m_msg.c_str( );
   }
 
   std::string getFileName( ) const {
       return m_file;
   }
 
   virtual ~FileError() throw() {
 
   }
 
protected:
   std::string m_file;
   std::string m_msg;
};
 
class FileOpenError : public FileError {
public:
 
   FileOpenError( const std::string &fileNameIn ) : FileError( fileNameIn ) {
       m_msg = "Unable to open " + fileNameIn;
   }
};
 
class FileReadError : public FileError {
public:
 
   FileReadError( const std::string &fileNameIn, int lineNumIn ) :
   FileError( fileNameIn ), m_lineNum( lineNumIn ) {
       std::ostringstream ostr;
       ostr << "Error reading " << fileNameIn << " at line " << lineNumIn;
       m_msg = ostr.str( );
   }
 
   int getLineNum( ) const {
       return m_lineNum;
   }
 
protected:
   int m_lineNum;
};
 
class FileWriteError : public FileError {
public:
 
   FileWriteError( const std::string &fileNameIn ) : FileError( fileNameIn ) {
       m_msg = "Unable to write " + fileNameIn;
   }
};
 
void readData( const std::string &fileName, int &firstNumber, int &secondNumber )
throw (FileOpenError, FileReadError);
 
void writeResult( const std::string &fileName, int number )
throw (FileOpenError, FileWriteError);
 
int sum(int firstNumber, int secondNumber);
 
int main() {
   std::string fileNameIn = "input.txt";
   int firstNumber = 0;
   int secondNumber = 0;
 
   try {
       readData( fileNameIn, firstNumber, secondNumber );
   } catch ( const FileError &e ) {
       std::cerr << e.what( ) << std::endl;
       return 1;
   }
 
   int result = sum(firstNumber, secondNumber);
 
   std::string fileNameOut = "output.txt";
   try {
       writeResult( fileNameOut, result );
   } catch ( const FileError &e ) {
       std::cerr << e.what( ) << std::endl;
       return 1;
   }
 
   return 0;
}
 
void readData( const std::string &fileName, int &firstNumber, int &secondNumber )
throw ( FileOpenError, FileReadError ) {
   std::ifstream file;
   file.open( fileName.c_str( ) );
   if ( file.fail( ) ) {
       throw FileOpenError( fileName );
   }
 
   int lineNumIn = 0;
   if ( !( file >> firstNumber >> secondNumber ) ) {
       lineNumIn++;
       throw FileReadError( fileName, lineNumIn );
   }
}
 
void writeResult( const std::string &fileName, int number )
throw (FileOpenError, FileWriteError) {
   std::ofstream file;
   file.open( fileName.c_str( ) );
 
   if ( file.fail( ) ) {
       throw FileOpenError( fileName );
   }
 
   if ( !( file << number ) ) {
       throw FileWriteError( fileName );
   }
 
   file << std::endl;
}
#endif // TESTING
 
int sum(int firstNumber, int secondNumber) {
   int result = firstNumber + secondNumber;
   return result;
}
 
 

acmp_0001_sum_tests.pro
Код
C++ (Qt)
#-------------------------------------------------
#
# Project created by QtCreator 2014-04-03T19:03:48
#
#-------------------------------------------------
 
QT       += testlib
 
QT       -= gui
 
TARGET = tst_SumTests
CONFIG   += console
CONFIG   -= app_bundle
 
TEMPLATE = app
 
 
SOURCES += tst_SumTests.cpp
SOURCES += ../acmp_0001_sum/main.cpp
DEFINES += SRCDIR=\\\"$$PWD/\\\"

tst_SumTests.cpp
Код
C++ (Qt)
#include <QString>
#include <QtTest>
 
int sum(int firstNumber, int secondNumber);
 
class SumTests : public QObject
{
   Q_OBJECT
 
public:
   SumTests();
 
private Q_SLOTS:
   void testCase1_data();
   void testCase1();
};
 
SumTests::SumTests()
{
}
 
void SumTests::testCase1_data()
{
   QTest::addColumn<int>("firstNumber");
   QTest::addColumn<int>("secondNumber");
   QTest::addColumn<int>("expected");
 
   QTest::newRow("sum_01") << 1 << 2 << 3;
   QTest::newRow("sum_02") << -1 << -2 << -3;
   QTest::newRow("sum_03") << -3 << 5 << 2;
   QTest::newRow("sum_04") << 5 << -3 << 2;
   QTest::newRow("sum_05") << -5 << 3 << -2;
}
 
void SumTests::testCase1()
{
   QFETCH(int, firstNumber);
   QFETCH(int, secondNumber);
   QFETCH(int, expected);
 
   int actual = sum(firstNumber, secondNumber);
   QCOMPARE(actual, expected);
}
 
QTEST_APPLESS_MAIN(SumTests)
 
#include "tst_SumTests.moc"
 
« Последнее редактирование: Апрель 03, 2014, 18:50 от 8Observer8 » Записан
8Observer8
Гость
« Ответ #168 : Апрель 05, 2014, 20:28 »

Я ещё усовершенствовал программу. Теперь функция может проверять свой аргумент.

Допустим мы хотим разработать функцию для решения задачи: http://acmp.ru/?main=task&id_task=3

Я назвал эту функцию: int five_and_five(int number). Она возвращает квадрат аргумента. В задаче сказано, что функция принимает аргумент, который заканчивается на цифру 5. А ещё есть ограничения диапазона значений аргумента: [5; 4*10^5]

Я решил смоделировать ситуацию, когда функция может контролировать свой входной аргумент и сообщать, если нарушено соглашение вызова.

Для этой функции хорошо бы подошли классы из стандартной библиотеки, унаследованные от std::logic_error: это std::invalid_argument и std::out_of_range: http://www.cplusplus.com/reference/stdexcept/logic_error/

Но тогда придёться писать текст сообщений об ошибках в функции five_and_five(), что совсем неудобно, так как в каждой тестируемой функции (если их будет много) придёться дублировать сообщения об ошибках). Поэтому я написал два класса исключений: InvalidArgument, OutOfRange унаследованные от третьего: LogicError, который в свою очередь унаследован от std::logic_error:
Код
C++ (Qt)
class LogicError : public std::logic_error {
public:
 
   LogicError( int argument ) : std::logic_error( "" ), m_argument( argument ) {
 
   }
 
   virtual const char *what( ) const throw () {
       return m_message.c_str( );
   }
 
   virtual ~LogicError( ) throw () {
 
   }
 
protected:
   int m_argument;
   std::string m_message;
};
 
class InvalidArgument : public LogicError {
public:
 
   InvalidArgument( int argument ) : LogicError( argument ) {
       std::stringstream stream;
       stream << argument;
       m_message = "Argument " + stream.str( ) + " mush be multiple of 5";
   }
};
 
class OutOfRange : public LogicError {
public:
 
   OutOfRange( int argument, int beginOfRange, int endOfRange ) : LogicError( argument ) {
       std::stringstream stream;
       std::string str_argument, str_beginOfRange, str_endOfRange;
 
       stream << argument;
       str_argument = stream.str();
       stream.str("");
 
       stream << beginOfRange;
       str_beginOfRange = stream.str();
       stream.str("");
 
       stream << endOfRange;
       str_endOfRange = stream.str();
       stream.str("");
 
       m_message = "Argument " + str_argument + " don't hit on the range [" +
               str_beginOfRange + ", " + str_endOfRange + "]";
   }
};
 

Пользователь функции five_and_five() может легко понять, какие исключения она выбрасывает. Для этого ему достаточно посмотреть на её объявление:
Код
C++ (Qt)
double fiveAndFive( int number ) throw (InvalidArgument, OutOfRange);
 

Так пользователь будет вызывать функцию в main():
Код
C++ (Qt)
   int result = 0;
   try {
       result = fiveAndFive(number);
   } catch ( const LogicError &e ) {
       std::cerr << e.what( ) << std::endl;
       return 1;
   } catch ( ... ) {
       std::cerr << "Uncaught exception." << std::endl;
       return 1;
   }
 

Так выглядит сама функция:
Код
C++ (Qt)
double fiveAndFive( int number ) throw (InvalidArgument, OutOfRange) {
   if ( (number % 5) != 0 ) {
       throw InvalidArgument( number );
   }
 
   const int beginOfRange = 5;
   const int endOfRange = 400000;
   if ( (number < beginOfRange) || (endOfRange < number) ) {
       throw OutOfRange( number, beginOfRange, endOfRange );
   }
 
   double result = (double) number * (double) number;
   return result;
}
 

Вот решение задачи (здесь макрос "#define TESTING" нужен для переключения в режим тестирования с помощью QTest):
Код
C++ (Qt)
#include <stdexcept>
#include <string>
#include <sstream>
 
//#define TESTING
#ifndef TESTING
 
#include <iostream>
#include <fstream>
 
class FileError : public std::runtime_error {
public:
 
   FileError( const std::string &fileIn ) : std::runtime_error( "" ), m_file( fileIn ) {
   }
 
   virtual const char* what( ) const throw() {
       return m_msg.c_str( );
   }
 
   std::string getFileName( ) const {
       return m_file;
   }
 
   virtual ~FileError() throw() {
 
   }
 
protected:
   std::string m_file;
   std::string m_msg;
};
 
class FileOpenError : public FileError {
public:
 
   FileOpenError( const std::string &fileNameIn ) : FileError( fileNameIn ) {
       m_msg = "Unable to open " + fileNameIn;
   }
};
 
class FileReadError : public FileError {
public:
 
   FileReadError( const std::string &fileNameIn, int lineNumIn ) :
       FileError( fileNameIn ), m_lineNum( lineNumIn ) {
       std::ostringstream ostr;
       ostr << "Error reading " << fileNameIn << " at line " << lineNumIn;
       m_msg = ostr.str( );
   }
 
   int getLineNum( ) const {
       return m_lineNum;
   }
 
protected:
   int m_lineNum;
};
 
class FileWriteError : public FileError {
public:
 
   FileWriteError( const std::string &fileNameIn ) : FileError( fileNameIn ) {
       m_msg = "Unable to write " + fileNameIn;
   }
};
#endif // TESTING
 
class LogicError : public std::logic_error {
public:
 
   LogicError( int argument ) : std::logic_error( "" ), m_argument( argument ) {
 
   }
 
   virtual const char *what( ) const throw () {
       return m_message.c_str( );
   }
 
   virtual ~LogicError( ) throw () {
 
   }
 
protected:
   int m_argument;
   std::string m_message;
};
 
class InvalidArgument : public LogicError {
public:
 
   InvalidArgument( int argument ) : LogicError( argument ) {
       std::stringstream stream;
       stream << argument;
       m_message = "Argument " + stream.str( ) + " mush be multiple of 5";
   }
};
 
class OutOfRange : public LogicError {
public:
 
   OutOfRange( int argument, int beginOfRange, int endOfRange ) : LogicError( argument ) {
       std::stringstream stream;
       stream << argument << beginOfRange << endOfRange;
       std::string str_argument, str_beginOfRange, str_endOfRange;
       stream >> str_argument >> str_beginOfRange >> str_endOfRange;
 
       m_message = "Argument " + str_argument + " don't hit on the range [" +
               str_beginOfRange + ", " + str_endOfRange + "]";
   }
};
 
#ifndef TESTING
void readData( const std::string &fileName, int &number )
throw (FileOpenError, FileReadError);
 
void writeResult( const std::string &fileName, int result )
throw (FileOpenError, FileWriteError);
 
double fiveAndFive( int number ) throw (InvalidArgument, OutOfRange);
 
int main() {
   std::string fileNameIn = "input.txt";
   int number = 0;
 
   try {
       readData( fileNameIn, number );
   } catch ( const FileError &e ) {
       std::cerr << e.what( ) << std::endl;
       return 1;
   } catch ( ... ) {
       std::cerr << "Uncaught exception." << std::endl;
       return 1;
   }
 
   int result = 0;
   try {
       result = fiveAndFive(number);
   } catch ( const LogicError &e ) {
       std::cerr << e.what( ) << std::endl;
       return 1;
   } catch ( ... ) {
       std::cerr << "Uncaught exception." << std::endl;
       return 1;
   }
 
   std::string fileNameOut = "output.txt";
   try {
       writeResult( fileNameOut, result );
   } catch ( const FileError &e ) {
       std::cerr << e.what( ) << std::endl;
       return 1;
   }
 
   return 0;
}
 
void readData( const std::string &fileName, int &number )
throw ( FileOpenError, FileReadError ) {
   std::ifstream file;
   file.open( fileName.c_str( ) );
   if ( file.fail( ) ) {
       throw FileOpenError( fileName );
   }
 
   int lineNumIn = 0;
   if ( !( file >> number ) ) {
       lineNumIn++;
       throw FileReadError( fileName, lineNumIn );
   }
}
 
void writeResult( const std::string &fileName, int result )
throw (FileOpenError, FileWriteError) {
   std::ofstream file;
   file.open( fileName.c_str( ) );
 
   if ( file.fail( ) ) {
       throw FileOpenError( fileName );
   }
 
   if ( !( file << result ) ) {
       throw FileWriteError( fileName );
   }
 
   file << std::endl;
}
#endif // TESTING
 
double fiveAndFive( int number ) throw (InvalidArgument, OutOfRange) {
   if ( (number % 5) != 0 ) {
       throw InvalidArgument( number );
   }
 
   const int beginOfRange = 5;
   const int endOfRange = 400000;
   if ( (number < beginOfRange) || (endOfRange < number) ) {
       throw OutOfRange( number, beginOfRange, endOfRange );
   }
 
   double result = (double) number * (double) number;
   return result;
}
 
« Последнее редактирование: Апрель 06, 2014, 09:00 от 8Observer8 » Записан
8Observer8
Гость
« Ответ #169 : Апрель 06, 2014, 08:59 »

Следующий код неверный:
Код
C++ (Qt)
class OutOfRange : public LogicError {
public:
 
   OutOfRange( int argument, int beginOfRange, int endOfRange ) : LogicError( argument ) {
       std::stringstream stream;
       stream << argument << beginOfRange << endOfRange;
       std::string str_argument, str_beginOfRange, str_endOfRange;
       stream >> str_argument >> str_beginOfRange >> str_endOfRange;
 
       m_message = "Argument " + str_argument + " don't hit on the range [" +
               str_beginOfRange + ", " + str_endOfRange + "]";
   }
};
 

Я его заменил на:
Код
C++ (Qt)
class OutOfRange : public LogicError {
public:
 
   OutOfRange( int argument, int beginOfRange, int endOfRange ) : LogicError( argument ) {
       std::stringstream stream;
       std::string str_argument, str_beginOfRange, str_endOfRange;
 
       stream << argument;
       str_argument = stream.str();
       stream.str("");
 
       stream << beginOfRange;
       str_beginOfRange = stream.str();
       stream.str("");
 
       stream << endOfRange;
       str_endOfRange = stream.str();
       stream.str("");
 
       m_message = "Argument " + str_argument + " don't hit on the range [" +
               str_beginOfRange + ", " + str_endOfRange + "]";
   }
};
 
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #170 : Апрель 06, 2014, 09:09 »

Сделай те уже функции для перевода числа в строку и наоборот. Улыбающийся
С шаблонами можно сделать две универсальные функции для любых чисел.
Записан
8Observer8
Гость
« Ответ #171 : Апрель 06, 2014, 11:07 »

Спасибо за идею Улыбающийся

Ну, как дело дойдёт до double - сделаю. А пока для int:
Код
C++ (Qt)
std::string intToString( int number ) {
   std::stringstream stream;
   stream << number;
   return stream.str( );
}
 
class OutOfRange : public LogicError {
public:
 
   OutOfRange( int argument, int beginOfRange, int endOfRange ) : LogicError( argument ) {
       std::string str_argument, str_beginOfRange, str_endOfRange;
 
       str_argument = intToString( argument );
       str_beginOfRange = intToString( beginOfRange );
       str_endOfRange = intToString( endOfRange );
 
       m_message = "Argument " + str_argument + " don't hit on the range [" +
               str_beginOfRange + ", " + str_endOfRange + "]";
   }
};
 
« Последнее редактирование: Апрель 06, 2014, 11:09 от 8Observer8 » Записан
8Observer8
Гость
« Ответ #172 : Апрель 07, 2014, 11:49 »

Оставляю здесь довольно полный пример. Возможно, кому-нибудь пригодится Улыбающийся

Пример разработки ПО через тестирование

Задание на разработку.. Написать класс с именем FiveAndFive. В этом классе должен быть метод fiveAndFive(), который возвращает квадрат своего аргумента.

Соглашения вызова метода:
1) Метод работает с аргументом из диапазона: [5, 4*10^5]. Если нарушено это соглашение, то метод выбрасывает исключение.
2) Метод работает с аргументом кратным пяти. Если нарушено это соглашение, то метод выбрасывает исключение.

- создаём консольное приложение "Qt Console Application"
Имя папки: FiveAndFiveProject
Имя проекта: FiveAndFive

- создаём проект "Qt Unit Test" в той же папке: FiveAndFiveProject
Имя проекта: FiveAndFiveTests
Имя класса с тестами: FiveAndFiveTests
Имя исходного файла с тестами: tst_FiveAndFiveTests.cpp

- в проекте FiveAndFive создаём класс с тем же именем: FiveAndFive, а так же создаём заглушку для метода fiveAndFive(). Так же добавляем классы исключений (код проекта см. ниже)

Весь код проекта FiveAndFive: https://github.com/8Observer8/FiveAndFive

FiveAndFive.pro
Код
C++ (Qt)
QT       += core
 
QT       -= gui
 
TARGET = FiveAndFive
CONFIG   += console
CONFIG   -= app_bundle
 
TEMPLATE = app
 
 
SOURCES += main.cpp \
   FiveAndFive.cpp
 
HEADERS += \
   FiveAndFive.h \
   FileError.h \
   FileOpenError.h \
   FileReadError.h \
   FileWriteError.h \
   LogicError.h \
   InvalidArgument.h \
   OutOfRange.h
 

FiveAndFive.h
Код
C++ (Qt)
#ifndef FIVEANDFIVE_H
#define FIVEANDFIVE_H
 
#include "InvalidArgument.h"
#include "OutOfRange.h"
 
class FiveAndFive {
public:
   double fiveAndFive( int number ) throw (InvalidArgument, OutOfRange);
};
 
#endif // FIVEANDFIVE_H
 

FiveAndFive.cpp
Код
C++ (Qt)
#include "FiveAndFive.h"
 
double FiveAndFive::fiveAndFive( int number ) throw (InvalidArgument, OutOfRange) {
   if ( (number % 5) != 0 ) {
       throw InvalidArgument( number );
   }
 
   const int beginOfRange = 5;
   const int endOfRange = 400000;
   if ( (number < beginOfRange) || (endOfRange < number) ) {
       throw OutOfRange( number, beginOfRange, endOfRange );
   }
 
   double result = (double) number * (double) number;
   return result;
}
 

main.cpp
Код
C++ (Qt)
#include <QCoreApplication>
#include <iostream>
#include <fstream>
#include "FiveAndFive.h"
#include "FileError.h"
#include "FileOpenError.h"
#include "FileReadError.h"
#include "FileWriteError.h"
#include "LogicError.h"
 
void readData( const std::string &fileName, int &number )
throw (FileOpenError, FileReadError);
 
void writeResult( const std::string &fileName, int result )
throw (FileOpenError, FileWriteError);
 
int main( int argc, char *argv[] ) {
   QCoreApplication a( argc, argv );
 
   std::string fileNameIn = "input.txt";
   int number = 0;
 
   try {
       readData( fileNameIn, number );
   } catch ( const FileError &e ) {
       std::cerr << e.what( ) << std::endl;
       return 1;
   } catch ( ... ) {
       std::cerr << "Uncaught exception." << std::endl;
       return 1;
   }
 
   int result = 0;
   FiveAndFive faf;
   try {
       result = faf.fiveAndFive( number );
   } catch ( const LogicError &e ) {
       std::cerr << e.what( ) << std::endl;
       return 1;
   } catch ( ... ) {
       std::cerr << "Uncaught exception." << std::endl;
       return 1;
   }
 
   std::string fileNameOut = "output.txt";
   try {
       writeResult( fileNameOut, result );
   } catch ( const FileError &e ) {
       std::cerr << e.what( ) << std::endl;
       return 1;
   }
 
   std::cout << "See file " << fileNameOut << std::endl;
 
   return a.exec( );
}
 
void readData( const std::string &fileName, int &number )
throw ( FileOpenError, FileReadError) {
   std::ifstream file;
   file.open( fileName.c_str( ) );
   if ( file.fail( ) ) {
       throw FileOpenError( fileName );
   }
 
   int lineNumIn = 0;
   if ( !(file >> number) ) {
       lineNumIn++;
       throw FileReadError( fileName, lineNumIn );
   }
}
 
void writeResult( const std::string &fileName, int result )
throw (FileOpenError, FileWriteError) {
   std::ofstream file;
   file.open( fileName.c_str( ) );
 
   if ( file.fail( ) ) {
       throw FileOpenError( fileName );
   }
 
   if ( !(file << result) ) {
       throw FileWriteError( fileName );
   }
 
   file << std::endl;
}
 

LogicError.h
Код
C++ (Qt)
#ifndef LOGICERROR_H
#define LOGICERROR_H
 
#include <string>
#include <stdexcept>
 
class LogicError : public std::logic_error {
public:
 
   LogicError( int argument ) : std::logic_error( "" ), m_argument( argument ) {
 
   }
 
   virtual const char *what( ) const throw () {
       return m_message.c_str( );
   }
 
   virtual ~LogicError( ) throw () {
 
   }
 
protected:
   int m_argument;
   std::string m_message;
};
 
#endif // LOGICERROR_H
 

InvalidArgument.h
Код
C++ (Qt)
#ifndef INVALIDARGUMENT_H
#define INVALIDARGUMENT_H
 
#include <string>
#include <sstream>
#include "LogicError.h"
 
class InvalidArgument : public LogicError {
public:
 
   InvalidArgument( int argument ) : LogicError( argument ) {
       std::stringstream stream;
       stream << argument;
       m_message = "Argument " + stream.str( ) + " mush be multiple of 5";
   }
};
 
#endif // INVALIDARGUMENT_H
 

OutOfRange.h
Код
C++ (Qt)
#ifndef OUTOFRANGE_H
#define OUTOFRANGE_H
 
#include <string>
#include <sstream>
#include "LogicError.h"
 
class OutOfRange : public LogicError {
public:
 
   OutOfRange( int argument, int beginOfRange, int endOfRange ) : LogicError( argument ) {
       std::string str_argument, str_beginOfRange, str_endOfRange;
 
       str_argument = intToString( argument );
       str_beginOfRange = intToString( beginOfRange );
       str_endOfRange = intToString( endOfRange );
 
       m_message = "Argument " + str_argument + " don't hit on the range [" +
               str_beginOfRange + ", " + str_endOfRange + "]";
   }
 
private:
   std::string intToString( int number ) {
       std::stringstream stream;
       stream << number;
       return stream.str( );
   }
};
 
#endif // OUTOFRANGE_H
 

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 &fileIn ) : std::runtime_error( "" ), m_file( fileIn ) {
   }
 
   virtual const char* what( ) const throw() {
       return m_msg.c_str( );
   }
 
   virtual ~FileError() throw() {
 
   }
 
protected:
   std::string m_file;
   std::string m_msg;
};
 
#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 &fileNameIn ) : FileError( fileNameIn ) {
       m_msg = "Unable to open " + fileNameIn;
   }
};
 
#endif // FILEOPENERROR_H
 

FileReadError.h
Код
C++ (Qt)
#ifndef FILEREADERROR_H
#define FILEREADERROR_H
 
#include <string>
#include <sstream>
#include "FileError.h"
 
class FileReadError : public FileError {
public:
 
   FileReadError( const std::string &fileNameIn, int lineNumIn ) :
       FileError( fileNameIn ), m_lineNum( lineNumIn ) {
       std::ostringstream ostr;
       ostr << "Error reading " << fileNameIn << " at line " << lineNumIn;
       m_msg = ostr.str( );
   }
 
protected:
   int m_lineNum;
};
 
#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 &fileNameIn ) : FileError( fileNameIn ) {
       m_msg = "Unable to write " + fileNameIn;
   }
};
 
#endif // FILEWRITEERROR_H
 

- открываем файл tst_FiveAndFiveTests.cpp проекта FiveAndFiveTests и добавляем тесты (код проекта см. ниже)

Примечание. Не забудьте добавить в файл .pro строки:
Код
C++ (Qt)
INCLUDEPATH += "../FiveAndFive"
SOURCES += ../FiveAndFive/FiveAndFive.cpp
 

Весь код проекта FiveAndFiveTests: https://github.com/8Observer8/FiveAndFiveTests

FiveAndFiveTests.pro
Код
C++ (Qt)
QT       += testlib
 
QT       -= gui
 
TARGET = tst_FiveAndFiveTests
CONFIG   += console
CONFIG   -= app_bundle
 
TEMPLATE = app
 
 
SOURCES += tst_FiveAndFiveTests.cpp
INCLUDEPATH += "../FiveAndFive"
SOURCES += ../FiveAndFive/FiveAndFive.cpp
DEFINES += SRCDIR=\\\"$$PWD/\\\"

tst_FiveAndFiveTests.cpp
Код
C++ (Qt)
#include <QString>
#include <QtTest>
 
#include "FiveAndFive.h"
 
class FiveAndFiveTests : public QObject {
   Q_OBJECT
 
public:
   FiveAndFiveTests( );
 
   static inline bool qFuzzyCompare( double p1, double p2, double delta ) {
       return ( qAbs( p1 - p2 ) <= delta * qMin( qAbs( p1 ), qAbs( p2 ) ));
   }
 
   private
Q_SLOTS:
   void testCase1_data( );
   void testCase1( );
};
 
FiveAndFiveTests::FiveAndFiveTests( ) {
}
 
void FiveAndFiveTests::testCase1_data( ) {
   QTest::addColumn<int>("number");
   QTest::addColumn<double>("expected");
   QTest::addColumn<bool>("isExpectedException");
 
   bool exception = true;
   bool no_exception = false;
 
   QTest::newRow( "fiveAndFive_02" ) << 0 << 0.0 << exception;
   QTest::newRow( "fiveAndFive_01" ) << 1 << 0.0 << exception;
   QTest::newRow( "fiveAndFive_03" ) << 4 << 0.0 << exception;
   QTest::newRow( "fiveAndFive_04" ) << 5 << 25.0 << no_exception;
   QTest::newRow( "fiveAndFive_05" ) << 6 << 0.0 << exception;
   QTest::newRow( "fiveAndFive_06" ) << 399995 << 159996000025.0 << no_exception;
   QTest::newRow( "fiveAndFive_07" ) << 400000 << 160000000000.0 << no_exception;
   QTest::newRow( "fiveAndFive_08" ) << 400001 << 0.0 << exception;
   QTest::newRow( "fiveAndFive_09" ) << 400005 << 0.0 << exception;
}
 
void FiveAndFiveTests::testCase1( ) {
   QFETCH( int, number );
   QFETCH( double, expected );
   QFETCH( bool, isExpectedException );
 
   double delta = 0.0001;
 
   FiveAndFive faf;
 
   try {
       double actual = faf.fiveAndFive( number );
       bool result = qFuzzyCompare( actual, expected, delta );
       if ( isExpectedException ) {
           QVERIFY2( false, "There is no exception." );
       } else {
           QString msg = QString( "\nActual: %1"
                   "\nExpected: %2"
                   "\nDelta: %3" ).arg( actual ).arg( expected ).arg( delta );
           QVERIFY2( result, msg.toStdString( ).c_str( ) );
       }
 
   } catch ( const LogicError& e ) {
       if ( !isExpectedException ) {
           QVERIFY2( false, "Exception was occur." );
       } else {
           QVERIFY2( true, "" );
       }
   } catch ( ... ) {
       QVERIFY2( false, "Uncaught exception." );
   }
}
 
QTEST_APPLESS_MAIN( FiveAndFiveTests )
 
#include "tst_FiveAndFiveTests.moc"
 
Записан
8Observer8
Гость
« Ответ #173 : Апрель 12, 2014, 00:03 »

Сделай те уже функции для перевода числа в строку и наоборот. Улыбающийся
С шаблонами можно сделать две универсальные функции для любых чисел.

Нашёл такой способ перевода из числа в строку:
Код
C++ (Qt)
#include <QCoreApplication>
#include <string>
#include <iostream>
#include "boost/lexical_cast.hpp"
 
int main( int argc, char *argv[] ) {
   QCoreApplication a( argc, argv );
 
   int i = 5;
   std::string s = boost::lexical_cast<std::string>(i);
   std::cout << s << std::endl;
 
   double d = 5.5;
   s = boost::lexical_cast<std::string>(d);
   std::cout << s << std::endl;
 
   return a.exec( );
}
 

Исходники: https://github.com/8Observer8/NumberToString
Записан
8Observer8
Гость
« Ответ #174 : Июнь 26, 2014, 07:29 »

Код
C++ (Qt)
#include <vector>
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;
 
int sumOfMaxAndMin(const vector<int>& arr) {
   int sum = 0;
 
   return sum;
}
 
int main(int argc, char** argv) {
   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;
   }
 
   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;
   }
 
   vector<int> arr;
   int value;
   string input;
   stringstream stream;
   while (in >> input) {
       stream << input;
       if (stream >> value) {
           cout << value << endl;
       } else {
           cerr << "Error: incorrect data in the file " << inFileName.c_str() << endl;
           in.close();
           out.close();
           return 1;
       }
   }
 
   int sum = sumOfMaxAndMin(arr);
   out << sum << endl;
 
   in.close();
   out.close();
   return 0;
}
 

В базовых вещах "плаваете" капитально, опыта/кладки почти нет. Ну не пишет программист 3 раза in.close(), не валит все в main и.т.д.

Код
C++ (Qt)
int main(int argc, char** argv)
{
 std::vector <int> vec;
 
// получаем имя файла из командной строки, если там пусто, то input.txt (по умолчанию)
 std::string iFileName = GetInName(argc, argv);
 
// читаем данные из файла
 int err = ReadData(iFileName, vec);
 if (err) return ShowError(err, &iFileName);
 
// выполняем содержательную часть
 int result;
 err = sumOfMaxAndMin(arr, &result);
 if (err) return ShowError(err, &iFileName);
 
// записываем выходной файл
 std::string oFileName = GetOutName(argc, argv);
 err = WriteResult(oFileName, result);
 if (err) return ShowError(err, &oFileName);
 
 return 0;
}
Теперь начинаем парить ReadData и др ф-ции, там тоже выделяем ф-ции, их будет уже меньше - и так до тех пор пока не напишем все. Когда Вы все это сделаете (аккуратно, с любовью а не абы как) - Вы с удивлением обнаружите что in.close() не понадобился ни разу  Улыбающийся

Благодаря этой теме, я нашёл способ оформления и разработки программ. Теперь функция main() не содержит ничего лишнего. Все файловые ошибки я наследую от класса "FileError", поэтому могу написать:

Код:
...
} catch ( const FileError &e) {
        std::cerr << e.what( ) << std::endl;
        return 1;
...

Проект "TextFile"

Исходники примера "TextFile": https://github.com/8Observer8/Qt_TextFile

Описание:
В этом примере целые числа читаются из файла "input.txt" и выводятся файл "output.txt". Числа не должны выходить из диапазона [-100, 100]. В противном случае, пользователю будет выведено сообщение:
Цитировать
Error: the argument "101" doesn't hit into the range [-100, 100]

Пример демонстрирует, как тестировать свободные функции с помощью макроса "ASSERT_EQ( expected, actual )", и исключения с помощью макросов: ASSERT_THROW и ASSERT_NO_THROW.

Пример выполнен по методологии TDD. То есть, к примеру:
- сначала создавалась заглушка разрабатываемой функции
- потом, описывалась функциональность будущей функции с помощью тестов на GTest
- далее, реализовывалась функциональность и перезапускались тесты, до полного их выполнения

Краткое пояснение к этому проекту:

main.cpp
Код
C++ (Qt)
#include <iostream>
#include <string>
#include <vector>
#include <QString>
#include "freeFunctions.h"
#include "LogicError.h"
#include "FileError.h"
 
int main( )
{
   // Read data from the input file
   QString fileNameIn = "input.txt";
   QString content;
 
   try {
       readData( fileNameIn, content );
   } 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;
   }
 
   // Parse the content to the integer array
   std::vector<int> arr;
   try {
       parseToIntArray( content, arr );
   } catch ( const LogicError &e ) {
       std::cerr << e.what( ) << std::endl;
       return 1;
   } catch ( ... ) {
       std::cerr << "Error: unknown exception" << std::endl;
       return 1;
   }
 
   // Write data to the output file
   QString fileNameOut = "output.txt";
   try {
       writeData( fileNameOut, arr );
   } 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;
}
 

Функция "void readData( const QString &fileName, QString &content )" читает данные из файла с именем "fileName" и помещает их в строковый объект "content".

Функция "void parseToIntArray( const QString &source, std::vector<int> &destination )" парсит данные из строкового объекта "content" в массив целых чисел.

Массив выводится в файл "output.txt" с помощью функции: void writeData( const QString &fileName, const std::vector<int> &arr )

Эти функции выбрасывают исключения. Вот некоторые из них:
- FileOpenError - "Ошибка открытия файла". К примеру, это исключение может выдать такой текст пользователю: Error: unable to open the file "input.txt" in the function "readData()"
- FileReadError - "Ошибка чтения из файла"
- EmptyArgument - "Входной аргумент представляет из себя пустой аргумент"

В корневой папке этого проекта находится папка "GTests", в которой хранятся тестовые проекты на Qt для всех классов и свободных функций, которые можно было протестировать в этом проекте. Вот список этих тестовых проектов:
- TextFile_EmptyArgument_gtests
- TextFile_FileOpenError_gtests
- TextFile_FileReadError_gtests
- TextFile_FileWriteError_gtest
- TextFile_isAllDigits_gtests
- TextFile_NotNumber_gtests
- TextFile_OutOfRange_gtests
- TextFile_parseToIntArray_gtests

Все детали вы увидите, когда просмотрите сам проект "TextFile" и проекты с тестами его модулей из папки GTests: https://github.com/8Observer8/Qt_TextFile
« Последнее редактирование: Июнь 26, 2014, 07:55 от 8Observer8 » Записан
8Observer8
Гость
« Ответ #175 : Июнь 26, 2014, 09:02 »

В шапку темы добавил текст:

Внимание! Эта тема перетекла, в следующую тему, в шапку которой я буду добавлять изменения/добавления. Вам достаточно будет подписаться на неё. О всех измененияx/добавленияx я буду извещать: http://www.prog.org.ru/topic_26944_0.html
Записан
Страниц: 1 ... 10 11 [12]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.887 секунд. Запросов: 23.