This commit is contained in:
2026-06-18 18:42:34 +03:00
parent a95a3a5d1b
commit e1b479a7ef
7 changed files with 719 additions and 17 deletions

View File

@@ -6,8 +6,10 @@ option(BUILD_EXAMPLES "Build examples" ON)
set(LIB_HEADERS
include/snipplib/concepts/snplib_concepts.h
include/snipplib/utils/snplib_hash.h
include/snipplib/utils/snplib_string.h
include/snipplib/utils/snplib_utils.h
include/snipplib/containers/snplib_hmap.h
include/snipplib/serialization/snplib_serialization.h
)
add_library(${PROJECT_NAME} INTERFACE ${LIB_HEADERS})

View File

@@ -104,7 +104,14 @@ int main()
if (ri) { // should not be
std::println("hmap_move['int_key'] = {}", ri.value());
} else {
std::println("cannot get value of hmap_move['int_key']");
std::println("cannot get value of hmap_move['int_key'] as int");
}
auto rd = hmap_move.get<double>("int_key");
if (rd) { // should not be
std::println("hmap_move['int_key'] = {}", rd.value());
} else {
std::println("cannot get value of hmap_move['int_key'] as double");
}
ri = hmap_copy.get<int>("int_key");
@@ -115,5 +122,11 @@ int main()
}
}
std::println("\n{:*^80}", " hmap size ");
std::println("hmap.size() = {}", hmap.size());
std::println("hmap_move.size() = {}", hmap_move.size());
std::println("hmap_copy.size() = {}", hmap_copy.size());
return 0;
}

View File

