diff options
Diffstat (limited to 'nihil.util')
| -rw-r--r-- | nihil.util/CMakeLists.txt | 20 | ||||
| -rw-r--r-- | nihil.util/capture_stream.ccm | 19 | ||||
| -rw-r--r-- | nihil.util/capture_stream.test.cc (renamed from nihil.util/test_capture_stream.cc) | 10 | ||||
| -rw-r--r-- | nihil.util/ctype.ccm | 73 | ||||
| -rw-r--r-- | nihil.util/ctype.test.cc (renamed from nihil.util/test_ctype.cc) | 9 | ||||
| -rw-r--r-- | nihil.util/next_word.ccm | 37 | ||||
| -rw-r--r-- | nihil.util/next_word.test.cc (renamed from nihil.util/test_next_word.cc) | 12 | ||||
| -rw-r--r-- | nihil.util/nihil.util.ccm | 8 | ||||
| -rw-r--r-- | nihil.util/parse_size.ccm | 75 | ||||
| -rw-r--r-- | nihil.util/parse_size.test.cc (renamed from nihil.util/test_parse_size.cc) | 8 | ||||
| -rw-r--r-- | nihil.util/save_errno.ccm | 35 | ||||
| -rw-r--r-- | nihil.util/skipws.ccm | 29 | ||||
| -rw-r--r-- | nihil.util/skipws.test.cc (renamed from nihil.util/test_skipws.cc) | 15 | ||||
| -rw-r--r-- | nihil.util/tabulate.ccm | 55 | ||||
| -rw-r--r-- | nihil.util/tabulate.test.cc (renamed from nihil.util/test_tabulate.cc) | 9 |
15 files changed, 180 insertions, 234 deletions
diff --git a/nihil.util/CMakeLists.txt b/nihil.util/CMakeLists.txt index 2ef916e..109e4d4 100644 --- a/nihil.util/CMakeLists.txt +++ b/nihil.util/CMakeLists.txt @@ -1,7 +1,12 @@ # This source code is released into the public domain. add_library(nihil.util STATIC) -target_link_libraries(nihil.util PRIVATE nihil.core nihil.error nihil.monad) +target_link_libraries(nihil.util PRIVATE + nihil.std + nihil.core + nihil.error + nihil.monad +) target_sources(nihil.util PUBLIC FILE_SET modules TYPE CXX_MODULES FILES nihil.util.ccm @@ -10,6 +15,7 @@ target_sources(nihil.util ctype.ccm parse_size.ccm next_word.ccm + save_errno.ccm skipws.ccm tabulate.ccm ) @@ -18,12 +24,12 @@ if(NIHIL_TESTS) enable_testing() add_executable(nihil.util.test - test_capture_stream.cc - test_ctype.cc - test_parse_size.cc - test_next_word.cc - test_skipws.cc - test_tabulate.cc + capture_stream.test.cc + ctype.test.cc + parse_size.test.cc + next_word.test.cc + skipws.test.cc + tabulate.test.cc ) target_link_libraries(nihil.util.test PRIVATE nihil.util diff --git a/nihil.util/capture_stream.ccm b/nihil.util/capture_stream.ccm index 7ec39a9..f061558 100644 --- a/nihil.util/capture_stream.ccm +++ b/nihil.util/capture_stream.ccm @@ -1,20 +1,13 @@ -/* - * This source code is released into the public domain. - */ - -module; - -#include <iostream> - +// This source code is released into the public domain. export module nihil.util:capture_stream; +import nihil.std; + namespace nihil { -/* - * Capture output written to a stream and redirect it to an internal string - * buffer. Call .str() to get the data written. Call .release() to stop - * capturing (or simply delete the capture_stream object). - */ +// Capture output written to a stream and redirect it to an internal string +// buffer. Call .str() to get the data written. Call .release() to stop +// capturing (or simply delete the capture_stream object). export template<typename Char, typename Traits> struct capture_stream { capture_stream(std::basic_ostream<Char, Traits> &stream) diff --git a/nihil.util/test_capture_stream.cc b/nihil.util/capture_stream.test.cc index 27c8596..a4821b7 100644 --- a/nihil.util/test_capture_stream.cc +++ b/nihil.util/capture_stream.test.cc @@ -1,13 +1,11 @@ -/* - * This source code is released into the public domain. - */ - -#include <iostream> +// This source code is released into the public domain. #include <catch2/catch_test_macros.hpp> +import nihil.std; import nihil.util; +namespace { TEST_CASE("nihil.util: capture", "[nihil][nihil.util]") { SECTION("std::cout with release()") { @@ -42,3 +40,5 @@ TEST_CASE("nihil.util: capture", "[nihil][nihil.util]") REQUIRE(cap.str() == "1+1=2\n"); } } + +} // anonymous namespace diff --git a/nihil.util/ctype.ccm b/nihil.util/ctype.ccm index 6d30c4f..8f5de27 100644 --- a/nihil.util/ctype.ccm +++ b/nihil.util/ctype.ccm @@ -1,14 +1,8 @@ -/* - * This source code is released into the public domain. - */ - -module; - -#include <concepts> -#include <locale> - +// This source code is released into the public domain. export module nihil.util:ctype; +import nihil.std; + namespace nihil { /* @@ -21,15 +15,16 @@ namespace nihil { * ctype_is copies the locale, so passing a temporary is fine. */ -export struct ctype_is final { - ctype_is(std::ctype_base::mask mask_, - std::locale const &locale_ = std::locale()) - : m_mask(mask_) - , m_locale(locale_) - {} +export struct ctype_is final +{ + explicit ctype_is(std::ctype_base::mask mask_, + std::locale const &locale_ = std::locale()) noexcept + : m_mask(mask_) + , m_locale(locale_) + { + } - [[nodiscard]] auto operator()(this ctype_is const &self, - std::integral auto c) + [[nodiscard]] auto operator()(this ctype_is const &self, std::integral auto c) { using ctype = std::ctype<decltype(c)>; auto &facet = std::use_facet<ctype>(self.m_locale); @@ -37,11 +32,11 @@ export struct ctype_is final { } private: - std::ctype_base::mask m_mask; - std::locale m_locale; + std::ctype_base::mask m_mask; + std::locale m_locale; }; -// Predefined tests for the current global locale. +// Predefined tests for the current global locale. export inline auto is_space = ctype_is(std::ctype_base::space); export inline auto is_print = ctype_is(std::ctype_base::print); @@ -59,29 +54,19 @@ export inline auto is_graph = ctype_is(std::ctype_base::graph); // Predefined tests for the C locale. The C locale is guaranteed to always be // available, so this doesn't create lifetime issues. -export inline auto is_c_space = - ctype_is(std::ctype_base::space, std::locale::classic()); -export inline auto is_c_print = - ctype_is(std::ctype_base::print, std::locale::classic()); -export inline auto is_c_cntrl = - ctype_is(std::ctype_base::cntrl, std::locale::classic()); -export inline auto is_c_upper = - ctype_is(std::ctype_base::upper, std::locale::classic()); -export inline auto is_c_lower = - ctype_is(std::ctype_base::lower, std::locale::classic()); -export inline auto is_c_alpha = - ctype_is(std::ctype_base::alpha, std::locale::classic()); -export inline auto is_c_digit = - ctype_is(std::ctype_base::digit, std::locale::classic()); -export inline auto is_c_punct = - ctype_is(std::ctype_base::punct, std::locale::classic()); -export inline auto is_c_xdigit = - ctype_is(std::ctype_base::xdigit, std::locale::classic()); -export inline auto is_c_blank = - ctype_is(std::ctype_base::blank, std::locale::classic()); -export inline auto is_c_alnum = - ctype_is(std::ctype_base::alnum, std::locale::classic()); -export inline auto is_c_graph = - ctype_is(std::ctype_base::graph, std::locale::classic()); +//NOLINTBEGIN: Technically, std::locale::classic() can throw. Assume it doesn't. +export inline auto is_c_space = ctype_is(std::ctype_base::space, std::locale::classic()); +export inline auto is_c_print = ctype_is(std::ctype_base::print, std::locale::classic()); +export inline auto is_c_cntrl = ctype_is(std::ctype_base::cntrl, std::locale::classic()); +export inline auto is_c_upper = ctype_is(std::ctype_base::upper, std::locale::classic()); +export inline auto is_c_lower = ctype_is(std::ctype_base::lower, std::locale::classic()); +export inline auto is_c_alpha = ctype_is(std::ctype_base::alpha, std::locale::classic()); +export inline auto is_c_digit = ctype_is(std::ctype_base::digit, std::locale::classic()); +export inline auto is_c_punct = ctype_is(std::ctype_base::punct, std::locale::classic()); +export inline auto is_c_xdigit = ctype_is(std::ctype_base::xdigit, std::locale::classic()); +export inline auto is_c_blank = ctype_is(std::ctype_base::blank, std::locale::classic()); +export inline auto is_c_alnum = ctype_is(std::ctype_base::alnum, std::locale::classic()); +export inline auto is_c_graph = ctype_is(std::ctype_base::graph, std::locale::classic()); +//NOLINTEND } // namespace nihil diff --git a/nihil.util/test_ctype.cc b/nihil.util/ctype.test.cc index 62721d1..d000b45 100644 --- a/nihil.util/test_ctype.cc +++ b/nihil.util/ctype.test.cc @@ -1,11 +1,12 @@ -/* - * This source code is released into the public domain. - */ +// This source code is released into the public domain. #include <catch2/catch_test_macros.hpp> +import nihil.std; import nihil.util; +namespace { + TEST_CASE("ctype: space", "[ctype]") { auto is_utf8_space = nihil::ctype_is(std::ctype_base::space, @@ -371,3 +372,5 @@ TEST_CASE("ctype: graph", "[ctype]") { REQUIRE(nihil::is_c_graph(L'\u0430') == false); REQUIRE(is_utf8_graph(L'\u0430') == true); } + +} // anonymous namespace diff --git a/nihil.util/next_word.ccm b/nihil.util/next_word.ccm index c5d3ad7..89eeaee 100644 --- a/nihil.util/next_word.ccm +++ b/nihil.util/next_word.ccm @@ -1,44 +1,27 @@ -/* - * This source code is released into the public domain. - */ - -module; - -#include <algorithm> -#include <locale> -#include <ranges> -#include <string> -#include <utility> - +// This source code is released into the public domain. export module nihil.util:next_word; import :skipws; namespace nihil { -/* - * Return the next word from a string_view. Skips leading whitespace, so - * calling this repeatedly will return each word from the string. - */ - -export template<typename Char> [[nodiscard]] -auto next_word(std::basic_string_view<Char> text, - std::locale const &locale = std::locale()) - -> std::pair<std::basic_string_view<Char>, - std::basic_string_view<Char>> +// Return the next word from a string_view. Skips leading whitespace, so +// calling this repeatedly will return each word from the string. +export template <typename Char> +[[nodiscard]] +auto next_word(std::basic_string_view<Char> text, std::locale const &locale = std::locale()) + -> std::pair<std::basic_string_view<Char>, std::basic_string_view<Char>> { text = skipws(text, locale); auto is_space = ctype_is(std::ctype_base::space, locale); auto split_pos = std::ranges::find_if(text, is_space); - return {{std::ranges::begin(text), split_pos}, - {split_pos, std::ranges::end(text)}}; + return {{std::ranges::begin(text), split_pos}, {split_pos, std::ranges::end(text)}}; } -export template<typename Char> -auto next_word(std::basic_string_view<Char> *text, - std::locale const &locale = std::locale()) +export template <typename Char> +auto next_word(std::basic_string_view<Char> *text, std::locale const &locale = std::locale()) -> std::basic_string_view<Char> { auto [word, rest] = next_word(*text, locale); diff --git a/nihil.util/test_next_word.cc b/nihil.util/next_word.test.cc index 7e61237..87d491a 100644 --- a/nihil.util/test_next_word.cc +++ b/nihil.util/next_word.test.cc @@ -1,14 +1,12 @@ -/* - * This source code is released into the public domain. - */ - -#include <locale> -#include <string> +// This source code is released into the public domain. #include <catch2/catch_test_macros.hpp> +import nihil.std; import nihil.util; +namespace { + TEST_CASE("next_word: basic", "[next_word]") { using namespace std::literals; @@ -63,3 +61,5 @@ TEST_CASE("next_word: locale", "[next_word]") REQUIRE(words.first == L"foo"); REQUIRE(words.second == L"\u2003bar\u2003baz"); } + +} // anonymous namespace diff --git a/nihil.util/nihil.util.ccm b/nihil.util/nihil.util.ccm index 89510c9..d8628a4 100644 --- a/nihil.util/nihil.util.ccm +++ b/nihil.util/nihil.util.ccm @@ -1,14 +1,10 @@ -/* - * This source code is released into the public domain. - */ - -module; - +// This source code is released into the public domain. export module nihil.util; export import :capture_stream; export import :ctype; export import :parse_size; export import :next_word; +export import :save_errno; export import :skipws; export import :tabulate; diff --git a/nihil.util/parse_size.ccm b/nihil.util/parse_size.ccm index c95ac50..7fc3fa4 100644 --- a/nihil.util/parse_size.ccm +++ b/nihil.util/parse_size.ccm @@ -1,20 +1,7 @@ -/* - * This source code is released into the public domain. - */ - -module; - -#include <algorithm> -#include <coroutine> -#include <cstdint> -#include <expected> -#include <ranges> -#include <string> -#include <system_error> -#include <utility> - +// This source code is released into the public domain. export module nihil.util:parse_size; +import nihil.std; import nihil.core; import nihil.error; import nihil.monad; @@ -23,53 +10,50 @@ import :ctype; namespace nihil { -template<typename Char> +export template <typename Char> auto get_multiplier(Char c) -> std::expected<std::uint64_t, error> { auto ret = std::uint64_t{1}; + // clang-format off switch (c) { - case 'p': case 'P': ret *= 1024; //NOLINT - case 't': case 'T': ret *= 1024; //NOLINT - case 'g': case 'G': ret *= 1024; //NOLINT - case 'm': case 'M': ret *= 1024; //NOLINT - case 'k': case 'K': ret *= 1024; //NOLINT + case 'p': case 'P': ret *= 1024; // NOLINT + case 't': case 'T': ret *= 1024; // NOLINT + case 'g': case 'G': ret *= 1024; // NOLINT + case 'm': case 'M': ret *= 1024; // NOLINT + case 'k': case 'K': ret *= 1024; // NOLINT return ret; default: - return std::unexpected(error(errc::invalid_unit)); + return error(errc::invalid_unit); } + // clang-format on } -/* - * Parse a string containing a human-formatted size, such as "1024" - * or "4g". Parsing is always done in the "C" locale and does not - * recognise thousands separators or negative numbers. - */ -export template<typename T, typename Char> [[nodiscard]] -auto parse_size(std::basic_string_view<Char> str) - -> std::expected<T, error> +// Parse a string containing a human-formatted size, such as "1024" +// or "4g". Parsing is always done in the "C" locale and does not +// recognise thousands separators or negative numbers. +export template <typename T, typename Char> +[[nodiscard]] +auto parse_size(std::basic_string_view<Char> str) -> std::expected<T, error> { // Extract the numeric part of the string. auto it = std::ranges::find_if_not(str, is_c_digit); - auto num_str = std::basic_string_view<Char>( - std::ranges::begin(str), it); + auto num_str = std::basic_string_view<Char>(std::ranges::begin(str), it); if (num_str.empty()) - co_return std::unexpected(error(errc::empty_string)); + co_return error(errc::empty_string); auto ret = T{0}; for (auto c : num_str) { if (ret > (std::numeric_limits<T>::max() / 10)) - co_return std::unexpected(error( - std::errc::result_out_of_range)); + co_return error(std::errc::result_out_of_range); ret *= 10; auto digit = static_cast<T>(c - '0'); if ((std::numeric_limits<T>::max() - digit) < ret) - co_return std::unexpected(error( - std::errc::result_out_of_range)); + co_return error(std::errc::result_out_of_range); ret += digit; } @@ -81,27 +65,26 @@ auto parse_size(std::basic_string_view<Char> str) if (it != str.end()) // Multiplier is more than one character. - co_return std::unexpected(error(errc::invalid_unit)); + co_return error(errc::invalid_unit); auto mult = co_await get_multiplier(mchar); if (std::cmp_greater(ret, std::numeric_limits<T>::max() / mult)) - co_return std::unexpected(error( - std::errc::result_out_of_range)); + co_return error(std::errc::result_out_of_range); - co_return ret * mult; + co_return ret *mult; } -export template<typename T> -[[nodiscard]] inline auto parse_size(char const *s) +export template <typename T> +[[nodiscard]] auto parse_size(char const *s) { return parse_size<T>(std::string_view(s)); } -export template<typename T> -[[nodiscard]] inline auto parse_size(wchar_t const *s) +export template <typename T> +[[nodiscard]] auto parse_size(wchar_t const *s) { return parse_size<T>(std::wstring_view(s)); } -} +} // namespace nihil diff --git a/nihil.util/test_parse_size.cc b/nihil.util/parse_size.test.cc index 692039b..d79912a 100644 --- a/nihil.util/test_parse_size.cc +++ b/nihil.util/parse_size.test.cc @@ -1,12 +1,8 @@ -/* - * This source code is released into the public domain. - */ - -#include <cstdint> -#include <system_error> +// This source code is released into the public domain. #include <catch2/catch_test_macros.hpp> +import nihil.std; import nihil.core; import nihil.error; import nihil.util; diff --git a/nihil.util/save_errno.ccm b/nihil.util/save_errno.ccm new file mode 100644 index 0000000..27567f8 --- /dev/null +++ b/nihil.util/save_errno.ccm @@ -0,0 +1,35 @@ +// This source code is released into the public domain. +module; + +#include <cerrno> + +export module nihil.util:save_errno; + +// save_errno: save the current value of errno and restore it when we're destroyed. +// this allows wrappers around C functions that use errno to preserve the caller's +// errno value. + +namespace nihil { + +export struct save_errno final +{ + save_errno() : m_errno(errno) {} + + ~save_errno() + { + errno = m_errno; + } + + // Not copyable + save_errno(const save_errno&) = delete; + auto operator=(const save_errno&) -> save_errno & = delete; + + // Not movable + save_errno(save_errno&&) = delete; + auto operator=(save_errno&&) -> save_errno & = delete; + +private: + int m_errno; +}; + +} // namespace nihil diff --git a/nihil.util/skipws.ccm b/nihil.util/skipws.ccm index 4813ae8..0a15775 100644 --- a/nihil.util/skipws.ccm +++ b/nihil.util/skipws.ccm @@ -1,27 +1,14 @@ -/* - * This source code is released into the public domain. - */ - -module; - -#include <algorithm> -#include <locale> -#include <ranges> -#include <string> - +// This source code is released into the public domain. export module nihil.util:skipws; import :ctype; namespace nihil { -/* - * Remove leading whitespace from a string. - */ - -export template<typename Char> [[nodiscard]] -auto skipws(std::basic_string_view<Char> text, - std::locale const &locale = std::locale()) +// Remove leading whitespace from a string. +export template <typename Char> +[[nodiscard]] +auto skipws(std::basic_string_view<Char> text, std::locale const &locale = std::locale()) -> std::basic_string_view<Char> { auto is_space = ctype_is(std::ctype_base::space, locale); @@ -29,10 +16,8 @@ auto skipws(std::basic_string_view<Char> text, return {nonws, std::ranges::end(text)}; } -export template<typename Char> -auto skipws(std::basic_string_view<Char> *text, - std::locale const &locale = std::locale()) - -> void +export template <typename Char> +auto skipws(std::basic_string_view<Char> *text, std::locale const &locale = std::locale()) -> void { *text = skipws(*text, locale); } diff --git a/nihil.util/test_skipws.cc b/nihil.util/skipws.test.cc index 837c1f3..0cb741c 100644 --- a/nihil.util/test_skipws.cc +++ b/nihil.util/skipws.test.cc @@ -1,17 +1,14 @@ -/* - * This source code is released into the public domain. - */ - -#include <locale> -#include <string> -using namespace std::literals; +// This source code is released into the public domain. #include <catch2/catch_test_macros.hpp> +import nihil.std; import nihil.util; TEST_CASE("skipws: basic", "[skipws]") { + using namespace std::literals; + REQUIRE(nihil::skipws("foo"sv) == "foo"); REQUIRE(nihil::skipws(" foo"sv) == "foo"); REQUIRE(nihil::skipws("foo "sv) == "foo "); @@ -20,6 +17,8 @@ TEST_CASE("skipws: basic", "[skipws]") TEST_CASE("skipws: pointer", "[skipws]") { + using namespace std::literals; + auto s = "foo"sv; nihil::skipws(&s); REQUIRE(s == "foo"); @@ -39,6 +38,8 @@ TEST_CASE("skipws: pointer", "[skipws]") TEST_CASE("skipws: locale", "[skipws]") { + using namespace std::literals; + // Assume the default locale is C. REQUIRE(nihil::skipws(L"\u2003foo"sv) == L"\u2003foo"); REQUIRE(nihil::skipws(L"\u2003foo"sv, std::locale("C.UTF-8")) == L"foo"); diff --git a/nihil.util/tabulate.ccm b/nihil.util/tabulate.ccm index 5998b24..8f5c22e 100644 --- a/nihil.util/tabulate.ccm +++ b/nihil.util/tabulate.ccm @@ -1,53 +1,38 @@ -/* - * This source code is released into the public domain. - */ - -module; - -#include <algorithm> -#include <cstdlib> -#include <format> -#include <ranges> -#include <iterator> -#include <vector> - +// This source code is released into the public domain. export module nihil.util:tabulate; +import nihil.std; import nihil.error; import :ctype; namespace nihil { -/* - * tabulate: format the given range in an ASCII table and write the output - * to the given output iterator. The range's values will be converted to - * strings as if by std::format. - * - * tabulate is implemented by copying the range; this allows it to work on - * input/forward ranges at the cost of slightly increased memory use. - * - * The table spec is a string consisting of zero or more field formats, - * formatted as {flags:fieldname}; both flags and fieldname are optional. - * If there are fewer field formats than fields, the remaining fields - * are formatted as if by {:}. - * - * The following flags are supported: - * - * < left-align this column (default) - * > right-align this column - */ +// tabulate: format the given range in an ASCII table and write the output +// to the given output iterator. The range's values will be converted to +// strings as if by std::format. +// +// tabulate is implemented by copying the range; this allows it to work on +// input/forward ranges at the cost of slightly increased memory use. +// +// The table spec is a string consisting of zero or more field formats, +// formatted as {flags:fieldname}; both flags and fieldname are optional. +// If there are fewer field formats than fields, the remaining fields +// are formatted as if by {:}. +// +// The following flags are supported: +// +// < left-align this column (default) +// > right-align this column // Exception thrown when a table spec is invalid. export struct table_spec_error : error { - table_spec_error(std::string_view what) + explicit table_spec_error(std::string_view what) : error(what) { } }; -/* - * The specification for a single field. - */ +// The specification for a single field. template<typename Char> struct field_spec { enum align_t { left, right }; diff --git a/nihil.util/test_tabulate.cc b/nihil.util/tabulate.test.cc index 8dee796..408cc18 100644 --- a/nihil.util/test_tabulate.cc +++ b/nihil.util/tabulate.test.cc @@ -1,13 +1,8 @@ -/* - * This source code is released into the public domain. - */ - -#include <iterator> -#include <string> -#include <vector> +// This source code is released into the public domain. #include <catch2/catch_test_macros.hpp> +import nihil.std; import nihil.util; using namespace std::literals; |
