Вы можете создать отдельный класс, который анализирует и обрабатывает связанные исключения. У меня нет опыта работы с бустом и функциональностью в вашем примере, поэтому мой не использует его - он все еще демонстрирует концепцию:
#include<iostream>
#include <fstream>
#include <exception>
#include <vector>
#include <string>
// Parses a file, collects data if correct,
// if not throws an exception
class Parser
{
public:
// Creates Parser object for parsing file file_name
// verbose indicates whether more detailed exception message
// should be printed
Parser(const std::string file_name, const bool verbose):
fname(file_name), print_ex_info(verbose) { }
// Parsing wrapper that calls actual parsing function
// and handles/prints exceptions
void parse();
// Retrieve parsed data
std::vector<std::string> get_data() const { return data; }
private:
std::vector<std::string> data;
std::string fname = {};
bool print_ex_info = true;
// Actual parsing
void parse_private();
};
void Parser::parse()
{
try{
parse_private();
} catch(const std::exception& ex) {
if (print_ex_info){
std::cout << "File " << fname
<< " thrown an exception "
<< ex.what() << std::endl;
}else{
std::cout << ex.what() << std::endl;
}
}
}
// Throws if file contains an entry that
// is not a positive integer
// (for simple demonstration)
void Parser::parse_private()
{
std::ifstream in(fname);
std::string entry;
while (in >> entry){
if (entry.find_first_not_of("0123456789") != std::string::npos){
throw std::runtime_error("Invalid entry " + entry + "\n");
}else{
data.push_back(entry);
}
}
}
// Retrieves and uses data parsed from a file
class Object
{
public:
void parse(const std::string file_name, const bool verbose)
{
Parser file_parser(file_name, verbose);
file_parser.parse();
parsed_data = file_parser.get_data();
}
void print_parsed_data()
{
for (const auto& entry : parsed_data)
std::cout << entry << " ";
std::cout << std::endl;
}
private:
std::vector<std::string> parsed_data;
};
int main()
{
Object obj;
bool verbose = true;
// Correct input case
std::cout << "No exception:\n";
obj.parse("parser_no_ex.txt", verbose);
obj.print_parsed_data();
std::cout << "\n";
// Invalid input, detailed exception info
std::cout << "Exception - verbose version:\n";
obj.parse("parser_invalid.txt", verbose);
// Invalid input, reduced exception info
std::cout << "Exception - minimal version:\n";
verbose = false;
obj.parse("parser_invalid.txt", verbose);
return 0;
}
Здесь Object
служит промежуточным классом, который извлекает и использует проанализированные данные,Проанализированные данные генерируются в объекте Parser
, который также выполняет проверку данных и генерирует исключения. Таким образом, код, который использует данные, не загроможден обработкой исключений, связанной с синтаксическим анализом - это относится как к коду, который использует Object
, так и к функциональности самого Object
.
По аналогичным причинам (беспорядок, удобочитаемость) Parser
имеет дополнительный уровень кода для синтаксического анализа - открытая функция parse()
имеет код обработки исключений и выполняет вызов фактической частной функции синтаксического анализа, которая не имеетобработка кода, но может бросить.
Кроме того, синтаксический анализ имеет опцию verbose
, которая управляет объемом информации, которую пользователь хочет видеть после возникновения исключения. Это неплохая идея, чтобы иметь возможность отключать более информативные исключения.
Для справки: это файлы, используемые в этой демонстрации,
Файл с правильным вводом (все положительные целые числа) - parser_no_ex.txt
:
123
456
900000
111
00
Файл с неправильным вводом - parser_invalid.txt
:
123
456789
***Hello***
345