From ea42948c42618398fc33049acb01be7e27ffbbe8 Mon Sep 17 00:00:00 2001 From: ado Date: Sat, 13 Feb 2021 01:14:25 +0100 Subject: [PATCH] add string_error and multiline within the setup, remove set_error_mode, update unit tests, update documentation --- README.md | 15 ++++--- include/ss/converter.hpp | 75 ++++++++++++++++--------------- include/ss/parser.hpp | 70 ++++++++++++++++------------- include/ss/setup.hpp | 27 ++++++++++-- include/ss/splitter.hpp | 90 +++++++++++++++++++------------------- include/ss/type_traits.hpp | 23 +++++++++- test/test_converter.cpp | 15 +++---- test/test_parser.cpp | 31 ++++++------- test/test_splitter.cpp | 24 ++++------ 9 files changed, 208 insertions(+), 162 deletions(-) diff --git a/README.md b/README.md index d9501bb..0789b95 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,6 @@ Bill (Heath) Gates,65,3.3 int main() { ss::parser p{"students.csv", ","}; if (!p.valid()) { - std::cout << p.error_msg() << std::endl; exit(EXIT_FAILURE); } @@ -232,14 +231,16 @@ Not yet documented. ## Error handling -Detailed error messages can be accessed via the **error_msg** method, and to enable them the error mode has to be changed to **error_mode::error_string** using the **set_error_mode** method: +Detailed error messages can be accessed via the **error_msg** method, and to enable them ss::string_error needs to be included in the setup. ```cpp -void parser::set_error_mode(ss::error_mode); const std::string& parser::error_msg(); bool parser::valid(); bool parser::eof(); + +// ... +ss::parser parser; ``` -Error messages can always be disabled by setting the error mode to **error_mode::error_bool**. An error can be detected using the **valid** method which would return **false** if the file could not be opened, or if the conversion could not be made (invalid types, invalid number of columns, ...). The **eof** method can be used to detect if the end of the file was reached. +An error can be detected using the **valid** method which would return **false** if the file could not be opened, or if the conversion could not be made (invalid types, invalid number of columns, ...). The **eof** method can be used to detect if the end of the file was reached. ## Substitute conversions @@ -340,7 +341,7 @@ p.try_next, udbl>( } }); ``` -It is a bit less readable, but it removes the need to check which conversion was invoked. The **composite** also has an **on_error** method which accepts a lambda which will be invoked if no previous conversions were successful. The lambda can take no arguments or just one argument, an **std::string**, in which the error message is stored if **error_mode** is set to **error_mode::error_string**: +It is a bit less readable, but it removes the need to check which conversion was invoked. The **composite** also has an **on_error** method which accepts a lambda which will be invoked if no previous conversions were successful. The lambda can take no arguments or just one argument, an **std::string**, in which the error message is stored if **string_error** is enabled: ```cpp p.try_next() .on_error([](const std::string& e) { /* int conversion failed */ }) @@ -371,8 +372,8 @@ if (c.valid()) { // do something with s } ``` -All special types and restrictions work on the converter too. Error handling is -also identical to error handling of the parser. +All setup parameters, special types and restrictions work on the converter too. +Error handling is also identical to error handling of the parser. The converter has also the ability to just split the line, tho it does not change it (kinda statically), hence the name of the library. It returns an **std::vector** of pairs of pointers, begin and end, each pair representing a split segment (column) of the whole string. The vector can then be used in a overloaded **convert** method. This allows the reuse of the same line without splitting it on every conversion. ```cpp diff --git a/include/ss/converter.hpp b/include/ss/converter.hpp index e73c5db..0a0b837 100644 --- a/include/ss/converter.hpp +++ b/include/ss/converter.hpp @@ -109,9 +109,13 @@ constexpr bool tied_class_v = tied_class::value; template class converter { - constexpr static auto default_delimiter = ","; using line_ptr_type = typename splitter::line_ptr_type; + constexpr static auto string_error = setup::string_error; + constexpr static auto default_delimiter = ","; + + using error_type = ss::ternary_t; + public: // parses line with given delimiter, returns a 'T' object created with // extracted values of type 'Ts' @@ -173,23 +177,23 @@ public: } bool valid() const { - return (error_mode_ == error_mode::error_string) ? string_error_.empty() - : bool_error_ == false; + if constexpr (string_error) { + return error_.empty(); + } else { + return !error_; + } + } + + const std::string& error_msg() const { + static_assert(string_error, + "'string_error' needs to be enabled to use 'error_msg'"); + return error_; } bool unterminated_quote() const { return splitter_.unterminated_quote(); } - const std::string& error_msg() const { - return string_error_; - } - - void set_error_mode(error_mode mode) { - splitter_.set_error_mode(mode); - error_mode_ = mode; - } - // 'splits' string by given delimiter, returns vector of pairs which // contain the beginnings and the ends of each column of the string const split_input& split(line_ptr_type line, @@ -203,7 +207,6 @@ public: } private: - //////////////// // resplit //////////////// @@ -218,8 +221,11 @@ private: //////////////// void clear_error() { - string_error_.clear(); - bool_error_ = false; + if constexpr (string_error) { + error_.clear(); + } else { + error_ = false; + } } std::string error_sufix(const string_range msg, size_t pos) const { @@ -234,44 +240,43 @@ private: } void set_error_unterminated_quote() { - if (error_mode_ == error_mode::error_string) { - string_error_.clear(); - string_error_.append(splitter_.error_msg()); + if constexpr (string_error) { + error_.clear(); + error_.append(splitter_.error_msg()); } else { - bool_error_ = true; + error_ = true; } } void set_error_invalid_conversion(const string_range msg, size_t pos) { - if (error_mode_ == error_mode::error_string) { - string_error_.clear(); - string_error_.append("invalid conversion for parameter ") + if constexpr (string_error) { + error_.clear(); + error_.append("invalid conversion for parameter ") .append(error_sufix(msg, pos)); } else { - bool_error_ = true; + error_ = true; } } void set_error_validate(const char* const error, const string_range msg, size_t pos) { - if (error_mode_ == error_mode::error_string) { - string_error_.clear(); - string_error_.append(error).append(" ").append( - error_sufix(msg, pos)); + if constexpr (string_error) { + error_.clear(); + error_.append(error).append(" ").append(error_sufix(msg, pos)); } else { - bool_error_ = true; + error_ = true; } } void set_error_number_of_colums(size_t expected_pos, size_t pos) { - if (error_mode_ == error_mode::error_string) { - string_error_.clear(); - string_error_.append("invalid number of columns, expected: ") + if constexpr (string_error) { + error_.clear(); + error_.append("invalid number of columns, expected: ") .append(std::to_string(expected_pos)) .append(", got: ") .append(std::to_string(pos)); } else { - bool_error_ = true; + error_ = true; } } @@ -374,12 +379,10 @@ private: // members //////////////// - std::string string_error_; - bool bool_error_; - enum error_mode error_mode_ { error_mode::error_bool }; + error_type error_; splitter splitter_; - template + template friend class parser; }; diff --git a/include/ss/parser.hpp b/include/ss/parser.hpp index 21811a1..af740b1 100644 --- a/include/ss/parser.hpp +++ b/include/ss/parser.hpp @@ -15,6 +15,11 @@ template class parser { struct none {}; + constexpr static auto string_error = setup::string_error; + constexpr static auto multiline = setup::multiline; + + using error_type = ss::ternary_t; + public: parser(const std::string& file_name, const std::string& delim = ss::default_delimiter) @@ -35,17 +40,17 @@ public: parser& operator=(const parser& other) = delete; bool valid() const { - return (error_mode_ == error_mode::error_string) ? string_error_.empty() - : bool_error_ == false; - } - - void set_error_mode(error_mode mode) { - error_mode_ = mode; - reader_.set_error_mode(mode); + if constexpr (string_error) { + return error_.empty(); + } else { + return !error_; + } } const std::string& error_msg() const { - return string_error_; + static_assert(string_error, + "'string_error' needs to be enabled to use 'error_msg'"); + return error_; } bool eof() const { @@ -124,6 +129,10 @@ public: if constexpr (std::is_invocable_v) { fun(); } else { + static_assert(string_error, + "to enable error messages within the " + "on_error method " + "callback string_error needs to be enabled"); std::invoke(std::forward(fun), parser_.error_msg()); } } @@ -246,34 +255,40 @@ private: //////////////// void clear_error() { - string_error_.clear(); - bool_error_ = false; + if constexpr (string_error) { + error_.clear(); + } else { + error_ = false; + } } void set_error_failed_check() { - if (error_mode_ == error_mode::error_string) { - string_error_.append(file_name_).append(" failed check."); + if constexpr (string_error) { + error_.append(file_name_).append(" failed check."); } else { - bool_error_ = true; + error_ = true; } } void set_error_file_not_open() { - string_error_.append(file_name_).append(" could not be opened."); - bool_error_ = true; + if constexpr (string_error) { + error_.append(file_name_).append(" could not be opened."); + } else { + error_ = true; + } } void set_error_eof_reached() { - if (error_mode_ == error_mode::error_string) { - string_error_.append(file_name_).append(" reached end of file."); + if constexpr (string_error) { + error_.append(file_name_).append(" reached end of file."); } else { - bool_error_ = true; + error_ = true; } } void set_error_invalid_conversion() { - if (error_mode_ == error_mode::error_string) { - string_error_.append(file_name_) + if constexpr (string_error) { + error_.append(file_name_) .append(" ") .append(std::to_string(line_number_)) .append(": ") @@ -282,7 +297,7 @@ private: .append(reader_.buffer_) .append("\""); } else { - bool_error_ = true; + error_ = true; } } @@ -360,7 +375,7 @@ private: size_t size = remove_eol(next_line_buffer_, ssize); - if constexpr (setup::escape::enabled) { + if constexpr (multiline && setup::escape::enabled) { while (escaped_eol(size)) { if (!append_line(next_line_buffer_, size)) { return false; @@ -370,7 +385,7 @@ private: next_line_converter_.split(next_line_buffer_, delim_); - if constexpr (setup::quote::enabled) { + if constexpr (multiline && setup::quote::enabled) { while (unterminated_quote()) { if (!append_line(next_line_buffer_, size)) { return false; @@ -382,11 +397,6 @@ private: return true; } - void set_error_mode(error_mode mode) { - converter_.set_error_mode(mode); - next_line_converter_.set_error_mode(mode); - } - void update() { std::swap(buffer_, next_line_buffer_); std::swap(converter_, next_line_converter_); @@ -478,9 +488,7 @@ private: //////////////// std::string file_name_; - std::string string_error_; - bool bool_error_{false}; - error_mode error_mode_{error_mode::error_bool}; + error_type error_; reader reader_; size_t line_number_{0}; bool eof_{false}; diff --git a/include/ss/setup.hpp b/include/ss/setup.hpp index 9d1c02b..c773fca 100644 --- a/include/ss/setup.hpp +++ b/include/ss/setup.hpp @@ -79,9 +79,8 @@ struct get_matcher; template