From e4062536943c6144166f882816023174f3d41fbb Mon Sep 17 00:00:00 2001 From: ado Date: Sat, 26 Dec 2020 00:46:42 +0100 Subject: [PATCH] make indent with 4 caracters instead of 8 --- include/ss/converter.hpp | 546 ++++++++++++++------------- include/ss/extract.hpp | 372 ++++++++++--------- include/ss/function_traits.hpp | 34 +- include/ss/parser.hpp | 618 +++++++++++++++---------------- include/ss/restrictions.hpp | 92 ++--- include/ss/type_traits.hpp | 118 +++--- test/test_converter.cpp | 496 +++++++++++++------------ test/test_extractions.cpp | 510 +++++++++++++------------- test/test_parser.cpp | 648 ++++++++++++++++----------------- 9 files changed, 1688 insertions(+), 1746 deletions(-) diff --git a/include/ss/converter.hpp b/include/ss/converter.hpp index 08c2e37..f18ed29 100644 --- a/include/ss/converter.hpp +++ b/include/ss/converter.hpp @@ -28,12 +28,12 @@ struct no_validator; template struct no_validator>> { - using type = typename member_wrapper::arg_type; + using type = typename member_wrapper::arg_type; }; template struct no_validator { - using type = T; + using type = T; }; template @@ -41,18 +41,17 @@ using no_validator_t = typename no_validator::type; template struct no_validator_tup { - using type = - typename apply_trait>::type; + using type = typename apply_trait>::type; }; template struct no_validator_tup> { - using type = typename no_validator_tup::type; + using type = typename no_validator_tup::type; }; template struct no_validator_tup> { - using type = no_validator_t; + using type = no_validator_t; }; template @@ -64,8 +63,8 @@ using no_validator_tup_t = typename no_validator_tup::type; template struct no_void_tup { - using type = - typename filter_not>::type; + using type = + typename filter_not>::type; }; template @@ -78,12 +77,12 @@ using no_void_tup_t = filter_not_t; // replace 'validators' and remove void from tuple template struct no_void_validator_tup { - using type = no_validator_tup_t>; + using type = no_validator_tup_t>; }; template struct no_void_validator_tup> { - using type = no_validator_tup_t>; + using type = no_validator_tup_t>; }; template @@ -97,8 +96,8 @@ using no_void_validator_tup_t = typename no_void_validator_tup::type; // the 'tied' method which is to be used for type deduction when converting template struct tied_class { - constexpr static bool value = - (sizeof...(Ts) == 0 && std::is_class_v && has_m_tied::value); + constexpr static bool value = + (sizeof...(Ts) == 0 && std::is_class_v && has_m_tied::value); }; template @@ -112,286 +111,277 @@ enum class error_mode { String, Bool }; //////////////// class converter { - using string_range = std::pair; - constexpr static auto default_delimiter = ','; + using string_range = std::pair; + constexpr static auto default_delimiter = ','; - public: - using split_input = std::vector; +public: + using split_input = std::vector; - // parses line with given delimiter, returns a 'T' object created with - // extracted values of type 'Ts' - template - T convert_object(const char* const line, - const std::string& delim = "") { - return to_object(convert(line, delim)); + // parses line with given delimiter, returns a 'T' object created with + // extracted values of type 'Ts' + template + T convert_object(const char* const line, const std::string& delim = "") { + return to_object(convert(line, delim)); + } + + // parses line with given delimiter, returns tuple of objects with + // extracted values of type 'Ts' + template + no_void_validator_tup_t convert(const char* const line, + const std::string& delim = "") { + input_ = split(line, delim); + return convert(input_); + } + + // parses already split line, returns 'T' object with extracted values + template + T convert_object(const split_input& elems) { + return to_object(convert(elems)); + } + + // parses already split line, returns either a tuple of objects with + // parsed values (returns raw element (no tuple) if Ts is empty), or if + // one argument is given which is a class which has a tied + // method which returns a tuple, returns that type + template + no_void_validator_tup_t convert(const split_input& elems) { + if constexpr (tied_class_v) { + using arg_ref_tuple = + typename std::result_of_t; + + using arg_tuple = + typename apply_trait::type; + + return to_object(convert_impl(elems, (arg_tuple*){})); + } else if constexpr (sizeof...(Ts) == 0 && + is_instance_of::value) { + return convert_impl(elems, (T*){}); + + } else { + return convert_impl(elems); + } + } + + bool valid() const { + return (error_mode_ == error_mode::String) ? string_error_.empty() + : bool_error_ == false; + } + + 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 + // contain the beginings and the ends of each column of the string + const split_input& split(const char* const line, + const std::string& delim = "") { + input_.clear(); + if (line[0] == '\0') { + return input_; } - // parses line with given delimiter, returns tuple of objects with - // extracted values of type 'Ts' - template - no_void_validator_tup_t convert(const char* const line, - const std::string& delim = "") { - input_ = split(line, delim); - return convert(input_); - } - - // parses already split line, returns 'T' object with extracted values - template - T convert_object(const split_input& elems) { - return to_object(convert(elems)); - } - - // parses already split line, returns either a tuple of objects with - // parsed values (returns raw element (no tuple) if Ts is empty), or if - // one argument is given which is a class which has a tied - // method which returns a tuple, returns that type - template - no_void_validator_tup_t convert(const split_input& elems) { - if constexpr (tied_class_v) { - using arg_ref_tuple = - typename std::result_of_t; - - using arg_tuple = - typename apply_trait::type; - - return to_object( - convert_impl(elems, (arg_tuple*){})); - } else if constexpr (sizeof...(Ts) == 0 && - is_instance_of::value) { - return convert_impl(elems, (T*){}); - - } else { - return convert_impl(elems); - } - } - - bool valid() const { - return (error_mode_ == error_mode::String) - ? string_error_.empty() - : bool_error_ == false; - } - - 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 - // contain the beginings and the ends of each column of the string - const split_input& split(const char* const line, - const std::string& delim = "") { - input_.clear(); - if (line[0] == '\0') { - return input_; - } - - switch (delim.size()) { - case 0: - return split_impl(line, ','); - case 1: - return split_impl(line, delim[0]); - default: - return split_impl(line, delim, delim.size()); - }; - } - - private: - //////////////// - // 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); - error.append("at column ") - .append(std::to_string(pos + 1)) - .append(": \'") - .append(msg.first, msg.second) - .append("\'"); - return error; - } - - void set_error_invalid_conversion(const string_range msg, size_t 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) { - 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) { - 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; - } - } - - //////////////// - // convert implementation - //////////////// - - template - no_void_validator_tup_t convert_impl(const split_input& elems) { - clear_error(); - no_void_validator_tup_t ret{}; - if (sizeof...(Ts) != elems.size()) { - set_error_number_of_colums(sizeof...(Ts), elems.size()); - return ret; - } - return extract_tuple(elems); - } - - // do not know how to specialize by return type :( - template - no_void_validator_tup_t> convert_impl( - const split_input& elems, const std::tuple*) { - return convert_impl(elems); - } - - //////////////// - // substring - //////////////// - - template - const split_input& split_impl(const char* const line, Delim delim, - size_t delim_size = 1) { - auto range = substring(line, delim); - input_.push_back(range); - while (range.second[0] != '\0') { - range = substring(range.second + delim_size, delim); - input_.push_back(range); - } - return input_; - } - - bool no_match(const char* end, char delim) const { - return *end != delim; - } - - bool no_match(const char* end, const std::string& delim) const { - return strncmp(end, delim.c_str(), delim.size()) != 0; - } - - template - string_range substring(const char* const begin, Delim delim) const { - const char* end; - for (end = begin; *end != '\0' && no_match(end, delim); ++end) - ; - - return string_range{begin, end}; - } - - //////////////// - // conversion - //////////////// - - template - void extract_one(no_validator_t& dst, const string_range msg, - size_t pos) { - if (!valid()) { - return; - } - - if (!extract(msg.first, msg.second, dst)) { - set_error_invalid_conversion(msg, pos); - return; - } - - if constexpr (has_m_ss_valid_t) { - if (T validator; !validator.ss_valid(dst)) { - if constexpr (has_m_error_t) { - set_error_validate(validator.error(), - msg, pos); - } else { - set_error_validate("validation error", - msg, pos); - } - return; - } - } - } - - template - void extract_multiple(no_void_validator_tup_t& tup, - const split_input& elems) { - using elem_t = std::tuple_element_t>; - - constexpr bool not_void = !std::is_void_v; - constexpr bool one_element = - count_not::size == 1; - - if constexpr (not_void) { - if constexpr (one_element) { - extract_one(tup, elems[ArgN], ArgN); - } else { - auto& el = std::get(tup); - extract_one(el, elems[ArgN], ArgN); - } - } - - if constexpr (sizeof...(Ts) > ArgN + 1) { - constexpr size_t NewTupN = (not_void) ? TupN + 1 : TupN; - extract_multiple(tup, elems); - } - } - - template - no_void_validator_tup_t extract_tuple(const split_input& elems) { - static_assert(!all_of::value, - "at least one parameter must be non void"); - no_void_validator_tup_t ret; - extract_multiple<0, 0, Ts...>(ret, elems); - return ret; + switch (delim.size()) { + case 0: + return split_impl(line, ','); + case 1: + return split_impl(line, delim[0]); + default: + return split_impl(line, delim, delim.size()); }; + } - //////////////// - // members - //////////////// +private: + //////////////// + // error + //////////////// - std::vector input_; - std::string string_error_; - bool bool_error_; - enum error_mode error_mode_ { error_mode::String }; + 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); + error.append("at column ") + .append(std::to_string(pos + 1)) + .append(": \'") + .append(msg.first, msg.second) + .append("\'"); + return error; + } + + void set_error_invalid_conversion(const string_range msg, size_t 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) { + 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) { + 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; + } + } + + //////////////// + // convert implementation + //////////////// + + template + no_void_validator_tup_t convert_impl(const split_input& elems) { + clear_error(); + no_void_validator_tup_t ret{}; + if (sizeof...(Ts) != elems.size()) { + set_error_number_of_colums(sizeof...(Ts), elems.size()); + return ret; + } + return extract_tuple(elems); + } + + // do not know how to specialize by return type :( + template + no_void_validator_tup_t> convert_impl( + const split_input& elems, const std::tuple*) { + return convert_impl(elems); + } + + //////////////// + // substring + //////////////// + + template + const split_input& split_impl(const char* const line, Delim delim, + size_t delim_size = 1) { + auto range = substring(line, delim); + input_.push_back(range); + while (range.second[0] != '\0') { + range = substring(range.second + delim_size, delim); + input_.push_back(range); + } + return input_; + } + + bool no_match(const char* end, char delim) const { + return *end != delim; + } + + bool no_match(const char* end, const std::string& delim) const { + return strncmp(end, delim.c_str(), delim.size()) != 0; + } + + template + string_range substring(const char* const begin, Delim delim) const { + const char* end; + for (end = begin; *end != '\0' && no_match(end, delim); ++end) + ; + + return string_range{begin, end}; + } + + //////////////// + // conversion + //////////////// + + template + void extract_one(no_validator_t& dst, const string_range msg, + size_t pos) { + if (!valid()) { + return; + } + + if (!extract(msg.first, msg.second, dst)) { + set_error_invalid_conversion(msg, pos); + return; + } + + if constexpr (has_m_ss_valid_t) { + if (T validator; !validator.ss_valid(dst)) { + if constexpr (has_m_error_t) { + set_error_validate(validator.error(), msg, pos); + } else { + set_error_validate("validation error", msg, pos); + } + return; + } + } + } + + template + void extract_multiple(no_void_validator_tup_t& tup, + const split_input& elems) { + using elem_t = std::tuple_element_t>; + + constexpr bool not_void = !std::is_void_v; + constexpr bool one_element = count_not::size == 1; + + if constexpr (not_void) { + if constexpr (one_element) { + extract_one(tup, elems[ArgN], ArgN); + } else { + auto& el = std::get(tup); + extract_one(el, elems[ArgN], ArgN); + } + } + + if constexpr (sizeof...(Ts) > ArgN + 1) { + constexpr size_t NewTupN = (not_void) ? TupN + 1 : TupN; + extract_multiple(tup, elems); + } + } + + template + no_void_validator_tup_t extract_tuple(const split_input& elems) { + static_assert(!all_of::value, + "at least one parameter must be non void"); + no_void_validator_tup_t ret; + extract_multiple<0, 0, Ts...>(ret, elems); + return ret; + }; + + //////////////// + // members + //////////////// + + std::vector input_; + std::string string_error_; + bool bool_error_; + enum error_mode error_mode_ { error_mode::String }; }; template <> inline void converter::extract_one(std::string& dst, const string_range msg, size_t) { - if (!valid()) { - return; - } + if (!valid()) { + return; + } - extract(msg.first, msg.second, dst); + extract(msg.first, msg.second, dst); } } /* ss */ diff --git a/include/ss/extract.hpp b/include/ss/extract.hpp index 966f37b..044c7c5 100644 --- a/include/ss/extract.hpp +++ b/include/ss/extract.hpp @@ -20,108 +20,108 @@ namespace ss { //////////////// template std::enable_if_t, T> pow10(int n) { - T ret = 1.0; - T r = 10.0; - if (n < 0) { - n = -n; - r = 0.1; - } + T ret = 1.0; + T r = 10.0; + if (n < 0) { + n = -n; + r = 0.1; + } - while (n) { - if (n & 1) { - ret *= r; - } - r *= r; - n >>= 1; + while (n) { + if (n & 1) { + ret *= r; } - return ret; + r *= r; + n >>= 1; + } + return ret; } template std::enable_if_t, std::optional> to_num( const char* begin, const char* const end) { - if (begin == end) { - return std::nullopt; - } - int sign = 1; - T int_part = 0.0; - T frac_part = 0.0; - bool has_frac = false; - bool has_exp = false; + if (begin == end) { + return std::nullopt; + } + int sign = 1; + T int_part = 0.0; + T frac_part = 0.0; + bool has_frac = false; + bool has_exp = false; - // +/- sign - if (*begin == '-') { - ++begin; - sign = -1; + // +/- sign + if (*begin == '-') { + ++begin; + sign = -1; + } + + while (begin != end) { + if (*begin >= '0' && *begin <= '9') { + int_part = int_part * 10 + (*begin - '0'); + } else if (*begin == '.') { + has_frac = true; + ++begin; + break; + } else if (*begin == 'e') { + has_exp = true; + ++begin; + break; + } else { + return std::nullopt; } + ++begin; + } + + if (has_frac) { + T frac_exp = 0.1; while (begin != end) { - if (*begin >= '0' && *begin <= '9') { - int_part = int_part * 10 + (*begin - '0'); - } else if (*begin == '.') { - has_frac = true; - ++begin; - break; - } else if (*begin == 'e') { - has_exp = true; - ++begin; - break; - } else { - return std::nullopt; - } + if (*begin >= '0' && *begin <= '9') { + frac_part += frac_exp * (*begin - '0'); + frac_exp *= 0.1; + } else if (*begin == 'e') { + has_exp = true; ++begin; - } - - if (has_frac) { - T frac_exp = 0.1; - - while (begin != end) { - if (*begin >= '0' && *begin <= '9') { - frac_part += frac_exp * (*begin - '0'); - frac_exp *= 0.1; - } else if (*begin == 'e') { - has_exp = true; - ++begin; - break; - } else { - return std::nullopt; - } - ++begin; - } - } - - // parsing exponent part - T exp_part = 1.0; - if (begin != end && has_exp) { - int exp_sign = 1; - if (*begin == '-') { - exp_sign = -1; - ++begin; - } else if (*begin == '+') { - ++begin; - } - - int e = 0; - while (begin != end && *begin >= '0' && *begin <= '9') { - e = e * 10 + *begin - '0'; - ++begin; - } - - exp_part = pow10(exp_sign * e); - } - - if (begin != end) { + break; + } else { return std::nullopt; + } + ++begin; + } + } + + // parsing exponent part + T exp_part = 1.0; + if (begin != end && has_exp) { + int exp_sign = 1; + if (*begin == '-') { + exp_sign = -1; + ++begin; + } else if (*begin == '+') { + ++begin; } - return sign * (int_part + frac_part) * exp_part; + int e = 0; + while (begin != end && *begin >= '0' && *begin <= '9') { + e = e * 10 + *begin - '0'; + ++begin; + } + + exp_part = pow10(exp_sign * e); + } + + if (begin != end) { + return std::nullopt; + } + + return sign * (int_part + frac_part) * exp_part; } inline std::optional from_char(char c) { - if (c >= '0' && c <= '9') { - return c - '0'; - } - return std::nullopt; + if (c >= '0' && c <= '9') { + return c - '0'; + } + return std::nullopt; } #if defined(__clang__) || defined(__GNUC__) || defined(__GUNG__) @@ -130,38 +130,38 @@ inline std::optional from_char(char c) { //////////////// template bool mul_overflow(T& result, T operand) { - return __builtin_mul_overflow(result, operand, &result); + return __builtin_mul_overflow(result, operand, &result); } template <> inline bool mul_overflow(int& result, int operand) { - return __builtin_smul_overflow(result, operand, &result); + return __builtin_smul_overflow(result, operand, &result); } template <> inline bool mul_overflow(long& result, long operand) { - return __builtin_smull_overflow(result, operand, &result); + return __builtin_smull_overflow(result, operand, &result); } template <> inline bool mul_overflow(long long& result, long long operand) { - return __builtin_smulll_overflow(result, operand, &result); + return __builtin_smulll_overflow(result, operand, &result); } template <> inline bool mul_overflow(unsigned int& result, unsigned int operand) { - return __builtin_umul_overflow(result, operand, &result); + return __builtin_umul_overflow(result, operand, &result); } template <> inline bool mul_overflow(unsigned long& result, unsigned long operand) { - return __builtin_umull_overflow(result, operand, &result); + return __builtin_umull_overflow(result, operand, &result); } template <> inline bool mul_overflow(unsigned long long& result, unsigned long long operand) { - return __builtin_umulll_overflow(result, operand, &result); + return __builtin_umulll_overflow(result, operand, &result); } //////////////// @@ -170,38 +170,38 @@ inline bool mul_overflow(unsigned long long& result, template inline bool add_overflow(T& result, T operand) { - return __builtin_add_overflow(result, operand, &result); + return __builtin_add_overflow(result, operand, &result); } template <> inline bool add_overflow(int& result, int operand) { - return __builtin_sadd_overflow(result, operand, &result); + return __builtin_sadd_overflow(result, operand, &result); } template <> inline bool add_overflow(long& result, long operand) { - return __builtin_saddl_overflow(result, operand, &result); + return __builtin_saddl_overflow(result, operand, &result); } template <> inline bool add_overflow(long long& result, long long operand) { - return __builtin_saddll_overflow(result, operand, &result); + return __builtin_saddll_overflow(result, operand, &result); } template <> inline bool add_overflow(unsigned int& result, unsigned int operand) { - return __builtin_uadd_overflow(result, operand, &result); + return __builtin_uadd_overflow(result, operand, &result); } template <> inline bool add_overflow(unsigned long& result, unsigned long operand) { - return __builtin_uaddl_overflow(result, operand, &result); + return __builtin_uaddl_overflow(result, operand, &result); } template <> inline bool add_overflow(unsigned long long& result, unsigned long long operand) { - return __builtin_uaddll_overflow(result, operand, &result); + return __builtin_uaddll_overflow(result, operand, &result); } //////////////// @@ -209,62 +209,61 @@ inline bool add_overflow(unsigned long long& result, //////////////// template inline bool sub_overflow(T& result, T operand) { - return __builtin_sub_overflow(result, operand, &result); + return __builtin_sub_overflow(result, operand, &result); } template <> inline bool sub_overflow(int& result, int operand) { - return __builtin_ssub_overflow(result, operand, &result); + return __builtin_ssub_overflow(result, operand, &result); } template <> inline bool sub_overflow(long& result, long operand) { - return __builtin_ssubl_overflow(result, operand, &result); + return __builtin_ssubl_overflow(result, operand, &result); } template <> inline bool sub_overflow(long long& result, long long operand) { - return __builtin_ssubll_overflow(result, operand, &result); + return __builtin_ssubll_overflow(result, operand, &result); } template <> inline bool sub_overflow(unsigned int& result, unsigned int operand) { - return __builtin_usub_overflow(result, operand, &result); + return __builtin_usub_overflow(result, operand, &result); } template <> inline bool sub_overflow(unsigned long& result, unsigned long operand) { - return __builtin_usubl_overflow(result, operand, &result); + return __builtin_usubl_overflow(result, operand, &result); } template <> inline bool sub_overflow(unsigned long long& result, unsigned long long operand) { - return __builtin_usubll_overflow(result, operand, &result); + return __builtin_usubll_overflow(result, operand, &result); } template bool shift_and_add_overflow(T& value, T digit, F add_last_digit_owerflow) { - if (mul_overflow(value, 10) || - add_last_digit_owerflow(value, digit)) { - return true; - } - return false; + if (mul_overflow(value, 10) || add_last_digit_owerflow(value, digit)) { + return true; + } + return false; } #else #warning "use clang or gcc!!!" template bool shift_and_add_overflow(T& value, T digit, U is_negative) { - digit = (is_negative) ? -digit : digit; - T old_value = value; - value = 10 * value + digit; + digit = (is_negative) ? -digit : digit; + T old_value = value; + value = 10 * value + digit; - T expected_old_value = (value - digit) / 10; - if (old_value != expected_old_value) { - return true; - } - return false; + T expected_old_value = (value - digit) / 10; + if (old_value != expected_old_value) { + return true; + } + return false; } #endif @@ -272,35 +271,34 @@ bool shift_and_add_overflow(T& value, T digit, U is_negative) { template std::enable_if_t, std::optional> to_num( const char* begin, const char* end) { - if (begin == end) { - return std::nullopt; - } - bool is_negative = false; - if constexpr (std::is_signed_v) { - is_negative = *begin == '-'; - if (is_negative) { - ++begin; - } + if (begin == end) { + return std::nullopt; + } + bool is_negative = false; + if constexpr (std::is_signed_v) { + is_negative = *begin == '-'; + if (is_negative) { + ++begin; } + } #if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) - auto add_last_digit_owerflow = - (is_negative) ? sub_overflow : add_overflow; + auto add_last_digit_owerflow = + (is_negative) ? sub_overflow : add_overflow; #else - auto add_last_digit_owerflow = is_negative; + auto add_last_digit_owerflow = is_negative; #endif - T value = 0; - for (auto i = begin; i != end; ++i) { - if (auto digit = from_char(*i); - !digit || - shift_and_add_overflow(value, digit.value(), - add_last_digit_owerflow)) { - return std::nullopt; - } + T value = 0; + for (auto i = begin; i != end; ++i) { + if (auto digit = from_char(*i); + !digit || shift_and_add_overflow(value, digit.value(), + add_last_digit_owerflow)) { + return std::nullopt; } + } - return value; + return value; } //////////////// @@ -310,7 +308,7 @@ std::enable_if_t, std::optional> to_num( namespace error { template struct unsupported_type { - constexpr static bool value = false; + constexpr static bool value = false; }; } /* namespace */ @@ -320,51 +318,51 @@ std::enable_if_t && !std::is_floating_point_v && !is_instance_of::value, bool> extract(const char*, const char*, T&) { - static_assert(error::unsupported_type::value, - "Conversion for given type is not defined, an " - "\'extract\' function needs to be defined!"); + static_assert(error::unsupported_type::value, + "Conversion for given type is not defined, an " + "\'extract\' function needs to be defined!"); } template std::enable_if_t || std::is_floating_point_v, bool> extract(const char* begin, const char* end, T& value) { - auto optional_value = to_num(begin, end); - if (!optional_value) { - return false; - } - value = optional_value.value(); - return true; + auto optional_value = to_num(begin, end); + if (!optional_value) { + return false; + } + value = optional_value.value(); + return true; } template std::enable_if_t::value, bool> extract( const char* begin, const char* end, T& value) { - typename T::value_type raw_value; - if (extract(begin, end, raw_value)) { - value = raw_value; - } else { - value = std::nullopt; - } - return true; + typename T::value_type raw_value; + if (extract(begin, end, raw_value)) { + value = raw_value; + } else { + value = std::nullopt; + } + return true; } template bool extract_variant(const char* begin, const char* end, T& value) { - using IthType = std::variant_alternative_t>; - IthType ithValue; - if (extract(begin, end, ithValue)) { - value = ithValue; - return true; - } else if constexpr (I + 1 < std::variant_size_v) { - return extract_variant(begin, end, value); - } - return false; + using IthType = std::variant_alternative_t>; + IthType ithValue; + if (extract(begin, end, ithValue)) { + value = ithValue; + return true; + } else if constexpr (I + 1 < std::variant_size_v) { + return extract_variant(begin, end, value); + } + return false; } template std::enable_if_t::value, bool> extract( const char* begin, const char* end, T& value) { - return extract_variant(begin, end, value); + return extract_variant(begin, end, value); } //////////////// @@ -373,38 +371,38 @@ std::enable_if_t::value, bool> extract( template <> inline bool extract(const char* begin, const char* end, bool& value) { - if (end == begin + 1) { - if (*begin == '1') { - value = true; - } else if (*begin == '0') { - value = false; - } else { - return false; - } + if (end == begin + 1) { + if (*begin == '1') { + value = true; + } else if (*begin == '0') { + value = false; } else { - size_t size = end - begin; - if (size == 4 && strncmp(begin, "true", size) == 0) { - value = true; - } else if (size == 5 && strncmp(begin, "false", size) == 0) { - value = false; - } else { - return false; - } + return false; } + } else { + size_t size = end - begin; + if (size == 4 && strncmp(begin, "true", size) == 0) { + value = true; + } else if (size == 5 && strncmp(begin, "false", size) == 0) { + value = false; + } else { + return false; + } + } - return true; + return true; } template <> inline bool extract(const char* begin, const char* end, char& value) { - value = *begin; - return (end == begin + 1); + value = *begin; + return (end == begin + 1); } template <> inline bool extract(const char* begin, const char* end, std::string& value) { - value = std::string(begin, end); - return true; + value = std::string(begin, end); + return true; } } /* ss */ diff --git a/include/ss/function_traits.hpp b/include/ss/function_traits.hpp index f91b6a9..c22a936 100644 --- a/include/ss/function_traits.hpp +++ b/include/ss/function_traits.hpp @@ -12,13 +12,13 @@ namespace ss { template struct decayed_arg_n { - static_assert(N - 1 != sizeof...(Ts), "index out of range"); - using type = typename decayed_arg_n::type; + static_assert(N - 1 != sizeof...(Ts), "index out of range"); + using type = typename decayed_arg_n::type; }; template struct decayed_arg_n<0, T, Ts...> { - using type = std::decay_t; + using type = std::decay_t; }; template @@ -26,12 +26,12 @@ struct function_traits; template struct function_traits> { - using arg_type = Arg; + using arg_type = Arg; }; template struct function_traits { - using arg0 = typename decayed_arg_n<0, Ts...>::type; + using arg0 = typename decayed_arg_n<0, Ts...>::type; }; template @@ -54,7 +54,7 @@ struct member_wrapper; template struct member_wrapper { - using arg_type = typename function_traits::arg0; + using arg_type = typename function_traits::arg0; }; //////////////// @@ -62,19 +62,19 @@ struct member_wrapper { //////////////// #define INIT_HAS_METHOD(method) \ - template \ - class has_m_##method { \ - template \ - static std::true_type test(decltype(&C::method)); \ + template \ + class has_m_##method { \ + template \ + static std::true_type test(decltype(&C::method)); \ \ - template \ - static std::false_type test(...); \ + template \ + static std::false_type test(...); \ \ - public: \ - constexpr static bool value = decltype(test(0))::value; \ - }; \ + public: \ + constexpr static bool value = decltype(test(0))::value; \ + }; \ \ - template \ - constexpr bool has_m_##method##_t = has_m_##method::value; + template \ + constexpr bool has_m_##method##_t = has_m_##method::value; } /* trait */ diff --git a/include/ss/parser.hpp b/include/ss/parser.hpp index 1c985a3..25dab2b 100644 --- a/include/ss/parser.hpp +++ b/include/ss/parser.hpp @@ -16,344 +16,328 @@ template class composite; class parser { +public: + parser(const std::string& file_name, const std::string& delimiter) + : file_name_{file_name}, delim_{delimiter}, + file_{fopen(file_name_.c_str(), "rb")} { + if (file_) { + read_line(); + } else { + set_error_file_not_open(); + eof_ = true; + } + } + + ~parser() { + fclose(file_); + } + + bool valid() const { + 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 { + return eof_; + } + + bool ignore_next() { + return buff_.read(file_); + } + + template + T get_object() { + return to_object(get_next()); + } + + template + no_void_validator_tup_t get_next() { + buff_.update(); + clear_error(); + if (eof_) { + set_error_eof_reached(); + return {}; + } + + split_input_ = converter_.split(buff_.get(), delim_); + auto value = converter_.convert(split_input_); + + if (!converter_.valid()) { + set_error_invalid_conversion(); + } + + read_line(); + return value; + } + + //////////////// + // composite conversion + //////////////// + template + class composite { public: - parser(const std::string& file_name, const std::string& delimiter) - : file_name_{file_name}, delim_{delimiter}, - file_{fopen(file_name_.c_str(), "rb")} { - if (file_) { - read_line(); - } else { - set_error_file_not_open(); - eof_ = true; - } + composite(std::tuple&& values, parser& parser) + : values_{std::move(values)}, parser_{parser} { } - ~parser() { - fclose(file_); - } - - bool valid() const { - 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 { - return eof_; - } - - bool ignore_next() { - return buff_.read(file_); - } - - template - T get_object() { - return to_object(get_next()); - } - - template - no_void_validator_tup_t get_next() { - buff_.update(); - clear_error(); - if (eof_) { - set_error_eof_reached(); - return {}; - } - - split_input_ = converter_.split(buff_.get(), delim_); - auto value = converter_.convert(split_input_); - - if (!converter_.valid()) { - set_error_invalid_conversion(); - } - - read_line(); - return value; - } - - //////////////// - // composite conversion - //////////////// - template - class composite { - public: - composite(std::tuple&& values, parser& parser) - : values_{std::move(values)}, parser_{parser} { - } - - // tries to convert the same line with a different output type - // only if the previous conversion was not successful, - // returns composite containing itself and the new output - // as optional, additionally, if a parameter is passed, and - // that parameter can be invoked using the converted value, - // than it will be invoked in the case of a valid conversion - template - composite>> - or_else(Fun&& fun = None{}) { - using Value = no_void_validator_tup_t; - std::optional value; - try_convert_and_invoke(value, fun); - return composite_with(std::move(value)); - } - - // same as or_else, but saves the result into a 'U' object - // instead of a tuple - template - composite> or_else_object( - Fun&& fun = None{}) { - std::optional value; - try_convert_and_invoke(value, fun); - return composite_with(std::move(value)); - } - - std::tuple values() { - return values_; - } - - template - auto on_error(Fun&& fun) { - if (!parser_.valid()) { - if constexpr (std::is_invocable_v) { - fun(); - } else { - std::invoke(std::forward(fun), - parser_.error_msg()); - } - } - return *this; - } - - private: - template - composite composite_with(T&& new_value) { - auto merged_values = - std::tuple_cat(std::move(values_), - std::tuple{ - std::forward(new_value)}); - return {std::move(merged_values), parser_}; - } - - template - void try_convert_and_invoke(std::optional& value, - Fun&& fun) { - if (!parser_.valid()) { - std::optional new_value; - auto tuple_output = try_same(); - if constexpr (!std::is_same_v< - U, decltype(tuple_output)>) { - new_value = to_object( - std::move(tuple_output)); - } else { - new_value = std::move(tuple_output); - } - if (parser_.valid()) { - value = std::move(new_value); - parser_.try_invoke(*value, - std::forward( - fun)); - } - } - } - - 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_; - }; - - // tries to convert a line and returns a composite which is - // able to try additional conversions in case of failure - template - composite>> try_next( + // tries to convert the same line with a different output type + // only if the previous conversion was not successful, + // returns composite containing itself and the new output + // as optional, additionally, if a parameter is passed, and + // that parameter can be invoked using the converted value, + // than it will be invoked in the case of a valid conversion + template + composite>> or_else( Fun&& fun = None{}) { - std::optional> value; - auto new_value = get_next(); - if (valid()) { - value = std::move(new_value); - try_invoke(*value, std::forward(fun)); + using Value = no_void_validator_tup_t; + std::optional value; + try_convert_and_invoke(value, fun); + return composite_with(std::move(value)); + } + + // same as or_else, but saves the result into a 'U' object + // instead of a tuple + template + composite> or_else_object(Fun&& fun = None{}) { + std::optional value; + try_convert_and_invoke(value, fun); + return composite_with(std::move(value)); + } + + std::tuple values() { + return values_; + } + + template + auto on_error(Fun&& fun) { + if (!parser_.valid()) { + if constexpr (std::is_invocable_v) { + fun(); + } else { + std::invoke(std::forward(fun), parser_.error_msg()); } - return {std::move(value), *this}; - }; + } + return *this; + } private: - template - friend class composite; - - // tries to invoke the given function (see below), if the function - // returns a value which can be used as a conditional, and it returns - // false, the function sets an error, and allows the invoke of the - // next possible conversion as if the validation of the current one - // failed - template - void try_invoke(Arg&& arg, Fun&& fun) { - constexpr bool is_none = - std::is_same_v, None>; - if constexpr (!is_none) { - using Ret = decltype( - try_invoke_impl(arg, std::forward(fun))); - constexpr bool returns_void = std::is_same_v; - if constexpr (!returns_void) { - if (!try_invoke_impl(arg, - std::forward(fun))) { - set_error_failed_check(); - } - } else { - try_invoke_impl(arg, std::forward(fun)); - } - } + template + composite composite_with(T&& new_value) { + auto merged_values = + std::tuple_cat(std::move(values_), + std::tuple{std::forward(new_value)}); + return {std::move(merged_values), parser_}; } - // tries to invoke the function if not None - // it first tries to invoke the function without arguments, - // than with one argument if the function accepts the whole tuple - // as an argument, and finally tries to invoke it with the tuple - // laid out as a parameter pack - template - auto try_invoke_impl(Arg&& arg, Fun&& fun) { - constexpr bool is_none = - std::is_same_v, None>; - if constexpr (!is_none) { - if constexpr (std::is_invocable_v) { - return fun(); - } else if constexpr (std::is_invocable_v) { - return std::invoke(std::forward(fun), - std::forward(arg)); - } else { - return std::apply(std::forward(fun), - std::forward(arg)); - } - } - } - - //////////////// - // 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(&new_buffer_, &size_, file); - size_t string_end = size - 1; - - if (size == -1) { - return false; - } - - if (size >= 2 && new_buffer_[size - 2] == '\r') { - string_end--; - } - - new_buffer_[string_end] = '\0'; - return true; - } - - const char* get() const { - return buffer_; - } - - void update() { - std::swap(buffer_, new_buffer_); - } - }; - - void read_line() { - eof_ = !buff_.read(file_); - ++line_number_; - } - - //////////////// - // error - //////////////// - - void clear_error() { - string_error_.clear(); - bool_error_ = false; - } - - void set_error_failed_check() { - if (error_mode_ == error_mode::String) { - string_error_.append(file_name_) - .append(" failed check."); + template + void try_convert_and_invoke(std::optional& value, Fun&& fun) { + if (!parser_.valid()) { + std::optional new_value; + auto tuple_output = try_same(); + if constexpr (!std::is_same_v) { + new_value = to_object(std::move(tuple_output)); } else { - bool_error_ = true; + new_value = std::move(tuple_output); } + if (parser_.valid()) { + value = std::move(new_value); + parser_.try_invoke(*value, std::forward(fun)); + } + } } - void set_error_file_not_open() { - if (error_mode_ == error_mode::String) { - string_error_.append(file_name_) - .append(" could not be not open."); - } else { - bool_error_ = true; - } + 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; } - void set_error_eof_reached() { - if (error_mode_ == error_mode::String) { - string_error_.append(file_name_) - .append(" reached end of file."); - } else { - bool_error_ = true; + std::tuple values_; + parser& parser_; + }; + + // tries to convert a line and returns a composite which is + // able to try additional conversions in case of failure + template + composite>> try_next( + Fun&& fun = None{}) { + std::optional> value; + auto new_value = get_next(); + if (valid()) { + value = std::move(new_value); + try_invoke(*value, std::forward(fun)); + } + return {std::move(value), *this}; + }; + +private: + template + friend class composite; + + // tries to invoke the given function (see below), if the function + // returns a value which can be used as a conditional, and it returns + // false, the function sets an error, and allows the invoke of the + // next possible conversion as if the validation of the current one + // failed + template + void try_invoke(Arg&& arg, Fun&& fun) { + constexpr bool is_none = std::is_same_v, None>; + if constexpr (!is_none) { + using Ret = decltype(try_invoke_impl(arg, std::forward(fun))); + constexpr bool returns_void = std::is_same_v; + if constexpr (!returns_void) { + if (!try_invoke_impl(arg, std::forward(fun))) { + set_error_failed_check(); } + } else { + try_invoke_impl(arg, std::forward(fun)); + } + } + } + + // tries to invoke the function if not None + // it first tries to invoke the function without arguments, + // than with one argument if the function accepts the whole tuple + // as an argument, and finally tries to invoke it with the tuple + // laid out as a parameter pack + template + auto try_invoke_impl(Arg&& arg, Fun&& fun) { + constexpr bool is_none = std::is_same_v, None>; + if constexpr (!is_none) { + if constexpr (std::is_invocable_v) { + return fun(); + } else if constexpr (std::is_invocable_v) { + return std::invoke(std::forward(fun), + std::forward(arg)); + } else { + return std::apply(std::forward(fun), + std::forward(arg)); + } + } + } + + //////////////// + // line reading + //////////////// + + class buffer { + char* buffer_{nullptr}; + char* new_buffer_{nullptr}; + size_t size_{0}; + + public: + ~buffer() { + free(buffer_); + free(new_buffer_); } - void set_error_invalid_conversion() { - 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; - } + bool read(FILE* file) { + ssize_t size = getline(&new_buffer_, &size_, file); + size_t string_end = size - 1; + + if (size == -1) { + return false; + } + + if (size >= 2 && new_buffer_[size - 2] == '\r') { + string_end--; + } + + new_buffer_[string_end] = '\0'; + return true; } - //////////////// - // members - //////////////// + const char* get() const { + return buffer_; + } - const std::string file_name_; - const std::string delim_; - 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}; - bool eof_{false}; + void update() { + std::swap(buffer_, new_buffer_); + } + }; + + void read_line() { + eof_ = !buff_.read(file_); + ++line_number_; + } + + //////////////// + // error + //////////////// + + void clear_error() { + string_error_.clear(); + bool_error_ = false; + } + + void set_error_failed_check() { + if (error_mode_ == error_mode::String) { + string_error_.append(file_name_).append(" failed check."); + } else { + bool_error_ = true; + } + } + + void set_error_file_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() { + 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() { + 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; + } + } + + //////////////// + // members + //////////////// + + const std::string file_name_; + const std::string delim_; + 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}; + bool eof_{false}; }; } /* ss */ diff --git a/include/ss/restrictions.hpp b/include/ss/restrictions.hpp index 1848e3e..5e45992 100644 --- a/include/ss/restrictions.hpp +++ b/include/ss/restrictions.hpp @@ -8,23 +8,23 @@ namespace ss { template struct ax { - private: - template - bool ss_valid_impl(const T& x) const { - if constexpr (sizeof...(Xs) != 0) { - return x != X && ss_valid_impl(x); - } - return x != X; +private: + template + bool ss_valid_impl(const T& x) const { + if constexpr (sizeof...(Xs) != 0) { + return x != X && ss_valid_impl(x); } + return x != X; + } - public: - bool ss_valid(const T& value) const { - return ss_valid_impl(value); - } +public: + bool ss_valid(const T& value) const { + return ss_valid_impl(value); + } - const char* error() const { - return "value excluded"; - } + const char* error() const { + return "value excluded"; + } }; //////////////// @@ -33,23 +33,23 @@ struct ax { template struct nx { - private: - template - bool ss_valid_impl(const T& x) const { - if constexpr (sizeof...(Xs) != 0) { - return x == X || ss_valid_impl(x); - } - return x == X; +private: + template + bool ss_valid_impl(const T& x) const { + if constexpr (sizeof...(Xs) != 0) { + return x == X || ss_valid_impl(x); } + return x == X; + } - public: - bool ss_valid(const T& value) const { - return ss_valid_impl(value); - } +public: + bool ss_valid(const T& value) const { + return ss_valid_impl(value); + } - const char* error() const { - return "value excluded"; - } + const char* error() const { + return "value excluded"; + } }; //////////////// @@ -58,13 +58,13 @@ struct nx { template struct ir { - bool ss_valid(const T& value) const { - return value >= Min && value <= Max; - } + bool ss_valid(const T& value) const { + return value >= Min && value <= Max; + } - const char* error() const { - return "out of range"; - } + const char* error() const { + return "out of range"; + } }; //////////////// @@ -73,13 +73,13 @@ struct ir { template struct oor { - bool ss_valid(const T& value) const { - return value < Min || value > Max; - } + bool ss_valid(const T& value) const { + return value < Min || value > Max; + } - const char* error() const { - return "in restricted range"; - } + const char* error() const { + return "in restricted range"; + } }; //////////////// @@ -88,13 +88,13 @@ struct oor { template struct ne { - bool ss_valid(const T& value) const { - return !value.empty(); - } + bool ss_valid(const T& value) const { + return !value.empty(); + } - const char* error() const { - return "empty field"; - } + const char* error() const { + return "empty field"; + } }; } /* ss */ diff --git a/include/ss/type_traits.hpp b/include/ss/type_traits.hpp index 70dddc1..11a2dc7 100644 --- a/include/ss/type_traits.hpp +++ b/include/ss/type_traits.hpp @@ -14,12 +14,12 @@ struct tup_cat; template struct tup_cat, std::tuple> { - using type = std::tuple; + using type = std::tuple; }; template struct tup_cat> { - using type = std::tuple; + using type = std::tuple; }; template @@ -34,14 +34,14 @@ struct left_of_impl; template struct left_of_impl { - static_assert(N < 128, "recursion limit reached"); - static_assert(N != 0, "cannot take the whole tuple"); - using type = tup_cat_t::type>; + static_assert(N < 128, "recursion limit reached"); + static_assert(N != 0, "cannot take the whole tuple"); + using type = tup_cat_t::type>; }; template struct left_of_impl<0, T, Ts...> { - using type = std::tuple; + using type = std::tuple; }; template @@ -62,12 +62,12 @@ struct right_of_impl; template struct right_of_impl { - using type = typename right_of_impl::type; + using type = typename right_of_impl::type; }; template struct right_of_impl<0, T, Ts...> { - using type = std::tuple; + using type = std::tuple; }; template @@ -88,19 +88,19 @@ struct apply_trait; template