@@ -7,13 +7,15 @@
namespace snplib
{
// type T is hashable
/* type T is hashable */
template <typename T>
concept snplib_hashable_c = requires(T t) {
{ std::hash<T>{}(t) } -> std::convertible_to<size_t>;
};
// some concepts for various types of char sequence
/* some concepts for various types of char sequence */
template <typename T, typename CharT = char>
concept snplib_char_range_c = std::ranges::range<T> && std::same_as<std::ranges::range_value_t<T>, CharT>;
@@ -27,7 +29,7 @@ template <typename T, typename CharT = char>
concept snplib_char_view_c = std::ranges::view<T> && std::same_as<std::ranges::range_value_t<T>, CharT>;
// std::chrono related concepts
/* std::chrono related concepts */
template <typename T>
concept snplib_time_duration_c = requires(T t) {
@@ -50,11 +52,104 @@ concept snplib_systime_point_c = snplib_time_point_c<T, std::chrono::system_cloc
template <typename T>
concept snplib_utctime_point_c = snplib_time_point_c<T, std::chrono::utc_clock>;
template <typename T>
concept snplib_anytime_point_c =
snplib_systime_point_c<T> || snplib_utctime_point_c<T> || snplib_time_point_c<T, std::chrono::tai_clock> ||
snplib_time_point_c<T, std::chrono::gps_clock> || snplib_time_point_c<T, std::chrono::file_clock> ||
snplib_time_point_c<T, std::chrono::local_t>;
// callables (does not working for templated functions/methods and generic lambdas!)
/* callables (NOTE: does not working for templated functions/methods and generic lambdas!) */
template <typename T>
concept snplib_callable_c = std::is_function_v<T> || (std::is_object_v<T> && requires(T) { &T::operator(); });
/* std::tuple-like types */
namespace details
{
template <typename T, size_t... Is>
concept snplib_get_all_indices_c = requires(T t) { (std::get<Is>(t), ...); };
template <typename T>
struct snplib_check_indices_t {
static constexpr bool value = false;
};
// Specialize to extract the index sequence matching the tuple size
template <typename T>
requires requires { typename std::tuple_size<T>::type; }
struct snplib_check_indices_t<T> {
static constexpr bool value = []<std::size_t... Is>(std::index_sequence<Is...>) {
return snplib_get_all_indices_c<T, Is...>;
}(std::make_index_sequence<std::tuple_size<T>::value>{});
};
template <typename T>
static constexpr bool snplib_check_indices_v = snplib_check_indices_t<T>::value;
} // namespace details
template <typename T>
concept snplib_tuple_like_c = details::snplib_check_indices_v<std::remove_cvref_t<T>>;
/* fixed-size and non-resizable ranges */
// std::array, std::span(v_t, N), std::tuple
template <typename T>
concept snplib_fixed_size_range_c =
std::ranges::range<T> && requires { std::tuple_size<std::remove_cvref_t<T>>::value; };
// non-resizable ranges
template <typename T>
concept snplib_non_resizable_range_c = std::ranges::range<T> && !requires(T r, std::size_t n) { r.resize(n); };
/* an concept for error-type */
//
// a default-constructible type wich can be converted to bool-type
// NOTE: in the SNIPPETS LIBRARY it is assumed that default constructed
// objects of the 'snplib_error_c' types are converted to 'false', i.e.:
//
// T v;
// bool ok = v == false; // 'ok' must be 'true'
//
//
template <typename T>
concept snplib_error_c = std::constructible_from<T> && requires(T&& t) {
static_cast<bool>(std::forward<T>(t));
{ !std::forward<T>(t) } -> std::convertible_to<bool>;
};
// utility function to deduce error-type
template <snplib_error_c ErrT, snplib_error_c FallbackErrT>
static auto snplib_deduced_error(ErrT const& err, FallbackErrT const& fallback_err)
{
if constexpr (std::same_as<ErrT, FallbackErrT>) {
return err;
} else if constexpr (std::is_error_code_enum_v<FallbackErrT>) { // special case
if constexpr (std::same_as<ErrT, std::error_code>) {
return err;
} else {
return fallback_err;
}
} else if constexpr (std::is_error_condition_enum_v<FallbackErrT>) { // special case
if constexpr (std::same_as<ErrT, std::error_condition>) {
return err;
} else {
return fallback_err;
}
} else {
return fallback_err;
}
}
} // namespace snplib

View File

@@ -36,6 +36,7 @@ protected:
inline static std::vector<std::function<void(const HeterogenMap*, HeterogenMap*)>> _copyFunc{};
inline static std::vector<std::function<void(HeterogenMap*)>> _clearFunc{};
inline static std::vector<std::function<size_t(const HeterogenMap*)>> _sizeFunc{};
inline static std::vector<std::function<bool(KeyT const&, const HeterogenMap*)>> _eraseFunc{};
inline static std::vector<std::function<bool(KeyT const&, const HeterogenMap*)>> _containsFunc{};
@@ -119,6 +120,18 @@ public:
}
}
size_t size() const
{
size_t N = 0;
for (auto& func : _sizeFunc) {
N += func(this);
}
return N;
}
template <typename VT>
bool push(KeyT const& key, VT&& value)
{
@@ -156,6 +169,8 @@ public:
_setter<v_t>[obj].clear();
});
_sizeFunc.emplace_back([](const HeterogenMap* obj) { return _values<v_t>[obj].size(); });
_eraseFunc.emplace_back([](KeyT const& k, const HeterogenMap* obj) {
bool do_delete = _values<v_t>[obj].erase(k) != 0;
if (do_delete) {

View File

@@ -0,0 +1,518 @@
#pragma once
#include "../concepts/snplib_concepts.h"
#include "snipplib/utils/snplib_string.h"
namespace snplib
{
/* SERIALIZATION/DESERIALIZATION PROCESS ERROR TYPE */
typedef std::error_code snplib_serialization_error_t;
/* SERIALIZATION/DESERIALIZATION PROCESS TUNING PARAMETERS */
// delimiter between items of serializing values sequence
static constexpr std::string_view SNPLIB_SERIALIZING_DEFAULT_SEQ_DELIMITER{";"};
// delimiter between items of aggregative (multi-element) serializing value
static constexpr std::string_view SNPLIB_SERIALIZING_DEFAULT_ELEM_DELIMITER{","};
template <typename T>
concept snplib_serialization_params_c = std::copyable<T> && requires(T t) {
// delimiter between items of serializing values sequence
requires snplib_output_char_range_c<decltype(t.elem_delim)>;
// delimiter between items of aggregative (multi-element) serializing value
requires snplib_output_char_range_c<decltype(t.seq_delim)>;
// a format string for std::chrono::time_point types
requires snplib_output_char_range_c<decltype(t.timepoint_format)>;
};
// default serializatio/deserialization process parameters type
struct snplib_serialization_params_t {
std::string elem_delim{SNPLIB_SERIALIZING_DEFAULT_ELEM_DELIMITER};
std::string seq_delim{SNPLIB_SERIALIZING_DEFAULT_SEQ_DELIMITER};
std::string integer_format{"{:d}"};
std::string real_format{"{:g}"};
std::string timepoint_format{"{:%FT%T}"};
};
/* SERIALIZER/DESERIALIZER CONCEPTS */
template <snplib_error_c RetT>
struct snplib_serializer_interface_t {
virtual ~snplib_serializer_interface_t() = default;
typedef RetT error_t;
template <std::derived_from<snplib_serializer_interface_t> SelfT, snplib_output_char_range_c R, typename ValueT>
RetT operator()(this SelfT&& self, R& output, ValueT const& value)
{
return std::forward<SelfT>(self)(output, value);
}
template <std::derived_from<snplib_serializer_interface_t> SelfT, snplib_output_char_range_c R, typename ValueT>
RetT operator()(this SelfT&& self, R& output, ValueT const& value, snplib_serialization_params_c auto const& params)
{
return std::forward<SelfT>(self)(output, value, params);
}
protected:
snplib_serializer_interface_t() = default;
};
template <typename T>
concept snplib_serializer_c =
std::derived_from<T, snplib_serializer_interface_t<typename T::error_t>> && requires(T t, const T t_const) {
// static const variable with name of the serializer
requires std::formattable<decltype(T::serializerName), char> && std::is_const_v<decltype(T::serializerName)>;
};
template <snplib_error_c RetT>
struct snplib_deserializer_interface_t {
virtual ~snplib_deserializer_interface_t() = default;
typedef RetT error_t;
template <std::derived_from<snplib_deserializer_interface_t> SelfT, snplib_input_char_range_c R, typename ValueT>
RetT operator()(this SelfT&& self, R const& input, ValueT& value)
{
return std::forward<SelfT>(self)(input, value);
}
template <std::derived_from<snplib_deserializer_interface_t> SelfT, snplib_input_char_range_c R, typename ValueT>
RetT operator()(this SelfT&& self, R const& input, ValueT& value, snplib_serialization_params_c auto& params)
{
return std::forward<SelfT>(self)(input, value, params);
}
protected:
snplib_deserializer_interface_t() = default;
};
template <typename T>
concept snplib_deserializer_c =
std::derived_from<T, snplib_deserializer_interface_t<typename T::error_t>> && requires(T t, const T t_const) {
// static const variable with name of the deserializer
requires std::formattable<decltype(T::deserializerName), char> &&
std::is_const_v<decltype(T::deserializerName)>;
};
/* SERIALIZATION/DESERIALIZATION PROCESS ERROR CODES */
enum class snplib_serializer_error_e : int { ERROR_OK, ERROR_UNDERLYING_SERIALIZER };
enum class snplib_deserializer_error_e : int {
ERROR_OK,
ERROR_UNDERLYING_DESERIALIZER,
ERROR_INVALID_SERIALIZED_VALUE
};
} // end of namespace snplib
namespace std
{
template <>
class is_error_code_enum<snplib::snplib_serializer_error_e> : public true_type
{
};
template <>
class is_error_code_enum<snplib::snplib_deserializer_error_e> : public true_type
{
};
} // end of namespace std
namespace snplib
{
/* BASIC IMPLEMENTATION OF SERIALIZER AND DESERIALIZER CLASSES */
/* error categories */
struct snplib_serializer_error_category_t : public std::error_category {
snplib_serializer_error_category_t() : std::error_category() {}
const char* name() const noexcept
{
return "SNPLIB-SERIALIZER-ERR-CATEGORY";
}
std::string message(int ec) const
{
snplib_serializer_error_e err = static_cast<snplib_serializer_error_e>(ec);
switch (err) {
case snplib_serializer_error_e::ERROR_OK:
return "OK";
case snplib_serializer_error_e::ERROR_UNDERLYING_SERIALIZER:
return "error returned by underlying serializer";
default:
return "UNKNOWN";
}
}
static const snplib_serializer_error_category_t& get()
{
static const snplib_serializer_error_category_t constInst;
return constInst;
}
};
struct snplib_deserializer_error_category_t : public std::error_category {
snplib_deserializer_error_category_t() : std::error_category() {}
const char* name() const noexcept
{
return "SNPLIB-DESERIALIZER-ERR-CATEGORY";
}
std::string message(int ec) const
{
snplib_deserializer_error_e err = static_cast<snplib_deserializer_error_e>(ec);
switch (err) {
case snplib_deserializer_error_e::ERROR_OK:
return "OK";
case snplib_deserializer_error_e::ERROR_UNDERLYING_DESERIALIZER:
return "error returned by underlying deserializer";
case snplib_deserializer_error_e::ERROR_INVALID_SERIALIZED_VALUE:
return "invalid serialized value";
default:
return "UNKNOWN";
}
}
static const snplib_deserializer_error_category_t& get()
{
static const snplib_deserializer_error_category_t constInst;
return constInst;
}
};
/* BASE SERIALIZER CLASS (FOR IMPLEMENTATIONS BELOW) */
struct snplib_serializer_base_t : snplib_serializer_interface_t<snplib_serialization_error_t> {
virtual ~snplib_serializer_base_t() = default;
using typename snplib_serializer_interface_t<snplib_serialization_error_t>::error_t;
protected:
snplib_serializer_base_t() = default;
static void addElemDelimiter(snplib_output_char_range_c auto& output,
snplib_serialization_params_c auto const& params)
{
std::format_to(std::back_inserter(output), "{}", params.elem_delim);
}
static void addSeqDelimiter(snplib_output_char_range_c auto& output,
snplib_serialization_params_c auto const& params)
{
std::format_to(std::back_inserter(output), "{}", params.seq_delim);
}
template <typename VT, typename R>
requires(std::ranges::input_range<R> && std::same_as<VT, std::ranges::range_value_t<R>>)
static error_t serializeRange(snplib_serializer_c auto& sr,
R const& r,
snplib_output_char_range_c auto& output,
snplib_serialization_params_c auto const& params)
{
size_t i = 0, N = std::ranges::size(r);
for (auto const& el : r) {
auto err = sr(output, el, params);
if (err) {
return snplib_deduced_error(err, snplib_serializer_error_e::ERROR_UNDERLYING_SERIALIZER);
}
if (++i < N) {
snplib_serializer_base_t::addElemDelimiter(output, params);
}
}
return snplib_serializer_error_e::ERROR_OK;
}
};
/* BASE DESERIALIZER CLASS (FOR IMPLEMENTATIONS BELOW) */
struct snplib_deserializer_base_t : snplib_deserializer_interface_t<snplib_serialization_error_t> {
using typename snplib_deserializer_interface_t<snplib_serialization_error_t>::error_t;
virtual ~snplib_deserializer_base_t() = default;
protected:
snplib_deserializer_base_t() = default;
//
// empty == true, if the 'input' is empty or if all elements consist of only spaces
//
static std::vector<std::string_view> splitValueIntoElements(snplib_input_char_range_c auto const& input,
snplib_serialization_params_c auto const& params,
bool& empty)
{
static_assert(std::ranges::contiguous_range<decltype(input)>, "NOT IMPLEMENTED FOR NON-CONTIGUIUS RANGES!!!");
std::vector<std::string_view> res;
if (std::ranges::size(input)) {
empty = true;
std::ranges::for_each(std::views::split(input, params.elem_delim), [&res, &empty](auto const& el) {
std::back_inserter(res) = snplib_trim_spaces_as_view(std::string_view{el.begin(), el.end()});
if (empty && res.back().size()) {
empty = false;
}
});
} else {
empty = true;
}
return res;
}
template <typename VT, std::ranges::output_range<VT> R, typename... DeserParamsT>
static error_t deserializingRange(snplib_deserializer_c auto& dsr,
snplib_input_char_range_c auto const& input,
R& r,
snplib_serialization_params_c auto const& params)
{
if (std::ranges::size(input) == 0) { // ignore an empty input, just return empty range?!!
r = R{};
return snplib_deserializer_error_e::ERROR_OK;
}
// auto r_str = std::views::split(input, params.seq_delim);
auto r_str = std::views::split(input, params.elem_delim);
VT val;
auto it = r.begin();
for (auto const& el : r_str) {
// auto err = dsr(el, val, std::forward<DeserParamsT>(params)...);
auto err = dsr(el, val, params);
if (err) {
return snplib_deduced_error(err, snplib_deserializer_error_e::ERROR_UNDERLYING_DESERIALIZER);
}
if (it == r.end()) {
if constexpr (!snplib_fixed_size_range_c<R>) {
std::back_inserter(r) = val;
it = r.end();
}
} else {
*it = val;
++it;
}
}
return snplib_deserializer_error_e::ERROR_OK;
}
};
/* MAIN (FALLBACK) TEMPLATED IMPLEMENTATION OF SERIALIZER/DESERIALIZER */
template <typename VT>
struct snplib_serializer_t : snplib_serializer_base_t {
constexpr static std::string_view serializerName{"SNPLIB-FALLBACK-SERIALIZER"};
template <snplib_serialization_params_c ParamsT = snplib_serialization_params_t>
error_t operator()(snplib_output_char_range_c auto& output,
VT const& value,
ParamsT const& params = snplib_serialization_params_t{})
{
if constexpr (std::convertible_to<VT, std::string>) {
std::string s = value;
std::ranges::copy(s, std::back_inserter(output));
} else if constexpr (std::ranges::range<VT>) {
using value_t = std::remove_cv_t<std::ranges::range_value_t<VT>>;
// special range (character sequence)
if constexpr (std::same_as<value_t, char>) {
std::ranges::copy(value, std::back_inserter(output));
} else {
snplib_serializer_t<value_t> sr;
return snplib_serializer_base_t::serializeRange<value_t>(sr, value, output, params);
}
} else if constexpr (snplib_tuple_like_c<VT>) {
return [&output, &params]<size_t I = 0>(this auto& self, VT& tp) -> error_t {
if constexpr (I < (std::tuple_size_v<VT> - 1)) {
auto err = snplib_serializer_t<std::tuple_element_t<I, VT>>{}(output, std::get<I>(tp), params);
if (err) {
return snplib_deduced_error(err, snplib_serializer_error_e::ERROR_UNDERLYING_SERIALIZER);
}
snplib_serializer_base_t::addElemDelimiter(output, params);
return self.template operator()<I + 1>(tp);
} else if constexpr (I < (std::tuple_size_v<VT> - 1)) { // the last element
auto err = snplib_serializer_t<std::tuple_element_t<I, VT>>{}(output, std::get<I>(tp), params);
if (err) {
return snplib_deduced_error(err, snplib_serializer_error_e::ERROR_UNDERLYING_SERIALIZER);
}
return snplib_serializer_error_e::ERROR_OK;
}
}(value);
} else if constexpr (snplib_anytime_point_c<VT>) {
std::string_view fmt{params.timepoint_format};
std::vformat_to(std::back_inserter(output), fmt, std::make_format_args(value));
} else if constexpr (snplib_time_duration_c<VT>) {
std::format_to(std::back_inserter(output), "{}", value.count());
} else if constexpr (std::integral<VT>) {
std::vformat_to(std::back_inserter(output), std::string_view{params.integer_format},
std::make_format_args(value));
} else if constexpr (std::floating_point<VT>) {
std::vformat_to(std::back_inserter(output), std::string_view{params.real_format},
std::make_format_args(value));
} else if constexpr (std::formattable<VT, char>) {
std::format_to(std::back_inserter(output), "{}", value);
} else {
static_assert(false, "UNSUPPORTED TYPE!!!");
}
return snplib_serializer_error_e::ERROR_OK;
}
};
template <typename VT>
struct snplib_deserializer_t : snplib_deserializer_base_t {
static constexpr std::string_view deserializerName{"SNPLIB-FALLBACK-DESERIALIZER"};
virtual ~snplib_deserializer_t() = default;
template <snplib_serialization_params_c ParamsT = snplib_serialization_params_t>
error_t operator()(snplib_input_char_range_c auto const& input,
VT& value,
ParamsT const& params = snplib_serialization_params_t{})
{
if constexpr (std::is_arithmetic_v<VT>) {
auto v = snplib_num_from_range<VT>(snplib_trim_spaces<std::string>(input));
if (!v.has_value()) {
return snplib_deserializer_error_e::ERROR_INVALID_SERIALIZED_VALUE;
}
value = v.value();
} else if constexpr (snplib_output_char_range_c<VT>) {
VT r;
if constexpr (snplib_fixed_size_range_c<VT>) {
size_t N =
std::ranges::size(r) <= std::ranges::size(input) ? std::ranges::size(r) : std::ranges::size(input);
for (size_t i = 0; i < N; ++i) {
r[i] = input[i];
}
if (std::ranges::size(r) > N) {
for (size_t i = N; i < std::ranges::size(r); ++i) {
r[i] = '\0';
}
}
} else {
std::ranges::copy(input, std::back_inserter(r));
}
value = r;
} else if constexpr (std::ranges::range<VT>) {
using el_t = std::ranges::range_value_t<VT>;
static_assert(std::ranges::output_range<VT, el_t>, "INVALID RANGE TYPE!!!");
// no reference or constants allowed
static_assert(!(std::is_reference_v<el_t> || std::is_const_v<el_t>), "INVALID RANGE ELEMENT TYPE!!!");
snplib_deserializer_t<el_t> dsr;
return deserializingRange<el_t>(dsr, input, value, params);
} else if constexpr (snplib_tuple_like_c<VT>) {
bool empty;
auto elems = splitValueIntoElements(input, params, &empty);
if (empty) {
value = VT{};
} else {
return [&input, &params, &elems]<size_t I = 0>(this auto& self, VT& tp) {
if constexpr (I < std::tuple_size_v<VT>) {
if (I < elems.size()) {
auto err =
snplib_deserializer_t<std::tuple_element_t<I, VT>>{}(elems[I], std::get<I>(tp), params);
if (err) {
return snplib_deduced_error(err,
snplib_deserializer_error_e::ERROR_UNDERLYING_DESERIALIZER);
}
return self.template operator()<I + 1>(tp);
} else {
return snplib_deserializer_error_e::ERROR_OK;
}
}
return snplib_deserializer_error_e::ERROR_OK;
}(value);
}
} else if constexpr (snplib_anytime_point_c<VT>) {
std::istringstream ist{snplib_trim_spaces<std::string>(input)};
std::chrono::from_stream(ist, params.timepoint_format.c_str(), value);
if (ist.fail()) {
return snplib_deserializer_error_e::ERROR_INVALID_SERIALIZED_VALUE;
}
} else if constexpr (snplib_time_duration_c<VT>) {
typename VT::rep vd;
snplib_deserializer_t<typename VT::rep> dsr;
auto err = dsr(snplib_trim_spaces_as_view(input), vd, params);
if (err) {
return snplib_deduced_err(err, snplib_deserializer_error_e::ERROR_UNDERLYING_DESERIALIZER);
}
value = VT{vd};
} else {
static_assert(false, "UNSUPPORTED VALUE TYPE!!!");
}
return snplib_deserializer_error_e::ERROR_OK;
}
};
} // end of namespace snplib

View File

@@ -5,7 +5,8 @@
#include "../concepts/snplib_concepts.h"
namespace snplib {
namespace snplib
{
constexpr static bool snplib_is_space(char in) noexcept
{
@@ -17,32 +18,37 @@ constexpr static bool snplib_is_space(char in) noexcept
enum class TrimType { TRIM_LEFT, TRIM_RIGHT, TRIM_BOTH };
template <snplib_output_char_range_c OR, snplib_input_char_range_c R, typename IsSpaceFuncT = decltype(snplib_is_space)>
constexpr static OR snplib_trim_spaces(R&& r, TrimType type = TrimType::TRIM_BOTH, IsSpaceFuncT&& is_space_func = snplib_is_space)
constexpr static OR snplib_trim_spaces(R&& r,
TrimType type = TrimType::TRIM_BOTH,
IsSpaceFuncT&& is_space_func = snplib_is_space)
{
if (type == TrimType::TRIM_LEFT) {
auto res = r | std::views::drop_while(is_space_func);
return OR{res.begin(), res.end()};
} else if (type == TrimType::TRIM_RIGHT) {
auto res = r | std::views::reverse | std::views::drop_while(is_space_func) |
std::views::reverse;
auto res = r | std::views::reverse | std::views::drop_while(is_space_func) | std::views::reverse;
return OR{res.begin(), res.end()};
} else if (type == TrimType::TRIM_BOTH) {
auto res = r | std::views::drop_while(is_space_func) | std::views::reverse | std::views::drop_while(is_space_func) |
std::views::reverse;
auto res = r | std::views::drop_while(is_space_func) | std::views::reverse |
std::views::drop_while(is_space_func) | std::views::reverse;
return OR{res.begin(), res.end()};
}
return OR{};
}
return OR{};
}
template <snplib_input_char_range_c R, typename IsSpaceFuncT = decltype(snplib_is_space)>
constexpr static std::string snplib_trim_spaces(R&& r, TrimType type = TrimType::TRIM_BOTH, IsSpaceFuncT&& is_space_func = snplib_is_space)
constexpr static std::string snplib_trim_spaces(R&& r,
TrimType type = TrimType::TRIM_BOTH,
IsSpaceFuncT&& is_space_func = snplib_is_space)
{
return snplib_trim_spaces<std::string>(std::forward<R>(r), type, std::forward<IsSpaceFuncT>(is_space_func));
}
template <snplib_input_char_range_c R, typename IsSpaceFuncT = decltype(snplib_is_space)>
constexpr static std::string_view snplib_trim_spaces_as_view(R&& r, TrimType type = TrimType::TRIM_BOTH, IsSpaceFuncT&& is_space_func = snplib_is_space)
constexpr static std::string_view snplib_trim_spaces_as_view(R&& r,
TrimType type = TrimType::TRIM_BOTH,
IsSpaceFuncT&& is_space_func = snplib_is_space)
requires std::ranges::contiguous_range<R>
{
auto end = std::forward<R>(r).end();
@@ -74,4 +80,55 @@ constexpr static std::string_view snplib_trim_spaces_as_view(R&& r, TrimType typ
}
} // end of snplib namespace
template <typename T, snplib_input_char_range_c R>
static std::optional<T> snplib_num_from_range(R&& r)
requires((std::integral<T> || std::floating_point<T>) && std::ranges::contiguous_range<R>)
{
T val;
const char* end_ptr = &*r.end();
if constexpr (std::integral<T>) {
auto cvt_res = std::from_chars(&*r.begin(), &*r.end(), val);
if (cvt_res.ec != std::errc()) {
return std::nullopt;
} else if (cvt_res.ptr != end_ptr) {
return std::nullopt;
}
} else {
#ifdef _LIBCPP_VERSION // clang's libc++ does not have floating-point overloads for std::from_chars
std::string s{str.begin(), str.end()};
size_t pos;
try {
if constexpr (std::same_as<T, float>) {
val = std::stof(s, &pos);
} else if constexpr (std::same_as<T, double>) {
val = std::stod(s, &pos);
} else {
val = std::stold(s, &pos);
}
} catch (...) {
return std::nullopt;
}
if (pos != s.size()) {
return std::nullopt;
}
#else
auto cvt_res = std::from_chars(&*r.begin(), &*r.end(), val);
if (cvt_res.ec != std::errc()) {
return std::nullopt;
} else if (cvt_res.ptr != end_ptr) {
return std::nullopt;
}
#endif
}
return val;
}
} // namespace snplib

View File

@@ -1,2 +1,4 @@
#pragma once
#include "snplib_hash.h"
#include "snplib_string.h"