// Stuff that doesn't really have a place right now, but that is // broadly useful. export module routemon:util; import std; namespace routemon::util { // For use with e.g. std::visit (on std::variant). template struct overloaded : Ts... { using Ts::operator()...; }; constexpr auto parse_double( std::string_view s, std::chars_format fmt = std::chars_format::general) noexcept -> std::optional { auto x = 0.0; auto [_, ec] = std::from_chars(s.data(), s.data() + s.size(), x, fmt); if (ec == std::errc{}) { return x; } else { return std::nullopt; } } constexpr auto parse_float( std::string_view s, std::chars_format fmt = std::chars_format::general) noexcept -> std::optional { auto x = 0.0; auto [_, ec] = std::from_chars(s.data(), s.data() + s.size(), x, fmt); if (ec == std::errc{}) { return x; } else { return std::nullopt; } } template class aolist : public std::enable_shared_from_this> { T v_; std::shared_ptr const> next_; explicit aolist(T v, std::shared_ptr const> next) : v_{v}, next_{next} { } public: static auto nil() -> std::shared_ptr> { return nullptr; } static auto cons(T v, std::shared_ptr const> l) -> std::shared_ptr> { return std::shared_ptr>{new aolist{v, l}}; } auto next() const -> std::shared_ptr const> { return next_; } auto value() const noexcept -> T const& { return v_; } }; constexpr auto size_from_int(int x) -> std::optional { static_assert( sizeof(int) <= sizeof(std::size_t), "cannot cast int to smaller size_t type"); if (x < 0) return std::nullopt; return static_cast(x); } constexpr auto int_from_size(std::size_t x) -> std::optional { constexpr auto int_max = size_from_int(std::numeric_limits::max()); static_assert(int_max.has_value()); if (x > *int_max) return std::nullopt; return static_cast(x); } class zstring_view { char const* s_; std::size_t length_; public: constexpr explicit zstring_view(char const* s, std::size_t length) : s_{s}, length_{length} { } constexpr zstring_view(char const* s) : zstring_view{s, std::char_traits::length(s)} { } constexpr auto length() const -> std::size_t { return length_; } constexpr auto c_str() const -> char const* { return s_; } constexpr operator std::string_view() const { return std::string_view{s_, length_}; } constexpr operator char const*() const { return s_; } }; // View for null-terminated strings for which we might not // necessarily be interested in the length. String length is only // calculated on demand, at most once on each thread (on more than // one thread when racing). May be null. // // It is undefined behavior to assign to a lazy_zstring_view when // it is in use by other threads. class lazy_zstring_view { static constexpr auto unset_length = std::numeric_limits::max(); char const* s_; // nullable mutable std::atomic length_; static_assert(decltype(length_)::is_always_lock_free); public: constexpr explicit lazy_zstring_view(char const* s) : s_{s}, length_{s ? unset_length : 0} { } lazy_zstring_view(lazy_zstring_view const& sv) noexcept; lazy_zstring_view(lazy_zstring_view&& sv) noexcept; ~lazy_zstring_view() = default; auto operator=(lazy_zstring_view const& rhs) noexcept -> lazy_zstring_view&; auto operator=(lazy_zstring_view&& rhs) noexcept -> lazy_zstring_view&; auto length() const noexcept -> std::size_t; auto c_str() const noexcept -> char const*; operator std::string_view() const noexcept; operator char const*() const noexcept; auto operator==(std::string_view sv) const noexcept -> bool; }; constexpr auto operator""_zsv(char const* s, std::size_t length) noexcept -> zstring_view { return zstring_view{s, length}; } auto operator==(zstring_view lhs, zstring_view rhs) -> bool; constexpr auto split_on(std::string_view s, char c) -> std::pair> { if (auto i = s.find(c); i != std::string_view::npos) return std::make_pair(s.substr(0, i), s.substr(i + 1)); return std::make_pair(s, std::nullopt); } template class not_null; template class not_null { T* p_; struct guaranteed_not_null_t { }; explicit not_null(T* p, guaranteed_not_null_t) noexcept : p_{p} {} public: explicit not_null(T* p) : p_{p} { if (!p_) throw std::runtime_error{"not_null constructed with null pointer"}; } ~not_null() = default; not_null(not_null const& other) = default; not_null(not_null&& other) noexcept = default; auto operator=(not_null const& rhs) noexcept -> not_null& = default; auto operator=(not_null&& rhs) noexcept -> not_null& = default; friend auto make_not_null(T* p) noexcept -> std::optional { if (p) return not_null(p, guaranteed_not_null_t{}); else return std::nullopt; } [[nodiscard]] auto get() const noexcept -> T* { return p_; } auto operator*() const noexcept -> std::add_lvalue_reference_t { return *p_; } auto operator->() const noexcept -> T* { return p_; } }; template explicit not_null(T*) -> not_null; template <> class not_null { lazy_zstring_view s_; public: explicit not_null(lazy_zstring_view s) : s_{std::move(s)} { if (!s_) throw std::runtime_error{"not_null constructed with null pointer"}; } [[nodiscard]] auto get() const noexcept -> lazy_zstring_view { return s_; } operator lazy_zstring_view() const noexcept { return s_; } operator std::string_view() const noexcept { return s_; } operator char const*() const noexcept { return s_; } }; explicit not_null(lazy_zstring_view s) -> not_null; } // namespace routemon::util