add trim_left and trim_right, add setup static asserts, update type_traits, refactor some code, add unit tests

This commit is contained in:
ado 2021-02-14 01:59:06 +01:00
parent ea42948c42
commit 2985027505
8 changed files with 381 additions and 146 deletions

19
include/ss/common.hpp Normal file
View File

@ -0,0 +1,19 @@
#pragma once
#include <vector>
namespace ss {
struct none {};
using string_range = std::pair<const char*, const char*>;
using split_data = std::vector<string_range>;
constexpr inline auto default_delimiter = ",";
template <bool StringError>
inline void assert_string_error_defined() {
static_assert(StringError,
"'string_error' needs to be enabled to use 'error_msg'");
}
} /* ss */

View File

@ -40,19 +40,13 @@ template <typename T>
using no_validator_t = typename no_validator<T>::type; using no_validator_t = typename no_validator<T>::type;
template <typename... Ts> template <typename... Ts>
struct no_validator_tup { struct no_validator_tup : apply_trait<no_validator, std::tuple<Ts...>> {};
using type = typename apply_trait<no_validator, std::tuple<Ts...>>::type;
};
template <typename... Ts> template <typename... Ts>
struct no_validator_tup<std::tuple<Ts...>> { struct no_validator_tup<std::tuple<Ts...>> : no_validator_tup<Ts...> {};
using type = typename no_validator_tup<Ts...>::type;
};
template <typename T> template <typename T>
struct no_validator_tup<std::tuple<T>> { struct no_validator_tup<std::tuple<T>> : no_validator<T> {};
using type = no_validator_t<T>;
};
template <typename... Ts> template <typename... Ts>
using no_validator_tup_t = typename no_validator_tup<Ts...>::type; using no_validator_tup_t = typename no_validator_tup<Ts...>::type;
@ -62,10 +56,7 @@ using no_validator_tup_t = typename no_validator_tup<Ts...>::type;
//////////////// ////////////////
template <typename... Ts> template <typename... Ts>
struct no_void_tup { struct no_void_tup : filter_not<std::is_void, no_validator_tup_t<Ts...>> {};
using type =
typename filter_not<std::is_void, no_validator_tup_t<Ts...>>::type;
};
template <typename... Ts> template <typename... Ts>
using no_void_tup_t = filter_not_t<std::is_void, Ts...>; using no_void_tup_t = filter_not_t<std::is_void, Ts...>;
@ -76,14 +67,11 @@ using no_void_tup_t = filter_not_t<std::is_void, Ts...>;
// replace 'validators' and remove void from tuple // replace 'validators' and remove void from tuple
template <typename... Ts> template <typename... Ts>
struct no_void_validator_tup { struct no_void_validator_tup : no_validator_tup<no_void_tup_t<Ts...>> {};
using type = no_validator_tup_t<no_void_tup_t<Ts...>>;
};
template <typename... Ts> template <typename... Ts>
struct no_void_validator_tup<std::tuple<Ts...>> { struct no_void_validator_tup<std::tuple<Ts...>>
using type = no_validator_tup_t<no_void_tup_t<Ts...>>; : no_validator_tup<no_void_tup_t<Ts...>> {};
};
template <typename... Ts> template <typename... Ts>
using no_void_validator_tup_t = typename no_void_validator_tup<Ts...>::type; using no_void_validator_tup_t = typename no_void_validator_tup<Ts...>::type;
@ -131,12 +119,12 @@ public:
no_void_validator_tup_t<Ts...> convert( no_void_validator_tup_t<Ts...> convert(
line_ptr_type line, const std::string& delim = default_delimiter) { line_ptr_type line, const std::string& delim = default_delimiter) {
split(line, delim); split(line, delim);
return convert<Ts...>(splitter_.split_input_); return convert<Ts...>(splitter_.split_data_);
} }
// parses already split line, returns 'T' object with extracted values // parses already split line, returns 'T' object with extracted values
template <typename T, typename... Ts> template <typename T, typename... Ts>
T convert_object(const split_input& elems) { T convert_object(const split_data& elems) {
return to_object<T>(convert<Ts...>(elems)); return to_object<T>(convert<Ts...>(elems));
} }
@ -151,17 +139,12 @@ public:
// one argument is given which is a class which has a tied // one argument is given which is a class which has a tied
// method which returns a tuple, returns that type // method which returns a tuple, returns that type
template <typename T, typename... Ts> template <typename T, typename... Ts>
no_void_validator_tup_t<T, Ts...> convert(const split_input& elems) { no_void_validator_tup_t<T, Ts...> convert(const split_data& elems) {
if constexpr (sizeof...(Ts) == 0 && if constexpr (sizeof...(Ts) == 0 && is_instance_of_v<std::tuple, T>) {
is_instance_of<T, std::tuple>::value) {
return convert_impl(elems, static_cast<T*>(nullptr)); return convert_impl(elems, static_cast<T*>(nullptr));
} else if constexpr (tied_class_v<T, Ts...>) { } else if constexpr (tied_class_v<T, Ts...>) {
using arg_ref_tuple = using arg_ref_tuple = std::result_of_t<decltype (&T::tied)(T)>;
typename std::result_of_t<decltype (&T::tied)(T)>; using arg_tuple = apply_trait_t<std::decay, arg_ref_tuple>;
using arg_tuple =
typename apply_trait<std::decay, arg_ref_tuple>::type;
return to_object<T>( return to_object<T>(
convert_impl(elems, static_cast<arg_tuple*>(nullptr))); convert_impl(elems, static_cast<arg_tuple*>(nullptr)));
@ -173,7 +156,7 @@ public:
// same as above, but uses cached split line // same as above, but uses cached split line
template <typename T, typename... Ts> template <typename T, typename... Ts>
no_void_validator_tup_t<T, Ts...> convert() { no_void_validator_tup_t<T, Ts...> convert() {
return convert<T, Ts...>(splitter_.split_input_); return convert<T, Ts...>(splitter_.split_data_);
} }
bool valid() const { bool valid() const {
@ -185,8 +168,7 @@ public:
} }
const std::string& error_msg() const { const std::string& error_msg() const {
static_assert(string_error, assert_string_error_defined<string_error>();
"'string_error' needs to be enabled to use 'error_msg'");
return error_; return error_;
} }
@ -196,11 +178,11 @@ public:
// 'splits' string by given delimiter, returns vector of pairs which // 'splits' string by given delimiter, returns vector of pairs which
// contain the beginnings and the ends of each column of the string // contain the beginnings and the ends of each column of the string
const split_input& split(line_ptr_type line, const split_data& split(line_ptr_type line,
const std::string& delim = default_delimiter) { const std::string& delim = default_delimiter) {
splitter_.split_input_.clear(); splitter_.split_data_.clear();
if (line[0] == '\0') { if (line[0] == '\0') {
return splitter_.split_input_; return splitter_.split_data_;
} }
return splitter_.split(line, delim); return splitter_.split(line, delim);
@ -211,7 +193,7 @@ private:
// resplit // resplit
//////////////// ////////////////
const split_input& resplit(line_ptr_type new_line, ssize_t new_size, const split_data& resplit(line_ptr_type new_line, ssize_t new_size,
const std::string& delim = default_delimiter) { const std::string& delim = default_delimiter) {
return splitter_.resplit(new_line, new_size, delim); return splitter_.resplit(new_line, new_size, delim);
} }
@ -285,7 +267,7 @@ private:
//////////////// ////////////////
template <typename... Ts> template <typename... Ts>
no_void_validator_tup_t<Ts...> convert_impl(const split_input& elems) { no_void_validator_tup_t<Ts...> convert_impl(const split_data& elems) {
clear_error(); clear_error();
if (!splitter_.valid()) { if (!splitter_.valid()) {
@ -306,7 +288,7 @@ private:
// do not know how to specialize by return type :( // do not know how to specialize by return type :(
template <typename... Ts> template <typename... Ts>
no_void_validator_tup_t<std::tuple<Ts...>> convert_impl( no_void_validator_tup_t<std::tuple<Ts...>> convert_impl(
const split_input& elems, const std::tuple<Ts...>*) { const split_data& elems, const std::tuple<Ts...>*) {
return convert_impl<Ts...>(elems); return convert_impl<Ts...>(elems);
} }
@ -345,11 +327,11 @@ private:
template <size_t ArgN, size_t TupN, typename... Ts> template <size_t ArgN, size_t TupN, typename... Ts>
void extract_multiple(no_void_validator_tup_t<Ts...>& tup, void extract_multiple(no_void_validator_tup_t<Ts...>& tup,
const split_input& elems) { const split_data& elems) {
using elem_t = std::tuple_element_t<ArgN, std::tuple<Ts...>>; using elem_t = std::tuple_element_t<ArgN, std::tuple<Ts...>>;
constexpr bool not_void = !std::is_void_v<elem_t>; constexpr bool not_void = !std::is_void_v<elem_t>;
constexpr bool one_element = count_not<std::is_void, Ts...>::size == 1; constexpr bool one_element = count_not_v<std::is_void, Ts...> == 1;
if constexpr (not_void) { if constexpr (not_void) {
if constexpr (one_element) { if constexpr (one_element) {
@ -367,8 +349,8 @@ private:
} }
template <typename... Ts> template <typename... Ts>
no_void_validator_tup_t<Ts...> extract_tuple(const split_input& elems) { no_void_validator_tup_t<Ts...> extract_tuple(const split_data& elems) {
static_assert(!all_of<std::is_void, Ts...>::value, static_assert(!all_of_v<std::is_void, Ts...>,
"at least one parameter must be non void"); "at least one parameter must be non void");
no_void_validator_tup_t<Ts...> ret{}; no_void_validator_tup_t<Ts...> ret{};
extract_multiple<0, 0, Ts...>(ret, elems); extract_multiple<0, 0, Ts...>(ret, elems);
@ -379,7 +361,7 @@ private:
// members // members
//////////////// ////////////////
error_type error_; error_type error_{};
splitter<Matchers...> splitter_; splitter<Matchers...> splitter_;
template <typename...> template <typename...>

View File

@ -225,8 +225,8 @@ struct unsupported_type {
template <typename T> template <typename T>
std::enable_if_t<!std::is_integral_v<T> && !std::is_floating_point_v<T> && std::enable_if_t<!std::is_integral_v<T> && !std::is_floating_point_v<T> &&
!is_instance_of<T, std::optional>::value && !is_instance_of_v<std::optional, T> &&
!is_instance_of<T, std::variant>::value, !is_instance_of_v<std::variant, T>,
bool> bool>
extract(const char*, const char*, T&) { extract(const char*, const char*, T&) {
static_assert(error::unsupported_type<T>::value, static_assert(error::unsupported_type<T>::value,
@ -246,7 +246,7 @@ extract(const char* begin, const char* end, T& value) {
} }
template <typename T> template <typename T>
std::enable_if_t<is_instance_of<T, std::optional>::value, bool> extract( std::enable_if_t<is_instance_of_v<std::optional, T>, bool> extract(
const char* begin, const char* end, T& value) { const char* begin, const char* end, T& value) {
typename T::value_type raw_value; typename T::value_type raw_value;
if (extract(begin, end, raw_value)) { if (extract(begin, end, raw_value)) {
@ -271,7 +271,7 @@ bool extract_variant(const char* begin, const char* end, T& value) {
} }
template <typename T> template <typename T>
std::enable_if_t<is_instance_of<T, std::variant>::value, bool> extract( std::enable_if_t<is_instance_of_v<std::variant, T>, bool> extract(
const char* begin, const char* end, T& value) { const char* begin, const char* end, T& value) {
return extract_variant<T, 0>(begin, end, value); return extract_variant<T, 0>(begin, end, value);
} }

View File

@ -13,8 +13,6 @@ namespace ss {
template <typename... Matchers> template <typename... Matchers>
class parser { class parser {
struct none {};
constexpr static auto string_error = setup<Matchers...>::string_error; constexpr static auto string_error = setup<Matchers...>::string_error;
constexpr static auto multiline = setup<Matchers...>::multiline; constexpr static auto multiline = setup<Matchers...>::multiline;
@ -48,8 +46,7 @@ public:
} }
const std::string& error_msg() const { const std::string& error_msg() const {
static_assert(string_error, assert_string_error_defined<string_error>();
"'string_error' needs to be enabled to use 'error_msg'");
return error_; return error_;
} }
@ -157,11 +154,13 @@ public:
if (!parser_.valid()) { if (!parser_.valid()) {
return; return;
} }
if constexpr (!std::is_same_v<U, decltype(tuple_output)>) { if constexpr (!std::is_same_v<U, decltype(tuple_output)>) {
value = to_object<U>(std::move(tuple_output)); value = to_object<U>(std::move(tuple_output));
} else { } else {
value = std::move(tuple_output); value = std::move(tuple_output);
} }
parser_.try_invoke(*value, std::forward<Fun>(fun)); parser_.try_invoke(*value, std::forward<Fun>(fun));
} }
} }
@ -177,6 +176,10 @@ public:
return value; return value;
} }
////////////////
// members
////////////////
std::tuple<Ts...> values_; std::tuple<Ts...> values_;
parser& parser_; parser& parser_;
}; };
@ -488,7 +491,7 @@ private:
//////////////// ////////////////
std::string file_name_; std::string file_name_;
error_type error_; error_type error_{};
reader reader_; reader reader_;
size_t line_number_{0}; size_t line_number_{0};
bool eof_{false}; bool eof_{false};

View File

@ -4,6 +4,10 @@
namespace ss { namespace ss {
////////////////
// matcher
////////////////
template <char... Cs> template <char... Cs>
struct matcher { struct matcher {
private: private:
@ -36,7 +40,7 @@ public:
}; };
template <typename FirstMatcher, typename SecondMatcher> template <typename FirstMatcher, typename SecondMatcher>
constexpr bool matches_intersect() { inline constexpr bool matches_intersect() {
for (const auto& first_match : FirstMatcher::matches) { for (const auto& first_match : FirstMatcher::matches) {
for (const auto& second_match : SecondMatcher::matches) { for (const auto& second_match : SecondMatcher::matches) {
if (first_match != '\0' && first_match == second_match) { if (first_match != '\0' && first_match == second_match) {
@ -47,6 +51,13 @@ constexpr bool matches_intersect() {
return false; return false;
} }
template <typename FirstMatcher, typename SecondMatcher1,
typename SecondMatcher2>
inline constexpr bool matches_intersect_union() {
return matches_intersect<FirstMatcher, SecondMatcher1>() ||
matches_intersect<FirstMatcher, SecondMatcher2>();
}
template <> template <>
class matcher<'\0'> { class matcher<'\0'> {
public: public:
@ -55,31 +66,57 @@ public:
static bool match(char c) = delete; static bool match(char c) = delete;
}; };
////////////////
// setup parameters
////////////////
template <char C> template <char C>
struct quote : matcher<C> {}; struct quote : matcher<C> {};
template <char... Cs> template <char... Cs>
struct trim : matcher<Cs...> {}; struct trim : matcher<Cs...> {};
template <char... Cs>
struct trim_left : matcher<Cs...> {};
template <char... Cs>
struct trim_right : matcher<Cs...> {};
template <char... Cs> template <char... Cs>
struct escape : matcher<Cs...> {}; struct escape : matcher<Cs...> {};
// TODO add limit
class multiline;
class string_error;
////////////////
// setup implementation
////////////////
template <typename T, template <char...> class Template> template <typename T, template <char...> class Template>
struct is_instance_of_matcher { struct is_instance_of_matcher : std::false_type {};
constexpr static bool value = false;
};
template <char... Ts, template <char...> class Template> template <char... Ts, template <char...> class Template>
struct is_instance_of_matcher<Template<Ts...>, Template> { struct is_instance_of_matcher<Template<Ts...>, Template> : std::true_type {};
constexpr static bool value = true;
}; template <typename T, template <char...> class Template>
using is_instance_of_matcher_t =
typename is_instance_of_matcher<T, Template>::type;
template <template <char...> class Matcher, typename... Ts> template <template <char...> class Matcher, typename... Ts>
struct get_matcher; struct get_matcher;
template <template <char...> class Matcher, typename T, typename... Ts> template <template <char...> class Matcher, typename T, typename... Ts>
struct get_matcher<Matcher, T, Ts...> { struct get_matcher<Matcher, T, Ts...> {
using type = ternary_t<is_instance_of_matcher<T, Matcher>::value, T,
template <typename U>
struct is_matcher : is_instance_of_matcher<U, Matcher> {};
static_assert(count_v<is_matcher, T, Ts...> <= 1,
"the same matcher is cannot"
"be defined multiple times");
using type = ternary_t<is_matcher<T>::value, T,
typename get_matcher<Matcher, Ts...>::type>; typename get_matcher<Matcher, Ts...>::type>;
}; };
@ -91,39 +128,72 @@ struct get_matcher<Matcher> {
template <template <char...> class Matcher, typename... Ts> template <template <char...> class Matcher, typename... Ts>
using get_matcher_t = typename get_matcher<Matcher, Ts...>::type; using get_matcher_t = typename get_matcher<Matcher, Ts...>::type;
class multiline;
class string_error;
template <typename... Ts> template <typename... Ts>
struct setup { struct setup {
private: private:
template <typename T> template <typename T>
struct is_multiline : std::is_same<T, multiline> {}; struct is_matcher
: std::disjunction<is_instance_of_matcher_t<T, quote>,
is_instance_of_matcher_t<T, escape>,
is_instance_of_matcher_t<T, trim>,
is_instance_of_matcher_t<T, trim_left>,
is_instance_of_matcher_t<T, trim_right>> {};
constexpr static auto count_multiline = count<is_multiline, Ts...>::size; template <typename T>
struct is_multiline : std::is_same<T, multiline> {};
template <typename T> template <typename T>
struct is_string_error : std::is_same<T, string_error> {}; struct is_string_error : std::is_same<T, string_error> {};
constexpr static auto count_string_error = constexpr static auto count_matcher = count_v<is_matcher, Ts...>;
count<is_string_error, Ts...>::size; constexpr static auto count_multiline = count_v<is_multiline, Ts...>;
constexpr static auto count_string_error = count_v<is_string_error, Ts...>;
constexpr static auto number_of_valid_setup_types =
count_matcher + count_multiline + count_string_error;
using trim_left_only = get_matcher_t<trim_left, Ts...>;
using trim_right_only = get_matcher_t<trim_right, Ts...>;
using trim_all = get_matcher_t<trim, Ts...>;
public: public:
using quote = get_matcher_t<quote, Ts...>; using quote = get_matcher_t<quote, Ts...>;
using trim = get_matcher_t<trim, Ts...>;
using escape = get_matcher_t<escape, Ts...>; using escape = get_matcher_t<escape, Ts...>;
using trim_left = ternary_t<trim_all::enabled, trim_all, trim_left_only>;
using trim_right = ternary_t<trim_all::enabled, trim_all, trim_right_only>;
constexpr static bool multiline = (count_multiline == 1); constexpr static bool multiline = (count_multiline == 1);
constexpr static bool string_error = (count_string_error == 1); constexpr static bool string_error = (count_string_error == 1);
private:
#define ASSERT_MSG "cannot have the same match character in multiple matchers"
static_assert(!matches_intersect<escape, quote>(), ASSERT_MSG);
constexpr static auto quote_trim_intersect =
matches_intersect_union<quote, trim_left, trim_right>();
static_assert(!quote_trim_intersect, ASSERT_MSG);
constexpr static auto escape_trim_intersect =
matches_intersect_union<escape, trim_left, trim_right>();
static_assert(!escape_trim_intersect, ASSERT_MSG);
#undef ASSERT_MSG
static_assert( static_assert(
!multiline || (multiline && (quote::enabled || escape::enabled)), !multiline || (multiline && (quote::enabled || escape::enabled)),
"to enable multiline either quote or escape need to be enabled"); "to enable multiline either quote or escape need to be enabled");
#define ASSERT_MSG "cannot have the same match character in multiple matchers" static_assert(!(trim_all::enabled && trim_left_only::enabled) &&
static_assert(!matches_intersect<quote, trim>(), ASSERT_MSG); !(trim_all::enabled && trim_right_only::enabled),
static_assert(!matches_intersect<trim, escape>(), ASSERT_MSG); "ambiguous trim setup");
static_assert(!matches_intersect<escape, quote>(), ASSERT_MSG);
#undef ASSERT_MSG static_assert(count_multiline <= 1, "mutliline defined multiple times");
static_assert(count_string_error <= 1,
"string_error defined multiple times");
static_assert(number_of_valid_setup_types == sizeof...(Ts),
"one or multiple invalid setup parameters defined");
}; };
template <typename... Ts> template <typename... Ts>

View File

@ -1,4 +1,5 @@
#pragma once #pragma once
#include "common.hpp"
#include "setup.hpp" #include "setup.hpp"
#include "type_traits.hpp" #include "type_traits.hpp"
#include <algorithm> #include <algorithm>
@ -10,16 +11,12 @@
namespace ss { namespace ss {
// TODO move to common or something
using string_range = std::pair<const char*, const char*>;
using split_input = std::vector<string_range>;
constexpr static auto default_delimiter = ",";
template <typename... Ts> template <typename... Ts>
class splitter { class splitter {
private: private:
using quote = typename setup<Ts...>::quote; using quote = typename setup<Ts...>::quote;
using trim = typename setup<Ts...>::trim; using trim_left = typename setup<Ts...>::trim_left;
using trim_right = typename setup<Ts...>::trim_right;
using escape = typename setup<Ts...>::escape; using escape = typename setup<Ts...>::escape;
constexpr static auto string_error = setup<Ts...>::string_error; constexpr static auto string_error = setup<Ts...>::string_error;
@ -39,8 +36,7 @@ public:
} }
const std::string& error_msg() const { const std::string& error_msg() const {
static_assert(string_error, assert_string_error_defined<string_error>();
"'string_error' needs to be enabled to use 'error_msg'");
return error_; return error_;
} }
@ -48,9 +44,9 @@ public:
return unterminated_quote_; return unterminated_quote_;
} }
const split_input& split(line_ptr_type new_line, const split_data& split(line_ptr_type new_line,
const std::string& delimiter = default_delimiter) { const std::string& delimiter = default_delimiter) {
split_input_.clear(); split_data_.clear();
return resplit(new_line, -1, delimiter); return resplit(new_line, -1, delimiter);
} }
@ -60,33 +56,35 @@ private:
//////////////// ////////////////
void adjust_ranges(const char* old_line) { void adjust_ranges(const char* old_line) {
for (auto& [begin, end] : split_input_) { for (auto& [begin, end] : split_data_) {
begin = begin - old_line + line_; begin = begin - old_line + line_;
end = end - old_line + line_; end = end - old_line + line_;
} }
} }
const split_input& resplit( const split_data& resplit(
line_ptr_type new_line, ssize_t new_size, line_ptr_type new_line, ssize_t new_size,
const std::string& delimiter = default_delimiter) { const std::string& delimiter = default_delimiter) {
line_ = new_line; line_ = new_line;
// resplitting, continue from last slice // resplitting, continue from last slice
if (!split_input_.empty() && unterminated_quote()) { if constexpr (quote::enabled) {
const auto& last = std::prev(split_input_.end()); if (!split_data_.empty() && unterminated_quote()) {
const auto& last = std::prev(split_data_.end());
const auto [old_line, old_begin] = *last; const auto [old_line, old_begin] = *last;
size_t begin = old_begin - old_line - 1; size_t begin = old_begin - old_line - 1;
split_input_.pop_back(); split_data_.pop_back();
adjust_ranges(old_line); adjust_ranges(old_line);
// safety measure // safety measure
if (new_size != -1 && static_cast<size_t>(new_size) < begin) { if (new_size != -1 && static_cast<size_t>(new_size) < begin) {
set_error_invalid_resplit(); set_error_invalid_resplit();
return split_input_; return split_data_;
} }
begin_ = line_ + begin; begin_ = line_ + begin;
} }
}
return split_impl_select_delim(delimiter); return split_impl_select_delim(delimiter);
} }
@ -163,9 +161,17 @@ private:
return delim.size(); return delim.size();
} }
void trim_if_enabled(line_ptr_type& curr) { void trim_left_if_enabled(line_ptr_type& curr) {
if constexpr (trim::enabled) { if constexpr (trim_left::enabled) {
while (trim::match(*curr)) { while (trim_left::match(*curr)) {
++curr;
}
}
}
void trim_right_if_enabled(line_ptr_type& curr) {
if constexpr (trim_right::enabled) {
while (trim_right::match(*curr)) {
++curr; ++curr;
} }
} }
@ -176,7 +182,7 @@ private:
const Delim& delim) { const Delim& delim) {
line_ptr_type end = begin; line_ptr_type end = begin;
trim_if_enabled(end); trim_right_if_enabled(end);
// just spacing // just spacing
if (*end == '\0') { if (*end == '\0') {
@ -190,7 +196,7 @@ private:
} }
end += delimiter_size(delim); end += delimiter_size(delim);
trim_if_enabled(end); trim_left_if_enabled(end);
// delimiter // delimiter
return {end - begin, true}; return {end - begin, true};
@ -213,7 +219,7 @@ private:
void shift_and_push() { void shift_and_push() {
shift_and_set_current(); shift_and_set_current();
split_input_.emplace_back(begin_, curr_); split_data_.emplace_back(begin_, curr_);
} }
void shift_if_escaped(line_ptr_type& curr) { void shift_if_escaped(line_ptr_type& curr) {
@ -241,13 +247,13 @@ private:
// split impl // split impl
//////////////// ////////////////
const split_input& split_impl_select_delim( const split_data& split_impl_select_delim(
const std::string& delimiter = default_delimiter) { const std::string& delimiter = default_delimiter) {
clear_error(); clear_error();
switch (delimiter.size()) { switch (delimiter.size()) {
case 0: case 0:
set_error_empty_delimiter(); set_error_empty_delimiter();
return split_input_; return split_data_;
case 1: case 1:
return split_impl(delimiter[0]); return split_impl(delimiter[0]);
default: default:
@ -256,18 +262,18 @@ private:
} }
template <typename Delim> template <typename Delim>
const split_input& split_impl(const Delim& delim) { const split_data& split_impl(const Delim& delim) {
if (split_input_.empty()) { if (split_data_.empty()) {
begin_ = line_; begin_ = line_;
} }
trim_if_enabled(begin_); trim_left_if_enabled(begin_);
for (done_ = false; !done_; read(delim)) for (done_ = false; !done_; read(delim))
; ;
return split_input_; return split_data_;
} }
//////////////// ////////////////
@ -329,7 +335,7 @@ private:
// eg: ..."hell\0 -> quote not terminated // eg: ..."hell\0 -> quote not terminated
if (*end_ == '\0') { if (*end_ == '\0') {
set_error_unterminated_quote(); set_error_unterminated_quote();
split_input_.emplace_back(line_, begin_); split_data_.emplace_back(line_, begin_);
done_ = true; done_ = true;
break; break;
} }
@ -363,7 +369,7 @@ private:
// mismatched quote // mismatched quote
// eg: ...,"hel"lo,... -> error // eg: ...,"hel"lo,... -> error
set_error_mismatched_quote(end_ - line_); set_error_mismatched_quote(end_ - line_);
split_input_.emplace_back(line_, begin_); split_data_.emplace_back(line_, begin_);
} }
done_ = true; done_ = true;
break; break;
@ -375,17 +381,16 @@ private:
// members // members
//////////////// ////////////////
static_assert(std::is_same_v<error_type, bool> || error_type error_{};
std::is_same_v<error_type, std::string>);
error_type error_;
bool unterminated_quote_{false}; bool unterminated_quote_{false};
bool done_;
size_t escaped_{0};
split_data split_data_;
line_ptr_type begin_; line_ptr_type begin_;
line_ptr_type curr_; line_ptr_type curr_;
line_ptr_type end_; line_ptr_type end_;
line_ptr_type line_; line_ptr_type line_;
bool done_;
size_t escaped_{0};
split_input split_input_;
template <typename...> template <typename...>
friend class converter; friend class converter;

View File

@ -103,6 +103,9 @@ struct apply_trait<Trait, std::tuple<T>> {
using type = std::tuple<typename Trait<T>::type>; using type = std::tuple<typename Trait<T>::type>;
}; };
template <template <typename...> class Trait, typename... Ts>
using apply_trait_t = typename apply_trait<Trait, Ts...>::type;
//////////////// ////////////////
// apply optional trait // apply optional trait
//////////////// ////////////////
@ -143,6 +146,9 @@ struct apply_optional_trait<Trait, std::tuple<T>> {
std::tuple<typename optional_trait<typename Trait<T>::type, T>::type>; std::tuple<typename optional_trait<typename Trait<T>::type, T>::type>;
}; };
template <template <typename...> class Trait, typename... Ts>
using apply_trait_optional_t = apply_optional_trait<Trait, Ts...>;
//////////////// ////////////////
// filter false_type // filter false_type
//////////////// ////////////////
@ -192,6 +198,9 @@ struct negate_impl {
using type = std::integral_constant<bool, !Trait<Ts...>::value>; using type = std::integral_constant<bool, !Trait<Ts...>::value>;
}; };
template <template <typename...> class Trait>
using negate_impl_t = typename negate_impl<Trait>::type;
//////////////// ////////////////
// filter by trait // filter by trait
//////////////// ////////////////
@ -235,20 +244,23 @@ struct count;
template <template <typename...> class Trait, typename T, typename... Ts> template <template <typename...> class Trait, typename T, typename... Ts>
struct count<Trait, T, Ts...> { struct count<Trait, T, Ts...> {
static constexpr size_t size = static constexpr size_t value =
std::tuple_size<filter_if_t<Trait, T, Ts...>>::value; std::tuple_size<filter_if_t<Trait, T, Ts...>>::value;
}; };
template <template <typename...> class Trait, typename T> template <template <typename...> class Trait, typename T>
struct count<Trait, T> { struct count<Trait, T> {
static constexpr size_t size = Trait<T>::value; static constexpr size_t value = Trait<T>::value;
}; };
template <template <typename...> class Trait> template <template <typename...> class Trait>
struct count<Trait> { struct count<Trait> {
static constexpr size_t size = 0; static constexpr size_t value = 0;
}; };
template <template <typename...> class Trait, typename... Ts>
constexpr size_t count_v = count<Trait, Ts...>::value;
//////////////// ////////////////
// count not // count not
//////////////// ////////////////
@ -258,34 +270,40 @@ struct count_not;
template <template <typename...> class Trait, typename T, typename... Ts> template <template <typename...> class Trait, typename T, typename... Ts>
struct count_not<Trait, T, Ts...> { struct count_not<Trait, T, Ts...> {
static constexpr size_t size = static constexpr size_t value =
std::tuple_size<filter_not_t<Trait, T, Ts...>>::value; std::tuple_size<filter_not_t<Trait, T, Ts...>>::value;
}; };
template <template <typename...> class Trait, typename T> template <template <typename...> class Trait, typename T>
struct count_not<Trait, T> { struct count_not<Trait, T> {
static constexpr size_t size = !Trait<T>::value; static constexpr size_t value = !Trait<T>::value;
}; };
template <template <typename...> class Trait> template <template <typename...> class Trait>
struct count_not<Trait> { struct count_not<Trait> {
static constexpr size_t size = 0; static constexpr size_t value = 0;
}; };
template <template <typename...> class Trait, typename... Ts>
constexpr size_t count_not_v = count_not<Trait, Ts...>::value;
//////////////// ////////////////
// all of // all of
//////////////// ////////////////
template <template <typename...> class Trait, typename... Ts> template <template <typename...> class Trait, typename... Ts>
struct all_of { struct all_of {
static constexpr bool value = count<Trait, Ts...>::size == sizeof...(Ts); static constexpr bool value = count_v<Trait, Ts...> == sizeof...(Ts);
}; };
template <template <typename...> class Trait, typename... Ts> template <template <typename...> class Trait, typename... Ts>
struct all_of<Trait, std::tuple<Ts...>> { struct all_of<Trait, std::tuple<Ts...>> {
static constexpr bool value = count<Trait, Ts...>::size == sizeof...(Ts); static constexpr bool value = count_v<Trait, Ts...> == sizeof...(Ts);
}; };
template <template <typename...> class Trait, typename... Ts>
constexpr bool all_of_v = all_of<Trait, Ts...>::value;
//////////////// ////////////////
// any of // any of
//////////////// ////////////////
@ -293,43 +311,52 @@ struct all_of<Trait, std::tuple<Ts...>> {
template <template <typename...> class Trait, typename... Ts> template <template <typename...> class Trait, typename... Ts>
struct any_of { struct any_of {
static_assert(sizeof...(Ts) > 0); static_assert(sizeof...(Ts) > 0);
static constexpr bool value = count<Trait, Ts...>::size > 0; static constexpr bool value = count_v<Trait, Ts...> > 0;
}; };
template <template <typename...> class Trait, typename... Ts> template <template <typename...> class Trait, typename... Ts>
struct any_of<Trait, std::tuple<Ts...>> { struct any_of<Trait, std::tuple<Ts...>> {
static_assert(sizeof...(Ts) > 0); static_assert(sizeof...(Ts) > 0);
static constexpr bool value = count<Trait, Ts...>::size > 0; static constexpr bool value = count_v<Trait, Ts...> > 0;
}; };
template <template <typename...> class Trait, typename... Ts>
constexpr bool any_of_v = any_of<Trait, Ts...>::value;
//////////////// ////////////////
// none of // none of
//////////////// ////////////////
template <template <typename...> class Trait, typename... Ts> template <template <typename...> class Trait, typename... Ts>
struct none_of { struct none_of {
static constexpr bool value = count<Trait, Ts...>::size == 0; static constexpr bool value = count_v<Trait, Ts...> == 0;
}; };
template <template <typename...> class Trait, typename... Ts> template <template <typename...> class Trait, typename... Ts>
struct none_of<Trait, std::tuple<Ts...>> { struct none_of<Trait, std::tuple<Ts...>> {
static constexpr bool value = count<Trait, Ts...>::size == 0; static constexpr bool value = count_v<Trait, Ts...> == 0;
}; };
template <template <typename...> class Trait, typename... Ts>
constexpr bool none_of_v = none_of<Trait, Ts...>::value;
//////////////// ////////////////
// is instance of // is instance of
//////////////// ////////////////
template <typename T, template <typename...> class Template> template <template <typename...> class Template, typename T>
struct is_instance_of { struct is_instance_of {
constexpr static bool value = false; constexpr static bool value = false;
}; };
template <typename... Ts, template <typename...> class Template> template <template <typename...> class Template, typename... Ts>
struct is_instance_of<Template<Ts...>, Template> { struct is_instance_of<Template, Template<Ts...>> {
constexpr static bool value = true; constexpr static bool value = true;
}; };
template <template <typename...> class Template, typename... Ts>
constexpr bool is_instance_of_v = is_instance_of<Template, Ts...>::value;
//////////////// ////////////////
// ternary // ternary
//////////////// ////////////////
@ -354,17 +381,21 @@ using ternary_t = typename ternary<B, T, U>::type;
// tuple to struct // tuple to struct
//////////////// ////////////////
template <class S, std::size_t... Is, class Tup> template <class T, std::size_t... Is, class U>
S to_object(std::index_sequence<Is...>, Tup&& tup) { T to_object_impl(std::index_sequence<Is...>, U&& data) {
return {std::get<Is>(std::forward<Tup>(tup))...}; return {std::get<Is>(std::forward<U>(data))...};
} }
// TODO Tup may not be a tuple ... template <class T, class U>
template <class S, class Tup> T to_object(U&& data) {
S to_object(Tup&& tup) { using NoRefU = std::remove_reference_t<U>;
using T = std::remove_reference_t<Tup>; if constexpr (is_instance_of_v<std::tuple, NoRefU>) {
return to_object<S>(std::make_index_sequence<std::tuple_size<T>{}>{}, return to_object_impl<
std::forward<Tup>(tup)); T>(std::make_index_sequence<std::tuple_size<NoRefU>{}>{},
std::forward<U>(data));
} else {
return T{std::forward<U>(data)};
}
} }
} /* trait */ } /* trait */

View File

@ -17,7 +17,7 @@ struct set_combinations_size {
} }
}; };
std::vector<std::string> words(const ss::split_input& input) { std::vector<std::string> words(const ss::split_data& input) {
std::vector<std::string> ret; std::vector<std::string> ret;
for (const auto& [begin, end] : input) { for (const auto& [begin, end] : input) {
ret.emplace_back(begin, end); ret.emplace_back(begin, end);
@ -88,6 +88,30 @@ auto spaced(const case_type& input, const std::string& s1,
return ret; return ret;
} }
auto spaced_left(const case_type& input, const std::string& s) {
case_type ret = input;
for (const auto& i : input) {
ret.push_back(concat(i));
ret.push_back(concat(s, i));
ret.push_back(concat(s, s, i));
ret.push_back(concat(s, s, s, i));
}
return ret;
}
auto spaced_right(const case_type& input, const std::string& s) {
case_type ret = input;
for (const auto& i : input) {
ret.push_back(concat(i));
ret.push_back(concat(i, s));
ret.push_back(concat(i, s, s));
ret.push_back(concat(i, s, s, s));
}
return ret;
}
std::vector<std::string> combinations(const std::vector<std::string>& v, std::vector<std::string> combinations(const std::vector<std::string>& v,
const std::string& delim, size_t n) { const std::string& delim, size_t n) {
if (n <= 1) { if (n <= 1) {
@ -709,3 +733,104 @@ TEST_CASE("splitter test invalid splits") {
CHECK(!s.unterminated_quote()); CHECK(!s.unterminated_quote());
CHECK(!s.error_msg().empty()); CHECK(!s.error_msg().empty());
} }
TEST_CASE("splitter test with trim_left") {
auto guard = set_combinations_size(3);
case_type case1 = spaced_left({R"(x )"}, " ");
case_type case2 = spaced_left({R"(yy )"}, " ");
case_type case3 = spaced_left({R"(y y )"}, " ");
case_type case4 = spaced_left({R"()"}, " ");
std::vector<std::string> delims = {",", "::", "\t", "\n"};
{
matches_type p{{case1, "x "},
{case2, "yy "},
{case3, "y y "},
{case4, ""}};
test_combinations<ss::trim_left<' '>>(p, delims);
}
case_type case5 = spaced_left({"z "}, "\t");
case_type case6 = spaced_left({"ab\t "}, " \t");
case_type case7 = spaced_left({"a\tb "}, " \t");
case_type case8 = spaced_left({"a \t b "}, " \t");
{
matches_type p{{case1, "x "}, {case2, "yy "}, {case3, "y y "},
{case4, ""}, {case5, "z "}, {case6, "ab\t "},
{case7, "a\tb "}, {case8, "a \t b "}};
test_combinations<ss::trim_left<' ', '\t'>>(p, {",", "::", "\n"});
}
}
TEST_CASE("splitter test with trim_right") {
auto guard = set_combinations_size(3);
case_type case1 = spaced_right({R"( x)"}, " ");
case_type case2 = spaced_right({R"( yy)"}, " ");
case_type case3 = spaced_right({R"( y y)"}, " ");
case_type case4 = spaced_right({R"()"}, " ");
std::vector<std::string> delims = {",", "::", "\t", "\n"};
{
matches_type p{{case1, " x"},
{case2, " yy"},
{case3, " y y"},
{case4, ""}};
test_combinations<ss::trim_right<' '>>(p, delims);
}
case_type case5 = spaced_right({" z"}, "\t");
case_type case6 = spaced_right({"\t ab"}, " \t");
case_type case7 = spaced_right({"\ta\tb"}, " \t");
case_type case8 = spaced_right({" \t a \t b"}, " \t");
{
matches_type p{{case1, " x"}, {case2, " yy"},
{case3, " y y"}, {case4, ""},
{case5, " z"}, {case6, "\t ab"},
{case7, "\ta\tb"}, {case8, " \t a \t b"}};
test_combinations<ss::trim_right<' ', '\t'>>(p, {",", "::", "\n"});
}
}
TEST_CASE("splitter test with trim_right and trim_left") {
auto guard = set_combinations_size(3);
case_type case1 = spaced_right({R"(-x)"}, "-");
case_type case2 = spaced_left({R"(yy_)"}, "_");
case_type case3 = spaced_right({R"(-y y)"}, "-");
case_type case4 = spaced_left({R"()"}, "-");
case_type case5 = spaced_left({R"()"}, "_");
case_type case6 = {"___---", "_-", "______-"};
std::vector<std::string> delims = {",", "::", "\t", "\n"};
{
matches_type p{{case1, "-x"}, {case2, "yy_"}, {case3, "-y y"},
{case4, ""}, {case5, ""}, {case6, ""}};
test_combinations<ss::trim_left<'_'>, ss::trim_right<'-'>>(p, delims);
}
}
TEST_CASE("splitter test with quote and escape, trim_left and trim_right") {
auto guard = set_combinations_size(3);
case_type case1 = spaced_left({R"("\"")", R"(\")", R"("""")"}, "_");
case_type case2 =
spaced_left({R"("x\"x")", R"(x\"x)", R"(x"x)", R"("x""x")"}, "_");
case_type case3 = spaced_left({R"("")", R"()"}, "_");
case_type case4 = spaced_left({R"("x")", R"(x)"}, "_");
case_type case5 =
spaced_right({R"("\"\"")", R"("""""")", R"("\"""")", R"("""\"")"}, "-");
case_type case6 = spaced_right({R"("\\")", R"(\\)"}, "-");
case_type case7 = spaced_right({R"("xxxxxxxxxx")", R"(xxxxxxxxxx)"}, "-");
std::vector<std::string> delims = {"::", "\n"};
{
matches_type p{{case1, "\""}, {case2, "x\"x"}, {case3, ""},
{case5, "\"\""}, {case6, "\\"}, {case7, "xxxxxxxxxx"}};
test_combinations<ss::quote<'"'>, ss::escape<'\\'>, ss::trim_left<'_'>,
ss::trim_right<'-'>>(p, delims);
}
}