serializing/deserializing functions
This commit is contained in:
@@ -21,7 +21,7 @@ namespace adc::traits
|
|||||||
// https://stackoverflow.com/questions/72430369/how-to-check-that-a-type-is-formattable-using-type-traits-concepts)
|
// https://stackoverflow.com/questions/72430369/how-to-check-that-a-type-is-formattable-using-type-traits-concepts)
|
||||||
template <typename T>
|
template <typename T>
|
||||||
concept formattable =
|
concept formattable =
|
||||||
requires(T& v, std::format_context ctx) { std::formatter<std::remove_cvref_t<T>>().format(v, ctx); };
|
requires(T v, std::format_context ctx) { std::formatter<std::remove_cvref_t<T>>().format(v, ctx); };
|
||||||
|
|
||||||
|
|
||||||
// range of char/const char
|
// range of char/const char
|
||||||
@@ -37,7 +37,8 @@ concept adc_output_char_range = std::ranges::output_range<R, CharT>;
|
|||||||
|
|
||||||
// range of char/const char
|
// range of char/const char
|
||||||
template <typename R, typename CharT = char>
|
template <typename R, typename CharT = char>
|
||||||
concept adc_input_char_range = std::ranges::input_range<CharT>;
|
concept adc_input_char_range = std::ranges::input_range<R>;
|
||||||
|
// concept adc_input_char_range = std::ranges::input_range<CharT>;
|
||||||
|
|
||||||
|
|
||||||
// deduce returned type of callable
|
// deduce returned type of callable
|
||||||
@@ -106,4 +107,28 @@ constexpr static auto adc_pf_wrapper(T&& v, Ts&&... vs)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// check if type T is one of types in sequence Rest (e.g. adc_is_any<int,char,float,int,std::string>::value == true)
|
||||||
|
|
||||||
|
template <typename T, typename... Rest>
|
||||||
|
struct adc_is_any_of : std::disjunction<std::is_same<T, Rest>...> {
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename... Rest>
|
||||||
|
static inline constexpr bool adc_is_any_of_v = std::disjunction_v<std::is_same<T, Rest>...>;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct adc_is_tuple : std::false_type {
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename... Ts>
|
||||||
|
struct adc_is_tuple<std::tuple<Ts...>> : std::true_type {
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static inline constexpr bool adc_is_tuple_v = adc_is_tuple<T>::value;
|
||||||
|
|
||||||
} // namespace adc::traits
|
} // namespace adc::traits
|
||||||
|
|||||||
@@ -30,18 +30,27 @@ static auto AdcTrimSpaces(R&& r)
|
|||||||
template <typename ValueT, traits::adc_char_range R>
|
template <typename ValueT, traits::adc_char_range R>
|
||||||
static ValueT AdcFromChars(R&& range)
|
static ValueT AdcFromChars(R&& range)
|
||||||
{
|
{
|
||||||
if constexpr (std::is_arithmetic_v<ValueT>) {
|
using v_t = std::remove_cv_t<ValueT>;
|
||||||
ValueT v;
|
|
||||||
|
|
||||||
|
#ifdef _LIBCPP_VERSION // clang's libc++ does not have floating-point overloads for std::from_chars
|
||||||
|
if constexpr (traits::adc_is_any_v<v_t, char, int, long, long long, __int128>) {
|
||||||
|
#else
|
||||||
|
if constexpr (traits::adc_is_any_of_v<v_t, char, int, long, long long, __int128, float, double, long double>) {
|
||||||
|
#endif
|
||||||
|
v_t v;
|
||||||
|
|
||||||
|
auto rr = AdcTrimSpaces(range);
|
||||||
|
|
||||||
std::from_chars_result res;
|
std::from_chars_result res;
|
||||||
const char* end_ptr;
|
const char* end_ptr;
|
||||||
|
|
||||||
// std::from_chars needs random-access container!!!
|
// std::from_chars needs random-access container!!!
|
||||||
if constexpr (std::ranges::random_access_range<R>) {
|
if constexpr (std::ranges::random_access_range<R>) {
|
||||||
res = std::from_chars(&*range.begin(), &*range.end(), v);
|
res = std::from_chars(&*rr.begin(), &*rr.end(), v);
|
||||||
end_ptr = &*range.end();
|
end_ptr = &*rr.end();
|
||||||
} else {
|
} else {
|
||||||
std::string s(range.begin(), range.end());
|
std::string s(rr.begin(), rr.end());
|
||||||
end_ptr = s.data() + s.size();
|
end_ptr = s.data() + s.size();
|
||||||
res = std::from_chars(s.data(), s.data() + s.size(), v);
|
res = std::from_chars(s.data(), s.data() + s.size(), v);
|
||||||
}
|
}
|
||||||
@@ -60,7 +69,30 @@ static ValueT AdcFromChars(R&& range)
|
|||||||
throw std::invalid_argument(
|
throw std::invalid_argument(
|
||||||
"AdcFromChars: cannot convert char-range to user-type value (result out of range)");
|
"AdcFromChars: cannot convert char-range to user-type value (result out of range)");
|
||||||
}
|
}
|
||||||
} else if (std::is_constructible_v<ValueT, std::ranges::iterator_t<R>, std::ranges::iterator_t<R>>) {
|
#ifdef _LIBCPP_VERSION // clang's libc++ does not have floating-point overloads for std::from_chars
|
||||||
|
} else if constexpr (std::is_floating_point_v<v_t>) {
|
||||||
|
v_t v;
|
||||||
|
|
||||||
|
auto rr = AdcTrimSpaces(range);
|
||||||
|
|
||||||
|
std::string s(rr.begin(), rr.end());
|
||||||
|
size_t pos;
|
||||||
|
if constexpr (std::is_same_v<v_t, float>) {
|
||||||
|
v = std::stof(s, &pos);
|
||||||
|
} else if constexpr (std::is_same_v<v_t, double>) {
|
||||||
|
v = std::stod(s, &pos);
|
||||||
|
} else if constexpr (std::is_same_v<v_t, long double>) {
|
||||||
|
v = std::stold(s, &pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos != s.size()) {
|
||||||
|
throw std::invalid_argument(
|
||||||
|
"AdcFromChars: cannot convert char-range to user-type value (invalid argument)");
|
||||||
|
}
|
||||||
|
|
||||||
|
return v;
|
||||||
|
#endif
|
||||||
|
} else if constexpr (std::is_constructible_v<ValueT, std::ranges::iterator_t<R>, std::ranges::iterator_t<R>>) {
|
||||||
return ValueT(range.begin(), range.end());
|
return ValueT(range.begin(), range.end());
|
||||||
} else {
|
} else {
|
||||||
static_assert(false,
|
static_assert(false,
|
||||||
@@ -68,6 +100,8 @@ static ValueT AdcFromChars(R&& range)
|
|||||||
// throw std::invalid_argument(
|
// throw std::invalid_argument(
|
||||||
// "AdcFromChars: cannot convert char-range to user-type value (unsupported user-value type)");
|
// "AdcFromChars: cannot convert char-range to user-type value (unsupported user-value type)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ValueT();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -76,14 +110,14 @@ static ValueT AdcFromChars(R&& range)
|
|||||||
static const std::regex AdcIntegerRegex("^ *[+-]?\\d+\\d* *$", std::regex::ECMAScript);
|
static const std::regex AdcIntegerRegex("^ *[+-]?\\d+\\d* *$", std::regex::ECMAScript);
|
||||||
static const std::regex AdcRealRegex("^ *[-+]?\\d+\\d*\\.?\\d*([Ee][-+]?\\d+)? *$", std::regex::ECMAScript);
|
static const std::regex AdcRealRegex("^ *[-+]?\\d+\\d*\\.?\\d*([Ee][-+]?\\d+)? *$", std::regex::ECMAScript);
|
||||||
|
|
||||||
template <traits::formattable VT, typename SerializedT = std::string>
|
template <typename SerializedT, traits::formattable VT>
|
||||||
static SerializedT AdcTrivialSerializer(VT&& value)
|
static SerializedT AdcTrivialSerializer(VT&& value)
|
||||||
{
|
{
|
||||||
using v_t = std::decay_t<VT>;
|
using v_t = std::decay_t<VT>;
|
||||||
|
|
||||||
if constexpr (std::is_convertible_v<v_t, SerializedT>) {
|
if constexpr (std::is_convertible_v<v_t, SerializedT>) {
|
||||||
return static_cast<SerializedT>(std::forward<VT>(value));
|
return static_cast<SerializedT>(std::forward<VT>(value));
|
||||||
} else if (traits::adc_output_char_range<SerializedT>) {
|
} else if constexpr (traits::adc_output_char_range<SerializedT>) {
|
||||||
SerializedT s_val;
|
SerializedT s_val;
|
||||||
std::format_to(std::back_inserter(s_val), "{}", std::forward<VT>(value));
|
std::format_to(std::back_inserter(s_val), "{}", std::forward<VT>(value));
|
||||||
return s_val;
|
return s_val;
|
||||||
@@ -99,7 +133,7 @@ static VT AdcTrivialDeserializer(SerializedT&& s_value)
|
|||||||
{
|
{
|
||||||
if constexpr (std::is_convertible_v<SerializedT, VT>) {
|
if constexpr (std::is_convertible_v<SerializedT, VT>) {
|
||||||
return static_cast<VT>(std::forward<SerializedT>(s_value));
|
return static_cast<VT>(std::forward<SerializedT>(s_value));
|
||||||
} else if (traits::adc_input_char_range<SerializedT>) {
|
} else if constexpr (traits::adc_input_char_range<SerializedT>) {
|
||||||
return AdcFromChars<VT>(std::forward<SerializedT>(s_value));
|
return AdcFromChars<VT>(std::forward<SerializedT>(s_value));
|
||||||
} else {
|
} else {
|
||||||
static_assert(false, "TRIVIAL DESERIALIZER: UNSUPPORTED SERIALIZING TYPE!!!");
|
static_assert(false, "TRIVIAL DESERIALIZER: UNSUPPORTED SERIALIZING TYPE!!!");
|
||||||
@@ -108,14 +142,13 @@ static VT AdcTrivialDeserializer(SerializedT&& s_value)
|
|||||||
|
|
||||||
|
|
||||||
template <std::ranges::range OutputR, traits::adc_input_char_range InputR, traits::adc_input_char_range DelimR>
|
template <std::ranges::range OutputR, traits::adc_input_char_range InputR, traits::adc_input_char_range DelimR>
|
||||||
OutputR AdcValueRangeFromCharRange(InputR&& input_r,
|
static auto AdcValueRangeFromCharRange(OutputR& res,
|
||||||
DelimR&& delim,
|
InputR&& input_r,
|
||||||
size_t max_len = std::numeric_limits<size_t>::max())
|
DelimR&& delim,
|
||||||
|
size_t max_len = std::numeric_limits<size_t>::max())
|
||||||
{
|
{
|
||||||
OutputR res;
|
|
||||||
|
|
||||||
if (max_len == 0) {
|
if (max_len == 0) {
|
||||||
return res;
|
return res.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
using value_t = std::ranges::range_value_t<OutputR>;
|
using value_t = std::ranges::range_value_t<OutputR>;
|
||||||
@@ -134,21 +167,20 @@ OutputR AdcValueRangeFromCharRange(InputR&& input_r,
|
|||||||
std::back_inserter(res));
|
std::back_inserter(res));
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template <traits::adc_output_char_range OutputR, std::ranges::range InputR, traits::adc_input_char_range DelimR>
|
template <traits::adc_output_char_range OutputR, std::ranges::range InputR, traits::adc_input_char_range DelimR>
|
||||||
OutputR AdcCharRangeFromValueRange(InputR&& input_r,
|
static auto AdcCharRangeFromValueRange(OutputR& res,
|
||||||
DelimR&& delim,
|
InputR&& input_r,
|
||||||
size_t max_len = std::numeric_limits<size_t>::max())
|
DelimR&& delim,
|
||||||
|
size_t max_len = std::numeric_limits<size_t>::max())
|
||||||
{
|
{
|
||||||
OutputR res;
|
|
||||||
|
|
||||||
max_len = max_len < std::ranges::size(input_r) ? max_len : std::ranges::size(input_r);
|
max_len = max_len < std::ranges::size(input_r) ? max_len : std::ranges::size(input_r);
|
||||||
|
|
||||||
if (max_len == 0) {
|
if (max_len == 0) {
|
||||||
return res;
|
return res.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
--max_len;
|
--max_len;
|
||||||
@@ -173,7 +205,73 @@ OutputR AdcCharRangeFromValueRange(InputR&& input_r,
|
|||||||
res = AdcTrivialSerializer<OutputR>(*std::forward<InputR>(input_r).begin());
|
res = AdcTrivialSerializer<OutputR>(*std::forward<InputR>(input_r).begin());
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
|
||||||
|
template <size_t I = 0, typename... Ts>
|
||||||
|
void setTupleElements(std::tuple<Ts...>& tp, auto&& r)
|
||||||
|
{
|
||||||
|
if (I < std::ranges::distance(r)) {
|
||||||
|
if constexpr (I < sizeof...(Ts)) {
|
||||||
|
std::get<I>(tp) =
|
||||||
|
AdcFromChars<std::tuple_element_t<I, std::tuple<Ts...>>>(*(r | std::views::drop(I)).begin());
|
||||||
|
|
||||||
|
setTupleElements<I + 1>(tp, std::forward<decltype(r)>(r));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <size_t I = 0, typename... Ts>
|
||||||
|
void fromTupleElements(const std::tuple<Ts...>& tp, auto& r, const auto& delim)
|
||||||
|
{
|
||||||
|
using r_t = std::decay_t<decltype(r)>;
|
||||||
|
|
||||||
|
if constexpr (I < (sizeof...(Ts) - 1)) {
|
||||||
|
auto el = AdcTrivialSerializer<r_t>(std::get<I>(tp));
|
||||||
|
std::ranges::copy(el, std::back_inserter(r));
|
||||||
|
std::ranges::copy(delim, std::back_inserter(r));
|
||||||
|
|
||||||
|
fromTupleElements<I + 1>(tp, r, delim);
|
||||||
|
} else if constexpr (I == (sizeof...(Ts) - 1)) {
|
||||||
|
auto el = AdcTrivialSerializer<r_t>(std::get<I>(tp));
|
||||||
|
std::ranges::copy(el, std::back_inserter(r));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
|
||||||
|
template <traits::adc_input_char_range InputR, traits::adc_input_char_range DelimR, typename... Ts>
|
||||||
|
static void AdcTupleFromCharRange(std::tuple<Ts...>& tp, InputR&& input_r, DelimR&& delim)
|
||||||
|
{
|
||||||
|
if constexpr (std::is_array_v<std::remove_cvref_t<DelimR>>) {
|
||||||
|
auto r = std::views::split(std::forward<InputR>(input_r), std::string_view(delim)) |
|
||||||
|
std::views::filter([](const auto& r) { return !std::ranges::empty(r); });
|
||||||
|
details::setTupleElements(tp, r);
|
||||||
|
} else {
|
||||||
|
auto r = std::views::split(std::forward<InputR>(input_r), std::forward<DelimR>(delim)) |
|
||||||
|
std::views::filter([](const auto& r) { return !std::ranges::empty(r); });
|
||||||
|
details::setTupleElements(tp, r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <traits::adc_output_char_range OutputR, traits::adc_input_char_range DelimR, typename... Ts>
|
||||||
|
static auto AdcCharRangeFromTuple(OutputR& res, const std::tuple<Ts...>& tp, DelimR&& delim)
|
||||||
|
{
|
||||||
|
if constexpr (sizeof...(Ts) > 1) {
|
||||||
|
details::fromTupleElements(tp, res, delim);
|
||||||
|
} else {
|
||||||
|
res = AdcTrivialSerializer<OutputR>(std::get<0>(tp));
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace adc::utils
|
} // namespace adc::utils
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
|
#include <list>
|
||||||
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
||||||
#include <doctest/doctest.h>
|
#include <doctest/doctest.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "../common/adc_utils.h"
|
||||||
#include "../common/adc_value_holder.h"
|
#include "../common/adc_value_holder.h"
|
||||||
|
|
||||||
TEST_CASE("[ADC VALUEHOLDER]")
|
TEST_CASE("[ADC VALUEHOLDER]")
|
||||||
@@ -83,4 +84,41 @@ TEST_CASE("[ADC VALUEHOLDER]")
|
|||||||
using namespace std::literals;
|
using namespace std::literals;
|
||||||
|
|
||||||
CHECK_THROWS_WITH_AS(vsh = ""s, "user value is not valid", std::system_error);
|
CHECK_THROWS_WITH_AS(vsh = ""s, "user value is not valid", std::system_error);
|
||||||
|
|
||||||
|
std::string ss = "\t\t1, 2, 3, 4,5 ";
|
||||||
|
|
||||||
|
std::vector<int> vv;
|
||||||
|
adc::utils::AdcValueRangeFromCharRange(vv, ss, ",");
|
||||||
|
|
||||||
|
for (auto v : vv) {
|
||||||
|
std::cout << "v = " << v << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<char> ll;
|
||||||
|
adc::utils::AdcCharRangeFromValueRange(ll, vv, "<>");
|
||||||
|
for (auto ch : ll) {
|
||||||
|
std::cout << ch;
|
||||||
|
}
|
||||||
|
std::cout << "\n";
|
||||||
|
|
||||||
|
ss.clear();
|
||||||
|
adc::utils::AdcCharRangeFromValueRange(ss, vv, "<>");
|
||||||
|
std::cout << "STR: [" << ss << "]\n";
|
||||||
|
|
||||||
|
|
||||||
|
std::tuple<int, float> tp{7, 77.77};
|
||||||
|
ss = "3, 33.33";
|
||||||
|
adc::utils::AdcTupleFromCharRange(tp, ss, ",");
|
||||||
|
|
||||||
|
std::cout << "TP: {" << std::get<0>(tp) << ", " << std::get<1>(tp) << "}\n";
|
||||||
|
|
||||||
|
std::get<1>(tp) = 77.77;
|
||||||
|
|
||||||
|
ll.clear();
|
||||||
|
adc::utils::AdcCharRangeFromTuple(ll, tp, "::");
|
||||||
|
|
||||||
|
for (auto ch : ll) {
|
||||||
|
std::cout << ch;
|
||||||
|
}
|
||||||
|
std::cout << "\n";
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user