diff --git a/include/leven.h b/include/leven.h index 0deb382..6f9495b 100644 --- a/include/leven.h +++ b/include/leven.h @@ -4,7 +4,7 @@ #include "exopt.h" #ifdef __cplusplus -//#include "util.hh" +#include "util.hh" #include #include @@ -88,8 +88,17 @@ namespace exopt { namespace util [[gnu::visibility("internal")]] { if constexpr(!N) return M; else if constexpr(!M) return N; - //else if constexpr(M < N) return N - M; // These lines are not correct, just fixing the sizes does not make the edit valid - //else if constexpr(N < M) return M - N; // ^ + else if constexpr(M < N) return N - M; + else if constexpr(N < M) return M - N; + else return leven_diff(std::string_view{sa}, std::string_view{sb}); + } + template + constexpr decltype(auto) leven_diff(std::array const& sa, std::array const& sb) noexcept { + if constexpr(!N) return M; + else if constexpr(!M) return N; + + else if constexpr(M < N) return N - M; + else if constexpr(N < M) return M - N; else return leven_diff(std::string_view{sa}, std::string_view{sb}); } @@ -100,9 +109,19 @@ namespace exopt { namespace util [[gnu::visibility("internal")]] { struct string_ord { using string_type = std::remove_reference_t; - constexpr string_ord(string_type&& str) noexcept + template requires(std::is_constructible_v) + constexpr string_ord(Args&&... args) noexcept(std::is_nothrow_constructible_v) + : m_string{std::forward(args)...} {} +// { return { S(std::forward(args)...) }; } + constexpr string_ord(std::add_rvalue_reference_t str) noexcept requires(!std::is_array_v) : m_string(std::move(str)) {} + constexpr string_ord(const char (&str)[sizeof(string_type)]) requires(std::is_array_v) + : string_ord(std::move(str), std::make_index_sequence{}) {} + + constexpr string_ord(const char (&&str)[sizeof(string_type)]) requires(std::is_array_v) + : string_ord(std::move(str), std::make_index_sequence{}) {} + constexpr string_ord(string_ord const&) = default; constexpr string_ord(string_ord &&) = default; constexpr string_ord& operator=(string_ord const&) = default; @@ -121,7 +140,17 @@ namespace exopt { namespace util [[gnu::visibility("internal")]] { return ab <=> ba; } - constexpr operator std::string_view() const noexcept { return m_string; } + constexpr std::string_view view() const noexcept { if constexpr(std::is_array_v) return { m_string, sizeof(string_type) - 1 }; else return { m_string }; } + constexpr operator std::string_view() const noexcept { return view(); } + + template requires(std::is_convertible_v and std::is_convertible_v) + constexpr explicit operator string_ord() && noexcept { return { std::move(m_string) }; } + + template requires(std::is_convertible_v and std::is_convertible_v and std::is_constructible_v) + constexpr explicit operator string_ord() const&& noexcept { return { U{std::move(m_string)} }; } + + template requires(std::is_convertible_v and std::is_convertible_v and std::is_constructible_v) + constexpr explicit operator string_ord() const& noexcept { return { U{m_string} }; } constexpr string_type& string() noexcept { return m_string; } constexpr string_type const& string() const noexcept { return m_string; } @@ -133,8 +162,14 @@ namespace exopt { namespace util [[gnu::visibility("internal")]] { constexpr string_type const&& operator*() const&& noexcept { return std::move(m_string); } private: + template requires(sizeof...(Is) == sizeof(string_type)) + constexpr string_ord(const char (&&l)[sizeof(string_type)], std::index_sequence) noexcept requires(std::is_array_v) + : m_string { l[Is]... } {} + string_type m_string; }; + template + string_ord(const char (&l)[N]) -> string_ord; /// Used to store all valid command names, so when an invalid one is found, the closest matching one(s) can be suggested to the user in a "did you mean ..." format with the *lowest difference* neighbour(s) to the invalid string first. template S = std::string_view> diff --git a/include/util.hh b/include/util.hh index c97122e..fb8d35e 100644 --- a/include/util.hh +++ b/include/util.hh @@ -2,6 +2,7 @@ #include #include +#include #include "exopt.h" @@ -34,4 +35,22 @@ namespace exopt { namespace util [[gnu::visibility("internal")]] { static_assert(comptime_value(std::string_view{"hello"}.size()) == 5, "Bad comptime_value()"); static_assert(_EO_CONSTANT_VALUE(std::string_view{"hello world"}) == std::string_view{"hello world"}, "Bad CONSTANT_VALUE()"); + + + template> + constexpr decltype(auto) array_literal(const T (&a)[N]) noexcept { + constexpr decltype(auto) _array_create = [](auto const& a, std::index_sequence) noexcept + { + return Array { a[I]... }; + }; + return _array_create(std::move(a), _I{}); + } + template> + constexpr decltype(auto) array_literal(T (&&a)[N]) noexcept { + constexpr decltype(auto) _array_create = [](auto&& a, std::index_sequence) noexcept + { + return Array { std::move(a[I])... }; + }; + return _array_create(std::move(a), _I{}); + } } } diff --git a/src/leven.cpp b/src/leven.cpp index a4fad89..aa19315 100644 --- a/src/leven.cpp +++ b/src/leven.cpp @@ -17,7 +17,14 @@ extern "C" { } namespace exopt { - namespace util [[gnu::visibility("internal")]] { + namespace util [[gnu::visibility("internal")]] { + namespace { + constexpr string_ord a("hello world"); + constexpr string_ord b{""}; + static_assert(a.view()[a.view().size()-1], "Levenshtein distance size mismatch for array literals"); + static_assert(b.view().empty(), "Levenshtein distance size mismatch for array literals (empty)"); + static_assert(a.difference_from(b) == (sizeof(a) - sizeof(b)), "Levenshtein distance size mismatch for array literals (calculation)"); + } static_assert(leven_diff("hello world", "Hello World") == 2, "Levelshtein distance incorrect for non-matching strings"); static_assert(leven_diff("hello world", "hello world") == 0, "Levelshtein distance incorrect for matching strings"); static_assert(leven_diff("hello world", std::string_view{"hello world"}) == 0, "Levelshtein distance incorrect for matching string container type 1");