#pragma once #include "type_traits.hpp" #include <array> namespace ss { //////////////// // matcher //////////////// template <char... Cs> struct matcher { private: template <char X, char... Xs> static bool match_impl(char c) { if constexpr (sizeof...(Xs) != 0) { return (c == X) || match_impl<Xs...>(c); } return (c == X); } constexpr static bool contains_string_terminator() { for (const auto& match : matches) { if (match == '\0') { return false; } } return true; } public: static bool match(char c) { return match_impl<Cs...>(c); } constexpr static bool enabled = true; constexpr static std::array<char, sizeof...(Cs)> matches{Cs...}; static_assert(contains_string_terminator(), "string terminator cannot be used as a match character"); }; template <typename FirstMatcher, typename SecondMatcher> inline constexpr bool matches_intersect() { for (const auto& first_match : FirstMatcher::matches) { for (const auto& second_match : SecondMatcher::matches) { if (first_match != '\0' && first_match == second_match) { return true; } } } 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 <> class matcher<'\0'> { public: constexpr static bool enabled = false; constexpr static std::array<char, 1> matches{'\0'}; static bool match(char c) = delete; }; //////////////// // setup //////////////// //////////////// // matcher //////////////// template <char C> struct quote : matcher<C> {}; template <char... Cs> struct trim : matcher<Cs...> {}; template <char... Cs> struct trim_left : matcher<Cs...> {}; template <char... Cs> struct trim_right : matcher<Cs...> {}; template <char... Cs> struct escape : matcher<Cs...> {}; template <typename T, template <char...> class Template> struct is_instance_of_matcher : std::false_type {}; template <char... Ts, template <char...> class Template> struct is_instance_of_matcher<Template<Ts...>, Template> : std::true_type {}; 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> struct get_matcher; template <template <char...> class Matcher, typename T, typename... Ts> struct get_matcher<Matcher, T, Ts...> { template <typename U> struct is_matcher : is_instance_of_matcher<U, Matcher> {}; static_assert(count_v<is_matcher, T, Ts...> <= 1, "the same matcher cannot " "be defined multiple times"); using type = std::conditional_t<is_matcher<T>::value, T, typename get_matcher<Matcher, Ts...>::type>; }; template <template <char...> class Matcher> struct get_matcher<Matcher> { using type = Matcher<'\0'>; }; template <template <char...> class Matcher, typename... Ts> using get_matcher_t = typename get_matcher<Matcher, Ts...>::type; //////////////// // multiline //////////////// template <size_t S, bool B = true> struct multiline_restricted { constexpr static auto size = S; constexpr static auto enabled = B; }; using multiline = multiline_restricted<0>; template <typename T> struct is_instance_of_multiline : std::false_type {}; template <size_t S, bool B> struct is_instance_of_multiline<multiline_restricted<S, B>> : std::true_type {}; template <typename T> using is_instance_of_multiline_t = typename is_instance_of_multiline<T>::type; template <typename... Ts> struct get_multiline; template <typename T, typename... Ts> struct get_multiline<T, Ts...> { using type = std::conditional_t<is_instance_of_multiline<T>::value, T, typename get_multiline<Ts...>::type>; }; template <> struct get_multiline<> { using type = multiline_restricted<0, false>; }; template <typename... Ts> using get_multiline_t = typename get_multiline<Ts...>::type; //////////////// // string_error //////////////// class string_error {}; //////////////// // ignore_header //////////////// class ignore_header {}; //////////////// // ignore_empty //////////////// class ignore_empty {}; //////////////// // throw_on_error //////////////// class throw_on_error {}; //////////////// // setup implementation //////////////// template <typename... Options> struct setup { private: template <typename Option> struct is_matcher : std::disjunction<is_instance_of_matcher_t<Option, quote>, is_instance_of_matcher_t<Option, escape>, is_instance_of_matcher_t<Option, trim>, is_instance_of_matcher_t<Option, trim_left>, is_instance_of_matcher_t<Option, trim_right>> {}; template <typename T> struct is_string_error : std::is_same<T, string_error> {}; template <typename T> struct is_ignore_header : std::is_same<T, ignore_header> {}; template <typename T> struct is_ignore_empty : std::is_same<T, ignore_empty> {}; template <typename T> struct is_throw_on_error : std::is_same<T, throw_on_error> {}; constexpr static auto count_matcher = count_v<is_matcher, Options...>; constexpr static auto count_multiline = count_v<is_instance_of_multiline, Options...>; constexpr static auto count_string_error = count_v<is_string_error, Options...>; constexpr static auto count_ignore_header = count_v<is_ignore_header, Options...>; constexpr static auto count_throw_on_error = count_v<is_throw_on_error, Options...>; constexpr static auto count_ignore_empty = count_v<is_ignore_empty, Options...>; constexpr static auto number_of_valid_setup_types = count_matcher + count_multiline + count_string_error + count_ignore_header + count_ignore_empty + count_throw_on_error; using trim_left_only = get_matcher_t<trim_left, Options...>; using trim_right_only = get_matcher_t<trim_right, Options...>; using trim_all = get_matcher_t<trim, Options...>; public: using quote = get_matcher_t<quote, Options...>; using escape = get_matcher_t<escape, Options...>; using trim_left = std::conditional_t<trim_all::enabled, trim_all, trim_left_only>; using trim_right = std::conditional_t<trim_all::enabled, trim_all, trim_right_only>; using multiline = get_multiline_t<Options...>; constexpr static bool string_error = (count_string_error == 1); constexpr static bool ignore_header = (count_ignore_header == 1); constexpr static bool ignore_empty = (count_ignore_empty == 1); constexpr static bool throw_on_error = (count_throw_on_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( !multiline::enabled || (multiline::enabled && (quote::enabled || escape::enabled)), "to enable multiline either quote or escape needs to be enabled"); static_assert(!(trim_all::enabled && trim_left_only::enabled) && !(trim_all::enabled && trim_right_only::enabled), "ambiguous trim setup"); static_assert(count_multiline <= 1, "mutliline defined multiple times"); static_assert(count_string_error <= 1, "string_error defined multiple times"); static_assert(count_throw_on_error <= 1, "throw_on_error defined multiple times"); static_assert(count_throw_on_error + count_string_error <= 1, "cannot define both throw_on_error and string_error"); static_assert(number_of_valid_setup_types == sizeof...(Options), "one or multiple invalid setup parameters defined"); }; template <typename... Options> struct setup<setup<Options...>> : setup<Options...> {}; } /* ss */