Info
-
Did you know that Compile Time Regular Expressions support extracting matches?
Example
int main() {
using namespace ctre::literals;
if (const auto match = ctre::match<"([0-9]+)">("42")) {
std::cout << match.get<1>().to_view(); // prints 42
}
}
Puzzle
- Can you implement
parse_args
function which returns an initialized typeT
based on their patterns?
template<ctll::fixed_string Pattern, class T>
struct arg {
static constexpr auto pattern = Pattern;
constexpr operator auto() const { return value; }
T value;
};
template<class T>
constexpr auto parse_args(std::string_view input) -> T {
return {}; // TODO
}
struct empty { };
struct args1 {
arg<"num=([0-9]*)", int> num;
};
struct args2 {
arg<"quant=([0-9]*)", int> quant;
arg<"lab=([0-9]*)", int> lab;
};
struct args3 {
arg<"quant=([^\\s]*)", std::string_view> quant;
arg<"lab=([^\\s]*)", std::string_view> lab;
arg<"num=([0-9]*)", int> num;
};
static_assert(std::is_same_v<empty, decltype(parse_args<empty>(""))>);
static_assert(42 == parse_args<args1>("num=42").num);
static_assert(4 == parse_args<args2>("quant=4 lab=2").quant);
static_assert(2 == parse_args<args2>("quant=4 lab=2").lab);
using std::literals::string_view_literals::operator""sv;
static_assert("Quant"sv == parse_args<args3>("quant=Quant lab=Lab num=42").quant);
static_assert("Lab"sv == parse_args<args3>("quant=Quant lab=Lab num=42").lab);
static_assert(42 == parse_args<args3>("quant=Quant lab=Lab num=42").num);
Solutions
namespace detail {
template <typename T>
constexpr auto to_value(std::string_view sv) {
return T{sv};
}
template <std::integral T>
constexpr auto to_value(std::string_view sv) {
T ret{};
nonstd::from_chars(std::data(sv), std::data(sv) + std::size(sv), ret);
return ret;
}
template <class T>
constexpr auto parse_arg(std::string_view input) {
if (const auto [whole, val] = ctre::search<T::pattern>(input); whole) {
return T{.value = to_value<decltype(T::value)>(val)};
} else {
return T{};
}
}
template <class T, template <class...> class TList, class... Args>
constexpr auto parse_args(std::string_view input, TList<Args...>) {
return T{parse_arg<Args>(input)...};
}
} // namespace detail
template<class T>
constexpr auto parse_args(std::string_view input) -> T {
return detail::parse_args<T>(input, detail::members_list_t<T>{});
}
template<class T>
constexpr auto parse_args(std::string_view input) -> T {
return [input]<auto... Ns>(std::index_sequence<Ns...>) {
constexpr auto tuple = to_tuple(T{});
constexpr auto pattern = ((std::get<Ns>(tuple).pattern + ctll::fixed_string("\\s*")) + ...);
const auto match = ctre::match<pattern>(input);
return T{
[]<class TValue>(auto arg) {
if constexpr (std::is_integral_v<TValue>) {
return stoi(arg);
} else {
return arg;
}
}.template operator()<decltype(std::get<Ns>(tuple).value)>(match.template get<Ns + 1>().to_view())...
};
}(std::make_index_sequence<std::tuple_size_v<decltype(to_tuple(T{}))>>{});
}
template <class T>
constexpr auto parse_args(std::string_view input)
{
const auto extract_match = [&input]<typename TArg>() {
const auto [_, match] = ctre::search<TArg::pattern>(input);
if constexpr (std::is_integral_v<typename TArg::value_t>) {
return detail::stoi(match);
} else {
return match;
}
};
auto output = T {};
if constexpr (requires { output.num; }) {
output.num = extract_match.template operator()<decltype(T {}.num)>();
}
if constexpr (requires { output.quant; }) {
output.quant = extract_match.template operator()<decltype(T {}.quant)>();
}
if constexpr (requires { output.lab; }) {
output.lab = extract_match.template operator()<decltype(T {}.lab)>();
}
return output;
}
namespace detail {
template <typename>
constexpr auto parse_arg(auto arg) {
return arg;
}
template <typename T> requires std::is_integral_v<T>
constexpr auto parse_arg(auto arg) {
return stoi(arg);
}
} // namespace detail
template <typename T>
constexpr auto parse_args(std::string_view input) {
constexpr auto members = detail::make_members_tuple<T>();
auto expand_args = [&]<auto... Ns>(std::index_sequence<Ns...>) -> T {
return {
detail::parse_arg<decltype(std::get<Ns>(members).value)>(
ctre::search<std::get<Ns>(members).pattern>(input)
.template get<1>()
.to_view())...};
};
return expand_args(
std::make_index_sequence<std::tuple_size_v<decltype(members)>>{});
}
template<class T>
constexpr T fromSV(std::string_view input);
template<>
constexpr std::string_view fromSV<std::string_view>(std::string_view input) {
return input;
}
template<>
constexpr int fromSV<int>(std::string_view input) {
int v = 0;
for(size_t i=0; i<input.size(); i++)
v = v*10 + (input[i]-'0');
return v;
}
template<ctll::fixed_string Pattern, class T>
struct arg {
constexpr arg(std::string_view input) {
if (const auto match = ctre::search<pattern>(input))
value = fromSV<T>( match.template get<1>().to_view());
else
value = {};
};
static constexpr auto pattern = Pattern;
constexpr operator auto() const { return value; }
T value;
};
template<class T>
constexpr auto parse_args(std::string_view input) -> T {
using sv = std::string_view;
if constexpr (std::is_constructible<T, sv, sv, sv>::value)
return {input,input,input};
if constexpr (std::is_constructible<T, sv, sv>::value)
return {input, input};
if constexpr (std::is_constructible<T, sv>::value)
return {input};
}
struct empty { };
template<>
constexpr auto parse_args<empty>(std::string_view input) -> empty {
return empty{};
}