diff --git a/CMakeLists.txt b/CMakeLists.txt index 37fc1f3..a23df22 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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}) diff --git a/examples/hmap_example.cpp b/examples/hmap_example.cpp index 4906bc2..397e11b 100644 --- a/examples/hmap_example.cpp +++ b/examples/hmap_example.cpp @@ -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("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_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; } \ No newline at end of file diff --git a/include/snipplib/concepts/snplib_concepts.h b/include/snipplib/concepts/snplib_concepts.h index 2f477c0..5083ce9 100644 --- a/include/snipplib/concepts/snplib_concepts.h +++ b/include/snipplib/concepts/snplib_concepts.h @@ -7,13 +7,15 @@ namespace snplib { -// type T is hashable +/* type T is hashable */ + template concept snplib_hashable_c = requires(T t) { { std::hash{}(t) } -> std::convertible_to; }; -// some concepts for various types of char sequence +/* some concepts for various types of char sequence */ + template concept snplib_char_range_c = std::ranges::range && std::same_as, CharT>; @@ -27,7 +29,7 @@ template concept snplib_char_view_c = std::ranges::view && std::same_as, CharT>; -// std::chrono related concepts +/* std::chrono related concepts */ template concept snplib_time_duration_c = requires(T t) { @@ -50,11 +52,104 @@ concept snplib_systime_point_c = snplib_time_point_c concept snplib_utctime_point_c = snplib_time_point_c; +template +concept snplib_anytime_point_c = + snplib_systime_point_c || snplib_utctime_point_c || snplib_time_point_c || + snplib_time_point_c || snplib_time_point_c || + snplib_time_point_c; -// callables (does not working for templated functions/methods and generic lambdas!) + +/* callables (NOTE: does not working for templated functions/methods and generic lambdas!) */ template concept snplib_callable_c = std::is_function_v || (std::is_object_v && requires(T) { &T::operator(); }); +/* std::tuple-like types */ + +namespace details +{ + +template +concept snplib_get_all_indices_c = requires(T t) { (std::get(t), ...); }; + +template +struct snplib_check_indices_t { + static constexpr bool value = false; +}; + +// Specialize to extract the index sequence matching the tuple size +template + requires requires { typename std::tuple_size::type; } +struct snplib_check_indices_t { + static constexpr bool value = [](std::index_sequence) { + return snplib_get_all_indices_c; + }(std::make_index_sequence::value>{}); +}; + +template +static constexpr bool snplib_check_indices_v = snplib_check_indices_t::value; + +} // namespace details + +template +concept snplib_tuple_like_c = details::snplib_check_indices_v>; + + +/* fixed-size and non-resizable ranges */ + +// std::array, std::span(v_t, N), std::tuple +template +concept snplib_fixed_size_range_c = + std::ranges::range && requires { std::tuple_size>::value; }; + +// non-resizable ranges +template +concept snplib_non_resizable_range_c = std::ranges::range && !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 +concept snplib_error_c = std::constructible_from && requires(T&& t) { + static_cast(std::forward(t)); + + { !std::forward(t) } -> std::convertible_to; +}; + +// utility function to deduce error-type + +template +static auto snplib_deduced_error(ErrT const& err, FallbackErrT const& fallback_err) +{ + if constexpr (std::same_as) { + return err; + } else if constexpr (std::is_error_code_enum_v) { // special case + if constexpr (std::same_as) { + return err; + } else { + return fallback_err; + } + } else if constexpr (std::is_error_condition_enum_v) { // special case + if constexpr (std::same_as) { + return err; + } else { + return fallback_err; + } + } else { + return fallback_err; + } +} + } // namespace snplib \ No newline at end of file diff --git a/include/snipplib/containers/snplib_hmap.h b/include/snipplib/containers/snplib_hmap.h index 46588ea..997fafd 100644 --- a/include/snipplib/containers/snplib_hmap.h +++ b/include/snipplib/containers/snplib_hmap.h @@ -36,6 +36,7 @@ protected: inline static std::vector> _copyFunc{}; inline static std::vector> _clearFunc{}; + inline static std::vector> _sizeFunc{}; inline static std::vector> _eraseFunc{}; inline static std::vector> _containsFunc{}; @@ -119,6 +120,18 @@ public: } } + + size_t size() const + { + size_t N = 0; + + for (auto& func : _sizeFunc) { + N += func(this); + } + + return N; + } + template bool push(KeyT const& key, VT&& value) { @@ -156,6 +169,8 @@ public: _setter[obj].clear(); }); + _sizeFunc.emplace_back([](const HeterogenMap* obj) { return _values[obj].size(); }); + _eraseFunc.emplace_back([](KeyT const& k, const HeterogenMap* obj) { bool do_delete = _values[obj].erase(k) != 0; if (do_delete) { diff --git a/include/snipplib/serialization/snplib_serialization.h b/include/snipplib/serialization/snplib_serialization.h new file mode 100644 index 0000000..e6535fb --- /dev/null +++ b/include/snipplib/serialization/snplib_serialization.h @@ -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 +concept snplib_serialization_params_c = std::copyable && requires(T t) { + // delimiter between items of serializing values sequence + requires snplib_output_char_range_c; + + // delimiter between items of aggregative (multi-element) serializing value + requires snplib_output_char_range_c; + + // a format string for std::chrono::time_point types + requires snplib_output_char_range_c; +}; + + +// 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 +struct snplib_serializer_interface_t { + virtual ~snplib_serializer_interface_t() = default; + + typedef RetT error_t; + + template SelfT, snplib_output_char_range_c R, typename ValueT> + RetT operator()(this SelfT&& self, R& output, ValueT const& value) + { + return std::forward(self)(output, value); + } + + template 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(self)(output, value, params); + } + +protected: + snplib_serializer_interface_t() = default; +}; + + +template +concept snplib_serializer_c = + std::derived_from> && requires(T t, const T t_const) { + // static const variable with name of the serializer + requires std::formattable && std::is_const_v; + }; + + + +template +struct snplib_deserializer_interface_t { + virtual ~snplib_deserializer_interface_t() = default; + + typedef RetT error_t; + + template SelfT, snplib_input_char_range_c R, typename ValueT> + RetT operator()(this SelfT&& self, R const& input, ValueT& value) + { + return std::forward(self)(input, value); + } + + template 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(self)(input, value, params); + } + +protected: + snplib_deserializer_interface_t() = default; +}; + + +template +concept snplib_deserializer_c = + std::derived_from> && requires(T t, const T t_const) { + // static const variable with name of the deserializer + requires std::formattable && + std::is_const_v; + }; + + + +/* 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 : public true_type +{ +}; + +template <> +class is_error_code_enum : 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(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(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 { + virtual ~snplib_serializer_base_t() = default; + + using typename snplib_serializer_interface_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 + requires(std::ranges::input_range && std::same_as>) + 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 { + using typename snplib_deserializer_interface_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 splitValueIntoElements(snplib_input_char_range_c auto const& input, + snplib_serialization_params_c auto const& params, + bool& empty) + { + static_assert(std::ranges::contiguous_range, "NOT IMPLEMENTED FOR NON-CONTIGUIUS RANGES!!!"); + + std::vector 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 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(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) { + 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 +struct snplib_serializer_t : snplib_serializer_base_t { + constexpr static std::string_view serializerName{"SNPLIB-FALLBACK-SERIALIZER"}; + + template + 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) { + std::string s = value; + std::ranges::copy(s, std::back_inserter(output)); + } else if constexpr (std::ranges::range) { + using value_t = std::remove_cv_t>; + + // special range (character sequence) + if constexpr (std::same_as) { + std::ranges::copy(value, std::back_inserter(output)); + } else { + snplib_serializer_t sr; + + return snplib_serializer_base_t::serializeRange(sr, value, output, params); + } + } else if constexpr (snplib_tuple_like_c) { + return [&output, ¶ms](this auto& self, VT& tp) -> error_t { + if constexpr (I < (std::tuple_size_v - 1)) { + auto err = snplib_serializer_t>{}(output, std::get(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()(tp); + } else if constexpr (I < (std::tuple_size_v - 1)) { // the last element + auto err = snplib_serializer_t>{}(output, std::get(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) { + 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) { + std::format_to(std::back_inserter(output), "{}", value.count()); + } else if constexpr (std::integral) { + std::vformat_to(std::back_inserter(output), std::string_view{params.integer_format}, + std::make_format_args(value)); + } else if constexpr (std::floating_point) { + std::vformat_to(std::back_inserter(output), std::string_view{params.real_format}, + std::make_format_args(value)); + } else if constexpr (std::formattable) { + std::format_to(std::back_inserter(output), "{}", value); + } else { + static_assert(false, "UNSUPPORTED TYPE!!!"); + } + + return snplib_serializer_error_e::ERROR_OK; + } +}; + + + +template +struct snplib_deserializer_t : snplib_deserializer_base_t { + static constexpr std::string_view deserializerName{"SNPLIB-FALLBACK-DESERIALIZER"}; + + virtual ~snplib_deserializer_t() = default; + + + template + 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) { + auto v = snplib_num_from_range(snplib_trim_spaces(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 r; + if constexpr (snplib_fixed_size_range_c) { + 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) { + using el_t = std::ranges::range_value_t; + + static_assert(std::ranges::output_range, "INVALID RANGE TYPE!!!"); + + // no reference or constants allowed + static_assert(!(std::is_reference_v || std::is_const_v), "INVALID RANGE ELEMENT TYPE!!!"); + + snplib_deserializer_t dsr; + + return deserializingRange(dsr, input, value, params); + } else if constexpr (snplib_tuple_like_c) { + bool empty; + auto elems = splitValueIntoElements(input, params, &empty); + if (empty) { + value = VT{}; + } else { + return [&input, ¶ms, &elems](this auto& self, VT& tp) { + if constexpr (I < std::tuple_size_v) { + if (I < elems.size()) { + auto err = + snplib_deserializer_t>{}(elems[I], std::get(tp), params); + if (err) { + return snplib_deduced_error(err, + snplib_deserializer_error_e::ERROR_UNDERLYING_DESERIALIZER); + } + + return self.template operator()(tp); + } else { + return snplib_deserializer_error_e::ERROR_OK; + } + } + + return snplib_deserializer_error_e::ERROR_OK; + }(value); + } + + } else if constexpr (snplib_anytime_point_c) { + std::istringstream ist{snplib_trim_spaces(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) { + typename VT::rep vd; + + snplib_deserializer_t 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 diff --git a/include/snipplib/utils/snplib_string.h b/include/snipplib/utils/snplib_string.h index f5d552d..3c0b1fe 100644 --- a/include/snipplib/utils/snplib_string.h +++ b/include/snipplib/utils/snplib_string.h @@ -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 -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 -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::forward(r), type, std::forward(is_space_func)); } template -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 { auto end = std::forward(r).end(); @@ -74,4 +80,55 @@ constexpr static std::string_view snplib_trim_spaces_as_view(R&& r, TrimType typ } -} // end of snplib namespace \ No newline at end of file + +template +static std::optional snplib_num_from_range(R&& r) + requires((std::integral || std::floating_point) && std::ranges::contiguous_range) +{ + T val; + const char* end_ptr = &*r.end(); + + if constexpr (std::integral) { + 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) { + val = std::stof(s, &pos); + } else if constexpr (std::same_as) { + 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 \ No newline at end of file diff --git a/include/snipplib/utils/snplib_utils.h b/include/snipplib/utils/snplib_utils.h index 3f59c93..632355b 100644 --- a/include/snipplib/utils/snplib_utils.h +++ b/include/snipplib/utils/snplib_utils.h @@ -1,2 +1,4 @@ #pragma once +#include "snplib_hash.h" +#include "snplib_string.h" \ No newline at end of file