From a06121ff92c745499228c17b41b2ca8b44f131f5 Mon Sep 17 00:00:00 2001 From: ado Date: Sat, 12 Dec 2020 23:32:06 +0100 Subject: [PATCH] add composite, add bool error handling --- include/ss/converter.hpp | 63 ++++++++++++---- include/ss/parser.hpp | 151 +++++++++++++++++++++++++++++++++------ 2 files changed, 177 insertions(+), 37 deletions(-) diff --git a/include/ss/converter.hpp b/include/ss/converter.hpp index 96da6bd..6bc5c35 100644 --- a/include/ss/converter.hpp +++ b/include/ss/converter.hpp @@ -99,16 +99,20 @@ struct tied_class { template constexpr bool tied_class_v = tied_class::value; +// the error can be set inside a string, or a bool +enum class error_mode { String, Bool }; + //////////////// // converter //////////////// class converter { using string_range = std::pair; - using split_input = std::vector; constexpr static auto default_delimiter = ','; public: + using split_input = std::vector; + // parses line with given delimiter, returns a 'T' object created with // extracted values of type 'Ts' template @@ -154,11 +158,17 @@ class converter { } bool valid() const { - return error_.empty(); + return (error_mode_ == error_mode::String) + ? string_error_.empty() + : bool_error_ == false; } - const std::string& error() const { - return error_; + const std::string& error_msg() const { + return string_error_; + } + + void set_error_mode(error_mode mode) { + error_mode_ = mode; } // 'splits' string by given delimiter, returns vector of pairs which @@ -185,6 +195,11 @@ class converter { // error //////////////// + void clear_error() { + string_error_.clear(); + bool_error_ = false; + } + std::string error_sufix(const string_range msg, size_t pos) const { std::string error; error.reserve(32); @@ -197,22 +212,38 @@ class converter { } void set_error_invalid_conversion(const string_range msg, size_t pos) { - error_.clear(); - error_.append("invalid conversion for parameter ") - .append(error_sufix(msg, pos)); + if (error_mode_ == error_mode::String) { + string_error_.clear(); + string_error_ + .append("invalid conversion for parameter ") + .append(error_sufix(msg, pos)); + } else { + bool_error_ = true; + } } void set_error_validate(const char* const error, const string_range msg, size_t pos) { - error_.clear(); - error_.append(error).append(" ").append(error_sufix(msg, pos)); + if (error_mode_ == error_mode::String) { + string_error_.clear(); + string_error_.append(error).append(" ").append( + error_sufix(msg, pos)); + } else { + bool_error_ = true; + } } void set_error_number_of_colums(size_t expected_pos, size_t pos) { - error_.append("invalid number of columns, expected: ") - .append(std::to_string(expected_pos)) - .append(", got: ") - .append(std::to_string(pos)); + if (error_mode_ == error_mode::String) { + string_error_.clear(); + string_error_ + .append("invalid number of columns, expected: ") + .append(std::to_string(expected_pos)) + .append(", got: ") + .append(std::to_string(pos)); + } else { + bool_error_ = true; + } } //////////////// @@ -221,7 +252,7 @@ class converter { template no_void_validator_tup_t convert_impl(const split_input& elems) { - error_.clear(); + clear_error(); no_void_validator_tup_t ret{}; if (sizeof...(Ts) != elems.size()) { set_error_number_of_colums(sizeof...(Ts), elems.size()); @@ -339,7 +370,9 @@ class converter { //////////////// std::vector input_; - std::string error_; + std::string string_error_; + bool bool_error_; + enum error_mode error_mode_ { error_mode::String }; }; template <> diff --git a/include/ss/parser.hpp b/include/ss/parser.hpp index ba3d300..ab9dfc7 100644 --- a/include/ss/parser.hpp +++ b/include/ss/parser.hpp @@ -4,12 +4,17 @@ #include "extract.hpp" #include "restrictions.hpp" #include +#include #include #include #include namespace ss { +struct None {}; +template +class composite; + class parser { public: parser(const std::string& file_name, const std::string& delimiter) @@ -28,7 +33,18 @@ class parser { } bool valid() const { - return error_.empty(); + return (error_mode_ == error_mode::String) + ? string_error_.empty() + : bool_error_ == false; + } + + void set_error_mode(error_mode mode) { + error_mode_ = mode; + converter_.set_error_mode(mode); + } + + const std::string& error_msg() const { + return string_error_; } bool eof() const { @@ -39,10 +55,6 @@ class parser { return buff_.read(file_); } - const std::string& error() const { - return error_; - } - template T get_struct() { return to_struct(get_next()); @@ -50,13 +62,15 @@ class parser { template no_void_validator_tup_t get_next() { - error_.clear(); + buff_.update(); + clear_error(); if (eof_) { set_error_eof_reached(); return {}; } - auto value = converter_.convert(buff_.get(), delim_); + split_input_ = converter_.split(buff_.get(), delim_); + auto value = converter_.convert(split_input_); if (!converter_.valid()) { set_error_invalid_conversion(); @@ -66,39 +80,110 @@ class parser { return value; } + //////////////// + // composite conversion + //////////////// + template + class composite { + public: + composite(std::tuple values, parser& parser) + : values_{values}, parser_{parser} { + } + + template + composite>> + or_else(Fun&& fun = None{}) { + std::optional> value; + + if (!parser_.valid()) { + auto new_value = try_same(); + if (parser_.valid()) { + value = new_value; + if constexpr (!std::is_same_v) { + fun(*value); + } + } + } + + auto values = + std::tuple_cat(std::move(values_), + std::tuple{std::move(value)}); + return {std::move(values), parser_}; + } + + private: + template + no_void_validator_tup_t try_same() { + parser_.clear_error(); + auto value = parser_.converter_.convert( + parser_.split_input_); + if (!parser_.converter_.valid()) { + parser_.set_error_invalid_conversion(); + } + return value; + } + + std::tuple values_; + parser& parser_; + }; + + template + composite>> try_next( + Fun&& fun = None{}) { + std::optional> value; + auto new_value = get_next(); + if (valid()) { + value = new_value; + if constexpr (!std::is_same_v) { + fun(*value); + } + } + return {value, *this}; + }; + private: + template + friend class composite; + //////////////// // line reading //////////////// class buffer { char* buffer_{nullptr}; + char* new_buffer_{nullptr}; size_t size_{0}; public: ~buffer() { free(buffer_); + free(new_buffer_); } bool read(FILE* file) { - ssize_t size = getline(&buffer_, &size_, file); + ssize_t size = getline(&new_buffer_, &size_, file); size_t string_end = size - 1; if (size == -1) { return false; } - if (size >= 2 && buffer_[size - 2] == '\r') { + if (size >= 2 && new_buffer_[size - 2] == '\r') { string_end--; } - buffer_[string_end] = '\0'; + new_buffer_[string_end] = '\0'; return true; } const char* get() const { return buffer_; } + + void update() { + std::swap(buffer_, new_buffer_); + } }; void read_line() { @@ -110,23 +195,42 @@ class parser { // error //////////////// + void clear_error() { + string_error_.clear(); + bool_error_ = false; + } + void set_error_file_not_open() { - error_.append(file_name_).append(" could not be not open."); + if (error_mode_ == error_mode::String) { + string_error_.append(file_name_) + .append(" could not be not open."); + } else { + bool_error_ = true; + } } void set_error_eof_reached() { - error_.append(file_name_).append(" reached end of file."); + if (error_mode_ == error_mode::String) { + string_error_.append(file_name_) + .append(" reached end of file."); + } else { + bool_error_ = true; + } } void set_error_invalid_conversion() { - error_.append(file_name_) - .append(" ") - .append(std::to_string(line_number_)) - .append(": ") - .append(converter_.error()) - .append(": \"") - .append(buff_.get()); - error_.append("\""); + if (error_mode_ == error_mode::String) { + string_error_.append(file_name_) + .append(" ") + .append(std::to_string(line_number_)) + .append(": ") + .append(converter_.error_msg()) + .append(": \"") + .append(buff_.get()) + .append("\""); + } else { + bool_error_ = true; + } } //////////////// @@ -135,8 +239,11 @@ class parser { const std::string file_name_; const std::string delim_; - std::string error_; - ss::converter converter_; + std::string string_error_; + bool bool_error_; + error_mode error_mode_{error_mode::String}; + converter converter_; + converter::split_input split_input_; FILE* file_{nullptr}; buffer buff_; size_t line_number_{0};