diff options
| author | Lexi Winter <lexi@le-fay.org> | 2025-06-27 12:08:58 +0100 |
|---|---|---|
| committer | Lexi Winter <lexi@le-fay.org> | 2025-06-27 12:08:58 +0100 |
| commit | 001c9917ace09f7b1c80d96eb067e1d37e86c546 (patch) | |
| tree | 89e360961b9659a8c6b897c5412b7d6834b8eed9 /nihil.ucl/string.ccm | |
| parent | 90aa957ca9b7c217af7569009d1675e0f3ff8e9b (diff) | |
| download | nihil-001c9917ace09f7b1c80d96eb067e1d37e86c546.tar.gz nihil-001c9917ace09f7b1c80d96eb067e1d37e86c546.tar.bz2 | |
improve error handling
Diffstat (limited to 'nihil.ucl/string.ccm')
| -rw-r--r-- | nihil.ucl/string.ccm | 196 |
1 files changed, 164 insertions, 32 deletions
diff --git a/nihil.ucl/string.ccm b/nihil.ucl/string.ccm index f8dc1cd..9127b2d 100644 --- a/nihil.ucl/string.ccm +++ b/nihil.ucl/string.ccm @@ -5,13 +5,18 @@ module; #include <cstdlib> +#include <expected> +#include <format> +#include <iosfwd> #include <string> #include <ucl.h> export module nihil.ucl:string; +import nihil; import :object; +import :type; namespace nihil::ucl { @@ -19,6 +24,7 @@ export struct string final : object { using contained_type = std::string_view; inline static constexpr object_type ucl_type = object_type::string; + // string is a container of char using value_type = char const; using size_type = std::size_t; using difference_type = std::ptrdiff_t; @@ -26,57 +32,130 @@ export struct string final : object { using pointer = value_type *; using iterator = pointer; - // Create a new string from a UCL object. - string(ref_t, ::ucl_object_t const *uobj); - string(noref_t, ::ucl_object_t *uobj); - - // Create a new empty string. + /* + * Create a new empty string. Throws std::system_error on failure. + */ string(); - // Create a new UCL string from a string. - explicit string(std::string_view value); + /* + * Create a string from a value. Throws std::system_error on failure. + */ + explicit string(std::string_view); + + /* + * Create a string from a C literal. Throws std::system_error + * on failure. + */ + explicit string(char const *); + + /* + * Create a string from a contiguous range. The range's value type + * must be char. Throws std::system_error on failure. + */ + template<std::ranges::contiguous_range Range> + requires (!std::same_as<std::string_view, Range> && + std::same_as<char, std::ranges::range_value_t<Range>>) + explicit string(Range &&range) + : string(std::string_view(std::ranges::begin(range), + std::ranges::end(range))) + {} - // Create a new UCL string from an iterator pair. - template<std::contiguous_iterator Iterator> - string(Iterator first, Iterator last) - : string(std::string_view(first, last)) + /* + * Create a string from a non-contiguous range. This requires a + * temporary value due to limitations of the UCL C API. + */ + template<std::ranges::range Range> + requires (!std::ranges::contiguous_range<Range> && + std::same_as<char, std::ranges::range_value_t<Range>>) + explicit string(Range &&range) + : string(std::string(std::from_range, range)) {} + /* + * Create a string from an iterator pair. The iterator's value type + * must be char. If the iterator pair is not contiguous, the value + * will be copied to a temporary first. + * + * Throws std::system_error on failure. + */ template<std::input_iterator Iterator> - requires(!std::contiguous_iterator<Iterator>) + requires (std::same_as<char, std::iter_value_t<Iterator>>) string(Iterator first, Iterator last) - : string(std::string(first, last)) + : string(std::ranges::subrange(first, last)) {} - // Create a new UCL string from a range. - string(std::from_range_t, std::ranges::range auto &&range) - : string(std::ranges::begin(range), - std::ranges::end(range)) - {} + /* + * Create a new string from a UCL object. Throws type_mismatch + * on failure. + */ + string(ref_t, ::ucl_object_t const *uobj); + string(noref_t, ::ucl_object_t *uobj); // Return the value of this string. - auto value(this string const &self) -> contained_type; + [[nodiscard]] auto value(this string const &self) -> contained_type; // Return the size of this string. - auto size(this string const &self) -> size_type; + [[nodiscard]] auto size(this string const &self) -> size_type; // Test if this string is empty. - auto empty(this string const &self) -> bool; + [[nodiscard]] auto empty(this string const &self) -> bool; // Access this string's data - auto data(this string const &self) -> pointer; + [[nodiscard]] auto data(this string const &self) -> pointer; // Iterator access - auto begin(this string const &self) -> iterator; - auto end(this string const &self) -> iterator; + [[nodiscard]] auto begin(this string const &self) -> iterator; + [[nodiscard]] auto end(this string const &self) -> iterator; }; /* + * String constructors. These return an error instead of throwing. + */ + +// Empty string +export [[nodiscard]] auto +make_string() -> std::expected<string, error>; + +// From string_view +export [[nodiscard]] auto +make_string(std::string_view) -> std::expected<string, error>; + +// From C literal +export [[nodiscard]] auto +make_string(char const *) -> std::expected<string, error>; + +// From contiguous range +export template<std::ranges::contiguous_range Range> +requires (!std::same_as<std::string_view, Range> && + std::same_as<char, std::ranges::range_value_t<Range>>) +[[nodiscard]] auto make_string(Range &&range) +{ + return make_string(std::string_view(range)); +} + +// From non-contiguous range +export template<std::ranges::range Range> +requires (!std::ranges::contiguous_range<Range> && + std::same_as<char, std::ranges::range_value_t<Range>>) +[[nodiscard]] auto make_string(Range &&range) +{ + return make_string(std::string(std::from_range, range)); +} + +// From iterator pair +export template<std::input_iterator Iterator> +requires (std::same_as<char, std::iter_value_t<Iterator>>) +[[nodiscard]] auto make_string(Iterator first, Iterator last) +{ + return make_string(std::ranges::subrange(first, last)); +} + +/* * Comparison operators. */ -export auto operator== (string const &a, string const &b) -> bool; -export auto operator<=> (string const &a, string const &b) +export [[nodiscard]] auto operator== (string const &a, string const &b) -> bool; +export [[nodiscard]] auto operator<=> (string const &a, string const &b) -> std::strong_ordering; /* @@ -84,15 +163,68 @@ export auto operator<=> (string const &a, string const &b) * construct a temporary UCL object. */ -export auto operator==(string const &lhs, std::string_view rhs) -> bool; -export auto operator==(string const &lhs, std::string const &rhs) -> bool; -export auto operator==(string const &lhs, char const *rhs) -> bool; +export [[nodiscard]] auto operator==(string const &lhs, + std::string_view rhs) -> bool; + +export [[nodiscard]] auto operator==(string const &lhs, + std::string const &rhs) -> bool; + +export [[nodiscard]] auto operator==(string const &lhs, + char const *rhs) -> bool; -export auto operator<=>(string const &lhs, std::string_view rhs) +export [[nodiscard]] auto operator<=>(string const &lhs, + std::string_view rhs) -> std::strong_ordering; -export auto operator<=>(string const &lhs, std::string const &rhs) + +export [[nodiscard]] auto operator<=>(string const &lhs, + std::string const &rhs) -> std::strong_ordering; -export auto operator<=>(string const &lhs, char const *rhs) + +export [[nodiscard]] auto operator<=>(string const &lhs, + char const *rhs) -> std::strong_ordering; +/* + * Print a string to a stream. + */ +export auto operator<<(std::ostream &, string const &) -> std::ostream &; + +/* + * Literal operator. + */ +inline namespace literals { + export constexpr auto operator""_ucl (char const *s, std::size_t n) + -> string + { + return string(std::string_view(s, n)); + } +} // namespace nihil::ucl::literals + } // namespace nihil::ucl + +namespace nihil { inline namespace literals { + export using namespace ::nihil::ucl::literals; +}} // namespace nihil::literals + +/* + * std::formatter for a string. This provides the same format operations + * as std::formatter<std::string_view>. + */ +export template<> +struct std::formatter<nihil::ucl::string, char> +{ + std::formatter<std::string_view> base_formatter; + + template<class ParseContext> + constexpr ParseContext::iterator parse(ParseContext& ctx) + { + return base_formatter.parse(ctx); + } + + template<class FmtContext> + FmtContext::iterator format(nihil::ucl::string const &o, + FmtContext& ctx) const + { + return base_formatter.format(o.value(), ctx); + } +}; |
