Skip to content

Commit

Permalink
feat(format_args): make them default constructible and add empty() fu…
Browse files Browse the repository at this point in the history
…nction (#98)
  • Loading branch information
Viatorus authored Sep 27, 2024
1 parent 00a37cf commit 75de22e
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 10 deletions.
8 changes: 4 additions & 4 deletions include/emio/buffer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,8 @@ class memory_buffer final : public buffer {
* @param capacity The initial capacity.
*/
constexpr explicit memory_buffer(const size_t capacity) noexcept {
// Request at least the internal storage size.
static_cast<void>(request_write_area(0, std::max(vec_.capacity(), capacity)));
// Request at least the internal storage size. Should never fail.
request_write_area(0, std::max(vec_.capacity(), capacity)).value();
}

constexpr memory_buffer(const memory_buffer& other)
Expand Down Expand Up @@ -206,7 +206,7 @@ class memory_buffer final : public buffer {
constexpr void reset() noexcept {
used_ = 0;
vec_.clear();
static_cast<void>(request_write_area(0, vec_.capacity()));
request_write_area(0, vec_.capacity()).value();
}

/**
Expand Down Expand Up @@ -527,7 +527,7 @@ class iterator_buffer<std::back_insert_iterator<Container>, Capacity> final : pu
*/
constexpr explicit iterator_buffer(std::back_insert_iterator<Container> it) noexcept
: container_{detail::get_container(it)} {
static_cast<void>(request_write_area(0, std::min(container_.capacity(), Capacity)));
request_write_area(0, std::min(container_.capacity(), Capacity)).value();
}

iterator_buffer(const iterator_buffer&) = delete;
Expand Down
23 changes: 21 additions & 2 deletions include/emio/detail/args.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ class arg {
template <typename Arg>
class args_span {
public:
args_span() = default;
args_span(const args_span&) = delete;
args_span(args_span&&) = delete;
args_span& operator=(const args_span&) = delete;
Expand All @@ -154,33 +155,51 @@ class args_span {
args_span(std::span<const Arg> args) : args_{args} {}

private:
std::span<const Arg> args_;
std::span<const Arg> args_{};
};

template <typename Arg>
class args_span_with_str : public args_span<Arg> {
public:
args_span_with_str() = default;
args_span_with_str(const args_span_with_str&) = delete;
args_span_with_str(args_span_with_str&&) = delete;
args_span_with_str& operator=(const args_span_with_str&) = delete;
args_span_with_str& operator=(args_span_with_str&&) = delete;
~args_span_with_str() = default;

/**
* Returns the validated format/scan string.
* @return The view or invalid_format if the validation failed.
*/
[[nodiscard]] result<std::string_view> get_str() const noexcept {
return str_.get();
}

/**
* Returns if it is just a plain string without arguments.
* @return True, if the string does not contain any escape sequences, replacement fields or arguments, otherwise
* false.
*/
[[nodiscard]] constexpr bool is_plain_str() const noexcept {
return str_.is_plain_str();
}

/**
* Returns if it is an empty string without arguments.
* @return True, if the string is empty without any arguments, otherwise false.
*/
[[nodiscard]] constexpr bool empty() const noexcept {
return str_.empty();
}

protected:
// NOLINTNEXTLINE(modernize-pass-by-value): false-positive since no dynamic allocation takes place
args_span_with_str(const validated_string_storage& str, std::span<const Arg> args)
: args_span<Arg>(args), str_{str} {}

private:
validated_string_storage str_;
validated_string_storage str_{};
};

template <typename Arg, size_t NbrOfArgs>
Expand Down
12 changes: 10 additions & 2 deletions include/emio/detail/validated_string_storage.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class validated_string_storage {
if (Trait::template validate_string<Args...>(s)) {
return {{false, s}};
}
return {};
return {{false, err::invalid_format}};
}

constexpr validated_string_storage() noexcept = default;
Expand All @@ -47,6 +47,14 @@ class validated_string_storage {
return str_.first;
}

/**
* Returns if it is an empty string.
* @return True, if the string is empty, otherwise false.
*/
[[nodiscard]] constexpr bool empty() const noexcept {
return is_plain_str() && str_.second->empty();
}

/**
* Returns the validated format/scan string.
* @return The view or invalid_format if the validation failed.
Expand All @@ -60,7 +68,7 @@ class validated_string_storage {
constexpr validated_string_storage(const std::pair<bool, result<std::string_view>>& str) noexcept : str_{str} {}

// Wonder why pair and not two variables? Look at this bug report: https://github.com/llvm/llvm-project/issues/67731
std::pair<bool, result<std::string_view>> str_{false, err::invalid_format};
std::pair<bool, result<std::string_view>> str_{true, ""};
};

} // namespace emio::detail
4 changes: 2 additions & 2 deletions include/emio/result.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ class [[nodiscard]] result<Value> {
*/
constexpr std::remove_reference_t<Value>* operator->() noexcept {
EMIO_Z_DEV_ASSERT(has_value());
return &*value_;
return &*value_; // NOLINT(bugprone-unchecked-optional-access): assumed
}

/**
Expand All @@ -212,7 +212,7 @@ class [[nodiscard]] result<Value> {
*/
constexpr const std::remove_reference_t<Value>* operator->() const noexcept {
EMIO_Z_DEV_ASSERT(has_value());
return &*value_;
return &*value_; // NOLINT(bugprone-unchecked-optional-access): assumed
}

/**
Expand Down
32 changes: 32 additions & 0 deletions test/unit_test/test_format_string.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,17 +112,49 @@ TEST_CASE("runtime format_string", "[format_string]") {
}

TEST_CASE("is_plain_str", "[format_string]") {
CHECK(emio::format_string<>{""}.is_plain_str());
CHECK(emio::format_string<>{""}.empty());
CHECK(emio::format_string<>{"abc 123"}.is_plain_str());
CHECK(emio::format_string<>{emio::runtime("abc 123")}.is_plain_str());
CHECK(emio::make_format_args(emio::runtime("abc 123")).is_plain_str());

CHECK_FALSE(emio::format_string<>{"abc 123 {{"}.is_plain_str());
CHECK_FALSE(emio::format_string<>{"abc 123 {{"}.empty());
CHECK_FALSE(emio::format_string<>{emio::runtime("abc 123 {{")}.is_plain_str());
CHECK_FALSE(emio::format_string<>{emio::runtime("abc 123 {{")}.empty());
CHECK_FALSE(emio::make_format_args(emio::runtime("abc 123 {{")).is_plain_str());
CHECK_FALSE(emio::make_format_args(emio::runtime("abc 123 {{")).empty());

CHECK_FALSE(emio::format_string<>{"abc }} 123"}.is_plain_str());
CHECK_FALSE(emio::format_string<>{emio::runtime("abc }} 123")}.is_plain_str());
CHECK_FALSE(emio::make_format_args(emio::runtime("abc }} 123")).is_plain_str());

CHECK_FALSE(emio::format_string<>{emio::runtime("abc {} 123")}.is_plain_str());
CHECK_FALSE(emio::make_format_args(emio::runtime("abc {} 123")).is_plain_str());

CHECK_FALSE(emio::make_format_args(emio::runtime(""), 1).is_plain_str());
CHECK_FALSE(emio::make_format_args(emio::runtime(""), 1).empty());

CHECK_FALSE(precompiled_format_str.is_plain_str());
CHECK_FALSE(emio::format_string<>{emio::runtime(format_str)}.is_plain_str());
}

TEST_CASE("default constructed format_args", "[format_string]") {
const auto check = [](const auto& args) {
CHECK(args.is_plain_str());
CHECK(args.get_str().value().empty());
CHECK(args.get_args().empty());
CHECK(args.empty());

CHECK(emio::vformat(args).value() == "");
CHECK(emio::format("{}", args).value() == "");
};

SECTION("default constructed") {
emio::format_args args;
check(args);
}
SECTION("empty constructed") {
check(emio::make_format_args(""));
}
}

0 comments on commit 75de22e

Please sign in to comment.