pw_span: Move pw_string, pw_unit_test to std::span
- Update pw_string and the test framework to std::span.
- Have pw_string pass std::span by value, for consistency with other
uses in Pigweed.
- Recommend using template specializations instead of overloads for
custom ToString implementations. Template specializations are
preferred because the main ToString definition is a template.
Change-Id: Ib67c6bce1752c4a90e2138bdb1f20c6671f55d50
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/12841
Reviewed-by: Keir Mierle <keir@google.com>
Commit-Queue: Wyatt Hepler <hepler@google.com>
diff --git a/pw_kvs/key_value_store_map_test.cc b/pw_kvs/key_value_store_map_test.cc
index ac6a83b..9919cae 100644
--- a/pw_kvs/key_value_store_map_test.cc
+++ b/pw_kvs/key_value_store_map_test.cc
@@ -13,13 +13,25 @@
// the License.
#include <cstdlib>
-#include <random>
#include <set>
+#include <span>
#include <string>
#include <string_view>
#include <unordered_map>
#include <unordered_set>
+// TODO(hepler): Clang 11 fails to compile this file if <random> is included
+// before <span>. It seems to miss the definition of std::span. This compiles
+// correctly in GCC 9.
+//
+// In file included from ../pw_kvs/key_value_store_map_test.cc:33:
+// In file included from ../pw_unit_test/public_overrides/gtest/gtest.h:20:
+// In file included from ../pw_unit_test/public/pw_unit_test/framework.h:32:
+// ../pw_string/public/pw_string/string_builder.h:267:25: error:
+// implicit instantiation of undefined template
+// 'std::span<char, 18446744073709551615>'
+#include <random>
+
#define DUMP_KVS_CONTENTS 0
#if DUMP_KVS_CONTENTS
@@ -33,7 +45,6 @@
#include "pw_kvs/internal/entry.h"
#include "pw_kvs/key_value_store.h"
#include "pw_log/log.h"
-#include "pw_span/span.h"
namespace pw::kvs {
namespace {
@@ -249,7 +260,7 @@
char value[kMaxValueLength + 1] = {};
EXPECT_EQ(Status::OK,
- item.Get(as_writable_bytes(span(value))).status());
+ item.Get(std::as_writable_bytes(std::span(value))).status());
EXPECT_EQ(map_entry->second, std::string(value));
}
}
@@ -262,7 +273,7 @@
StartOperation("Put", key);
EXPECT_LE(value.size(), kMaxValueLength);
- Status result = kvs_.Put(key, as_bytes(span(value)));
+ Status result = kvs_.Put(key, std::as_bytes(std::span(value)));
if (key.empty() || key.size() > internal::Entry::kMaxKeyLength) {
EXPECT_EQ(Status::INVALID_ARGUMENT, result);
diff --git a/pw_string/format.cc b/pw_string/format.cc
index b069fe8..c9ed379 100644
--- a/pw_string/format.cc
+++ b/pw_string/format.cc
@@ -18,7 +18,7 @@
namespace pw::string {
-StatusWithSize Format(const span<char>& buffer, const char* format, ...) {
+StatusWithSize Format(std::span<char> buffer, const char* format, ...) {
va_list args;
va_start(args, format);
const StatusWithSize result = FormatVaList(buffer, format, args);
@@ -27,7 +27,7 @@
return result;
}
-StatusWithSize FormatVaList(const span<char>& buffer,
+StatusWithSize FormatVaList(std::span<char> buffer,
const char* format,
va_list args) {
if (buffer.empty()) {
diff --git a/pw_string/format_test.cc b/pw_string/format_test.cc
index ad7e39e..dfaac7b 100644
--- a/pw_string/format_test.cc
+++ b/pw_string/format_test.cc
@@ -15,9 +15,9 @@
#include "pw_string/format.h"
#include <cstdarg>
+#include <span>
#include "gtest/gtest.h"
-#include "pw_span/span.h"
namespace pw::string {
namespace {
@@ -41,7 +41,7 @@
}
TEST(Format, EmptyBuffer_ReturnsResourceExhausted) {
- auto result = Format(span<char>(), "?");
+ auto result = Format(std::span<char>(), "?");
EXPECT_EQ(Status::RESOURCE_EXHAUSTED, result.status());
EXPECT_EQ(0u, result.size());
@@ -65,7 +65,9 @@
EXPECT_STREQ("2big", buffer);
}
-StatusWithSize CallFormatWithVaList(span<char> buffer, const char* fmt, ...) {
+StatusWithSize CallFormatWithVaList(std::span<char> buffer,
+ const char* fmt,
+ ...) {
va_list args;
va_start(args, fmt);
diff --git a/pw_string/public/pw_string/format.h b/pw_string/public/pw_string/format.h
index 72198bf..5c0adfe 100644
--- a/pw_string/public/pw_string/format.h
+++ b/pw_string/public/pw_string/format.h
@@ -22,9 +22,9 @@
// the null terminator.
#include <cstdarg>
+#include <span>
#include "pw_preprocessor/compiler.h"
-#include "pw_span/span.h"
#include "pw_status/status_with_size.h"
namespace pw::string {
@@ -40,12 +40,12 @@
// Status::INVALID_ARGUMENT if there was a formatting error.
//
PW_PRINTF_FORMAT(2, 3)
-StatusWithSize Format(const span<char>& buffer, const char* format, ...);
+StatusWithSize Format(std::span<char> buffer, const char* format, ...);
// Writes a printf-style formatted string with va_list-packed arguments to the
// provided buffer, similarly to std::vsnprintf. The return value is the same as
// above.
-StatusWithSize FormatVaList(const span<char>& buffer,
+StatusWithSize FormatVaList(std::span<char> buffer,
const char* format,
va_list args);
diff --git a/pw_string/public/pw_string/string_builder.h b/pw_string/public/pw_string/string_builder.h
index 1204e71..96a88ef 100644
--- a/pw_string/public/pw_string/string_builder.h
+++ b/pw_string/public/pw_string/string_builder.h
@@ -17,12 +17,12 @@
#include <cstdarg>
#include <cstddef>
#include <cstring>
+#include <span>
#include <string_view>
#include <type_traits>
#include <utility>
#include "pw_preprocessor/compiler.h"
-#include "pw_span/span.h"
#include "pw_status/status.h"
#include "pw_status/status_with_size.h"
#include "pw_string/to_string.h"
@@ -42,30 +42,31 @@
// also supports std::string-like append functions and printf-style output.
//
// StringBuilder uses the ToString function to support arbitrary types. Defining
-// a ToString overload in the pw::string namespace allows writing that type to a
-// StringBuilder with <<.
+// a ToString template specialization overload in the pw namespace allows
+// writing that type to a StringBuilder with <<.
//
// For example, the following ToString overload allows writing MyStatus objects
// to StringBuilders:
//
-// namespace pw::string {
+// namespace pw {
//
-// StatusWithSize ToString(MyStatus value, const span<char>& buffer) {
+// template <>
+// StatusWithSize ToString<MyStatus>(MyStatus value, std::span<char> buffer) {
// return CopyString(MyStatusString(value), buffer);
// }
//
-// } // namespace pw::string
+// } // namespace pw
//
// For complex types, it may be easier to override StringBuilder's << operator,
// similar to the standard library's std::ostream. For example:
//
-// namespace pw::string {
+// namespace pw {
//
// StringBuilder& operator<<(StringBuilder& sb, const MyType& value) {
// return sb << "MyType(" << value.foo << ", " << value.bar << ')';
// }
//
-// } // namespace pw::string
+// } // namespace pw
//
// Alternately, complex types may use a StringBuilder in their ToString, but it
// is likely to be simpler to override StringBuilder's operator<<.
@@ -85,8 +86,7 @@
class StringBuilder {
public:
// Creates an empty StringBuilder.
- constexpr StringBuilder(const span<char>& buffer)
- : buffer_(buffer), size_(0) {
+ constexpr StringBuilder(std::span<char> buffer) : buffer_(buffer), size_(0) {
NullTerminate();
}
@@ -108,9 +108,9 @@
// passed into functions that take a std::string_view.
operator std::string_view() const { return view(); }
- // Returns a span<const std::byte> representation of this StringBuffer.
- span<const std::byte> as_bytes() const {
- return span(reinterpret_cast<const std::byte*>(buffer_.data()), size_);
+ // Returns a std::span<const std::byte> representation of this StringBuffer.
+ std::span<const std::byte> as_bytes() const {
+ return std::span(reinterpret_cast<const std::byte*>(buffer_.data()), size_);
}
// Returns the StringBuilder's status, which reflects the most recent error
@@ -242,7 +242,7 @@
protected:
// Functions to support StringBuffer copies.
- constexpr StringBuilder(const span<char>& buffer, const StringBuilder& other)
+ constexpr StringBuilder(std::span<char> buffer, const StringBuilder& other)
: buffer_(buffer),
size_(other.size_),
status_(other.status_),
@@ -263,7 +263,7 @@
void SetErrorStatus(Status status);
- const span<char> buffer_;
+ const std::span<char> buffer_;
size_t size_;
Status status_;
diff --git a/pw_string/public/pw_string/to_string.h b/pw_string/public/pw_string/to_string.h
index fde6a69..463005f 100644
--- a/pw_string/public/pw_string/to_string.h
+++ b/pw_string/public/pw_string/to_string.h
@@ -21,8 +21,8 @@
// buffer has room.
//
// ToString functions may be defined for any type. This is done by providing a
-// ToString overload in the pw namespace. The overload must follow ToString's
-// semantics:
+// ToString template specialization in the pw namespace. The specialization must
+// follow ToString's semantics:
//
// 1. Always null terminate if the output buffer has room.
// 2. Return the number of characters written, excluding the null terminator,
@@ -31,13 +31,14 @@
// with the number of characters written and a status of
// RESOURCE_EXHAUSTED. Other status codes may be used for different errors.
//
-// For example, providing the following overload would allow ToString, and any
-// classes that use it, to print instances of a custom type:
+// For example, providing the following specialization would allow ToString, and
+// any classes that use it, to print instances of a custom type:
//
// namespace pw {
//
-// inline StatusWithSize ToString(const SomeCustomType& value,
-// const std::span<char>& buffer) {
+// template <>
+// StatusWithSize ToString<SomeCustomType>(const SomeCustomType& value,
+// std::span<char> buffer) {
// return /* ... implementation ... */;
// }
//
@@ -51,10 +52,10 @@
// StringBuilder may be easier to work with. StringBuilder's operator<< may be
// overloaded for custom types.
+#include <span>
#include <string_view>
#include <type_traits>
-#include "pw_span/span.h"
#include "pw_status/status.h"
#include "pw_string/type_to_string.h"
@@ -63,7 +64,7 @@
// This function provides string printing numeric types, enums, and anything
// that convertible to a std::string_view, such as std::string.
template <typename T>
-StatusWithSize ToString(const T& value, const span<char>& buffer) {
+StatusWithSize ToString(const T& value, std::span<char> buffer) {
if constexpr (std::is_same_v<std::remove_cv_t<T>, bool>) {
return string::BoolToString(value, buffer);
} else if constexpr (std::is_same_v<std::remove_cv_t<T>, char>) {
@@ -85,17 +86,18 @@
}
}
-// ToString overloads for custom types may be provided.
-inline StatusWithSize ToString(Status status, const span<char>& buffer) {
+// ToString overloads for Pigweed types. To override ToString for a custom type,
+// specialize the ToString template function.
+inline StatusWithSize ToString(Status status, std::span<char> buffer) {
return string::CopyString(status.str(), buffer);
}
-inline StatusWithSize ToString(pw_Status status, const span<char>& buffer) {
+inline StatusWithSize ToString(pw_Status status, std::span<char> buffer) {
return ToString(Status(status), buffer);
}
-inline StatusWithSize ToString(std::byte byte, const span<char>& buffer) {
- return string::IntToHexString(static_cast<uint64_t>(byte), buffer);
+inline StatusWithSize ToString(std::byte byte, std::span<char> buffer) {
+ return string::IntToHexString(static_cast<unsigned>(byte), buffer);
}
} // namespace pw
diff --git a/pw_string/public/pw_string/type_to_string.h b/pw_string/public/pw_string/type_to_string.h
index ae646ff..147badb 100644
--- a/pw_string/public/pw_string/type_to_string.h
+++ b/pw_string/public/pw_string/type_to_string.h
@@ -18,10 +18,10 @@
// in "pw_string/to_string.h" should be used instead of these functions.
#include <cstdint>
+#include <span>
#include <string_view>
#include <type_traits>
-#include "pw_span/span.h"
#include "pw_status/status_with_size.h"
namespace pw::string {
@@ -55,7 +55,7 @@
// sites pass their arguments directly and casting instructions are shared.
//
template <typename T>
-StatusWithSize IntToString(T value, const span<char>& buffer) {
+StatusWithSize IntToString(T value, std::span<char> buffer) {
if constexpr (std::is_signed_v<T>) {
return IntToString<int64_t>(value, buffer);
} else {
@@ -64,14 +64,14 @@
}
template <>
-StatusWithSize IntToString(uint64_t value, const span<char>& buffer);
+StatusWithSize IntToString(uint64_t value, std::span<char> buffer);
template <>
-StatusWithSize IntToString(int64_t value, const span<char>& buffer);
+StatusWithSize IntToString(int64_t value, std::span<char> buffer);
// Writes an integer as a hexadecimal string. Semantics match IntToString. The
// output is lowercase without a leading 0x.
-StatusWithSize IntToHexString(uint64_t value, const span<char>& buffer);
+StatusWithSize IntToHexString(uint64_t value, std::span<char> buffer);
// Rounds a floating point number to an integer and writes it as a
// null-terminated string. Returns the number of characters written, excluding
@@ -92,17 +92,17 @@
// FloatAsIntToString(INFINITY, buffer) -> writes "-inf" to the buffer
// FloatAsIntToString(-NAN, buffer) -> writes "-NaN" to the buffer
//
-StatusWithSize FloatAsIntToString(float value, const span<char>& buffer);
+StatusWithSize FloatAsIntToString(float value, std::span<char> buffer);
// Writes a bool as "true" or "false". Semantics match CopyEntireString.
-StatusWithSize BoolToString(bool value, const span<char>& buffer);
+StatusWithSize BoolToString(bool value, std::span<char> buffer);
// String used to represent null pointers.
inline constexpr std::string_view kNullPointerString("(null)");
// Writes the pointer's address or kNullPointerString. Semantics match
// CopyEntireString.
-StatusWithSize PointerToString(const void* pointer, const span<char>& buffer);
+StatusWithSize PointerToString(const void* pointer, std::span<char> buffer);
// Copies the string to the buffer, truncating if the full string does not fit.
// Always null terminates if buffer.size() > 0.
@@ -110,9 +110,9 @@
// Returns the number of characters written, excluding the null terminator. If
// the string is truncated, the status is RESOURCE_EXHAUSTED.
StatusWithSize CopyString(const std::string_view& value,
- const span<char>& buffer);
+ std::span<char> buffer);
-inline StatusWithSize CopyString(const char* value, const span<char>& buffer) {
+inline StatusWithSize CopyString(const char* value, std::span<char> buffer) {
if (value == nullptr) {
return PointerToString(value, buffer);
}
@@ -126,10 +126,10 @@
// the full string does not fit, only a null terminator is written and the
// status is RESOURCE_EXHAUSTED.
StatusWithSize CopyEntireString(const std::string_view& value,
- const span<char>& buffer);
+ std::span<char> buffer);
inline StatusWithSize CopyEntireString(const char* value,
- const span<char>& buffer) {
+ std::span<char> buffer) {
if (value == nullptr) {
return PointerToString(value, buffer);
}
@@ -144,6 +144,6 @@
// printing for unknown types, if desired. Implementations must follow the
// ToString semantics.
template <typename T>
-StatusWithSize UnknownTypeToString(const T& value, const span<char>& buffer);
+StatusWithSize UnknownTypeToString(const T& value, std::span<char> buffer);
} // namespace pw::string
diff --git a/pw_string/size_report/format_many_without_error_handling.cc b/pw_string/size_report/format_many_without_error_handling.cc
index 9d2df74..357fc2a 100644
--- a/pw_string/size_report/format_many_without_error_handling.cc
+++ b/pw_string/size_report/format_many_without_error_handling.cc
@@ -43,7 +43,7 @@
void OutputStringsToBuffer() {
#if USE_FORMAT
- auto buffer = pw::span(get_buffer, get_size);
+ auto buffer = std::span(get_buffer, get_size);
#else
char* buffer = get_buffer;
unsigned buffer_size = get_size;
diff --git a/pw_string/size_report/format_multiple.cc b/pw_string/size_report/format_multiple.cc
index 1b16a9c..b27df5a 100644
--- a/pw_string/size_report/format_multiple.cc
+++ b/pw_string/size_report/format_multiple.cc
@@ -31,7 +31,7 @@
#include "pw_string/format.h"
#define FORMAT_FUNCTION(...) \
- pw::string::Format(span(buffer, buffer_size - string_size), __VA_ARGS__)
+ pw::string::Format(std::span(buffer, buffer_size - string_size), __VA_ARGS__)
#define CHECK_RESULT(result) ProcessResult(&string_size, result)
namespace {
diff --git a/pw_string/size_report/format_single.cc b/pw_string/size_report/format_single.cc
index 04f9356..94afe76 100644
--- a/pw_string/size_report/format_single.cc
+++ b/pw_string/size_report/format_single.cc
@@ -40,7 +40,10 @@
#if USE_FORMAT
// The code for using pw::string::Format is much simpler and safer.
- return Format(span(buffer, buffer_size), "hello %s %d", get_buffer, get_size)
+ return Format(std::span(buffer, buffer_size),
+ "hello %s %d",
+ get_buffer,
+ get_size)
.size();
#else // std::snprintf
if (buffer_size == 0u) {
diff --git a/pw_string/size_report/string_builder_size_report_incremental.cc b/pw_string/size_report/string_builder_size_report_incremental.cc
index 87e7da9..a37fdbe 100644
--- a/pw_string/size_report/string_builder_size_report_incremental.cc
+++ b/pw_string/size_report/string_builder_size_report_incremental.cc
@@ -87,7 +87,7 @@
ProcessResult(buffer, &bytes, size, result);
- pw::StringBuilder sb(pw::span(buffer, size));
+ pw::StringBuilder sb(std::span(buffer, size));
sb << "This is part of the base " << 123 << false << '\n';
sb.clear();
diff --git a/pw_string/string_builder_test.cc b/pw_string/string_builder_test.cc
index ccbbcae..c36c748 100644
--- a/pw_string/string_builder_test.cc
+++ b/pw_string/string_builder_test.cc
@@ -12,18 +12,19 @@
// License for the specific language governing permissions and limitations under
// the License.
-#include "pw_string//string_builder.h"
+#include "pw_string/string_builder.h"
#include <cinttypes>
#include <cmath>
#include <cstdint>
#include <cstring>
+#include <span>
#include <string_view>
#include "gtest/gtest.h"
#include "pw_string/format.h"
-namespace {
+namespace this_pw_test {
struct CustomType {
uint32_t a;
@@ -38,18 +39,25 @@
CustomType& operator=(const CustomType&) = delete;
};
-} // namespace
+} // namespace this_pw_test
namespace pw {
-StatusWithSize ToString(const ::CustomType&, const span<char>& buffer) {
- return string::Format(buffer, ::CustomType::kToString);
+template <>
+StatusWithSize ToString<this_pw_test::CustomType>(
+ const this_pw_test::CustomType&, std::span<char> buffer) {
+ return string::Format(buffer, this_pw_test::CustomType::kToString);
}
+} // namespace pw
+
+namespace pw {
namespace {
+using this_pw_test::CustomType;
+
TEST(StringBuilder, EmptyBuffer_SizeAndMaxSizeAreCorrect) {
- StringBuilder sb(span<char>{});
+ StringBuilder sb(std::span<char>{});
EXPECT_TRUE(sb.empty());
EXPECT_EQ(0u, sb.size());
@@ -64,7 +72,7 @@
char buffer[kNoTouch.size()];
std::memcpy(buffer, kNoTouch.data(), sizeof(buffer));
- StringBuilder sb(span(buffer, 0));
+ StringBuilder sb(std::span(buffer, 0));
sb << CustomType() << " is " << 12345;
EXPECT_EQ(Status::RESOURCE_EXHAUSTED, sb.status());
@@ -75,7 +83,7 @@
char buffer[kNoTouch.size()];
std::memcpy(buffer, kNoTouch.data(), sizeof(buffer));
- StringBuilder sb(span(buffer, 0));
+ StringBuilder sb(std::span(buffer, 0));
EXPECT_FALSE(sb.append("Hello").ok());
EXPECT_EQ(kNoTouch, std::string_view(buffer, sizeof(buffer)));
@@ -85,7 +93,7 @@
char buffer[kNoTouch.size()];
std::memcpy(buffer, kNoTouch.data(), sizeof(buffer));
- StringBuilder sb(span(buffer, 0));
+ StringBuilder sb(std::span(buffer, 0));
sb.resize(0);
EXPECT_TRUE(sb.ok());
@@ -93,7 +101,7 @@
}
TEST(StringBuilder, EmptyBuffer_AppendEmpty_ResourceExhausted) {
- StringBuilder sb(span<char>{});
+ StringBuilder sb(std::span<char>{});
EXPECT_EQ(Status::OK, sb.last_status());
EXPECT_EQ(Status::OK, sb.status());
@@ -424,6 +432,14 @@
EXPECT_EQ(Status::RESOURCE_EXHAUSTED, two.status());
}
+TEST(StringBuilder, Object) {
+ StringBuffer<64> sb;
+ sb << CustomType();
+
+ EXPECT_STREQ(CustomType::kToString, sb.data());
+ EXPECT_EQ(std::strlen(CustomType::kToString), sb.size());
+}
+
TEST(MakeString, Object) {
CustomType custom;
const auto sb = MakeString<64>(custom);
diff --git a/pw_string/to_string_test.cc b/pw_string/to_string_test.cc
index 1c25023..6f4a4da 100644
--- a/pw_string/to_string_test.cc
+++ b/pw_string/to_string_test.cc
@@ -39,7 +39,7 @@
CustomType& operator=(const CustomType&) = delete;
};
-StatusWithSize ToString(const CustomType&, const span<char>& buffer) {
+StatusWithSize ToString(const CustomType&, std::span<char> buffer) {
int result =
std::snprintf(buffer.data(), buffer.size(), CustomType::kToString);
if (result < 0) {
@@ -106,13 +106,13 @@
}
TEST(ToString, Integer_EmptyBuffer_WritesNothing) {
- auto result = ToString(-1234, span(buffer, 0));
+ auto result = ToString(-1234, std::span(buffer, 0));
EXPECT_EQ(0u, result.size());
EXPECT_EQ(Status::RESOURCE_EXHAUSTED, result.status());
}
TEST(ToString, Integer_BufferTooSmall_WritesNullTerminator) {
- auto result = ToString(-1234, span(buffer, 5));
+ auto result = ToString(-1234, std::span(buffer, 5));
EXPECT_EQ(0u, result.size());
EXPECT_FALSE(result.ok());
EXPECT_STREQ("", buffer);
@@ -241,7 +241,7 @@
TEST(ToString, StringView_TooSmall_Truncates) {
std::string_view view = "kale!";
- EXPECT_EQ(3u, ToString(view, span(buffer, 4)).size());
+ EXPECT_EQ(3u, ToString(view, std::span(buffer, 4)).size());
EXPECT_STREQ("kal", buffer);
}
@@ -250,8 +250,9 @@
char test_buffer[sizeof(kOriginal)];
std::memcpy(test_buffer, kOriginal, sizeof(kOriginal));
- EXPECT_EQ(0u,
- ToString(std::string_view("Hello!"), span(test_buffer, 0)).size());
+ EXPECT_EQ(
+ 0u,
+ ToString(std::string_view("Hello!"), std::span(test_buffer, 0)).size());
ASSERT_EQ(0, std::memcmp(kOriginal, test_buffer, sizeof(kOriginal)));
}
diff --git a/pw_string/type_to_string.cc b/pw_string/type_to_string.cc
index fd2d1f6..e5ebcf8 100644
--- a/pw_string/type_to_string.cc
+++ b/pw_string/type_to_string.cc
@@ -47,7 +47,7 @@
10000000000000000000ull, // 10^19
};
-StatusWithSize HandleExhaustedBuffer(const span<char>& buffer) {
+StatusWithSize HandleExhaustedBuffer(std::span<char> buffer) {
if (!buffer.empty()) {
buffer[0] = '\0';
}
@@ -72,7 +72,7 @@
// DecimalDigitCount and its table). I didn't measure performance, but I don't
// think std::to_chars will be faster, so I kept this implementation for now.
template <>
-StatusWithSize IntToString(uint64_t value, const span<char>& buffer) {
+StatusWithSize IntToString(uint64_t value, std::span<char> buffer) {
constexpr uint32_t base = 10;
constexpr uint32_t max_uint32_base_power = 1'000'000'000;
constexpr uint_fast8_t max_uint32_base_power_exponent = 9;
@@ -110,7 +110,7 @@
return StatusWithSize(total_digits);
}
-StatusWithSize IntToHexString(uint64_t value, const span<char>& buffer) {
+StatusWithSize IntToHexString(uint64_t value, std::span<char> buffer) {
const uint_fast8_t digits = HexDigitCount(value);
if (digits >= buffer.size()) {
@@ -127,7 +127,7 @@
}
template <>
-StatusWithSize IntToString(int64_t value, const span<char>& buffer) {
+StatusWithSize IntToString(int64_t value, std::span<char> buffer) {
if (value >= 0) {
return IntToString<uint64_t>(value, buffer);
}
@@ -146,7 +146,7 @@
// TODO(hepler): Look into using the float overload of std::to_chars when it is
// available.
-StatusWithSize FloatAsIntToString(float value, const span<char>& buffer) {
+StatusWithSize FloatAsIntToString(float value, std::span<char> buffer) {
// If it's finite and fits in an int64_t, print it as a rounded integer.
if (std::isfinite(value) &&
std::abs(value) <
@@ -167,11 +167,11 @@
return HandleExhaustedBuffer(buffer);
}
-StatusWithSize BoolToString(bool value, const span<char>& buffer) {
+StatusWithSize BoolToString(bool value, std::span<char> buffer) {
return CopyEntireString(value ? "true" : "false", buffer);
}
-StatusWithSize PointerToString(const void* pointer, const span<char>& buffer) {
+StatusWithSize PointerToString(const void* pointer, std::span<char> buffer) {
if (pointer == nullptr) {
return CopyEntireString(kNullPointerString, buffer);
}
@@ -179,7 +179,7 @@
}
StatusWithSize CopyString(const std::string_view& value,
- const span<char>& buffer) {
+ std::span<char> buffer) {
if (buffer.empty()) {
return StatusWithSize::RESOURCE_EXHAUSTED;
}
@@ -192,7 +192,7 @@
}
StatusWithSize CopyEntireString(const std::string_view& value,
- const span<char>& buffer) {
+ std::span<char> buffer) {
if (value.size() >= buffer.size()) {
return HandleExhaustedBuffer(buffer);
}
diff --git a/pw_string/type_to_string_test.cc b/pw_string/type_to_string_test.cc
index 482b841..1169222 100644
--- a/pw_string/type_to_string_test.cc
+++ b/pw_string/type_to_string_test.cc
@@ -107,47 +107,47 @@
class IntToStringTest : public TestWithBuffer {};
TEST_F(IntToStringTest, Unsigned_EmptyBuffer_WritesNothing) {
- auto result = IntToString(9u, span(buffer_, 0));
+ auto result = IntToString(9u, std::span(buffer_, 0));
EXPECT_EQ(0u, result.size());
EXPECT_FALSE(result.ok());
EXPECT_STREQ(kStartingString, buffer_);
}
TEST_F(IntToStringTest, Unsigned_TooSmall_1Char_OnlyNullTerminates) {
- auto result = IntToString(9u, span(buffer_, 1));
+ auto result = IntToString(9u, std::span(buffer_, 1));
EXPECT_EQ(0u, result.size());
EXPECT_FALSE(result.ok());
EXPECT_STREQ("", buffer_);
}
TEST_F(IntToStringTest, Unsigned_TooSmall_2Chars_OnlyNullTerminates) {
- auto result = IntToString(10u, span(buffer_, 2));
+ auto result = IntToString(10u, std::span(buffer_, 2));
EXPECT_EQ(0u, result.size());
EXPECT_FALSE(result.ok());
EXPECT_STREQ("", buffer_);
}
TEST_F(IntToStringTest, Unsigned_TooSmall_3Chars_OnlyNullTerminates) {
- auto result = IntToString(123u, span(buffer_, 3));
+ auto result = IntToString(123u, std::span(buffer_, 3));
EXPECT_EQ(0u, result.size());
EXPECT_FALSE(result.ok());
EXPECT_STREQ("", buffer_);
}
TEST_F(IntToStringTest, Unsigned_1Char_FitsExactly) {
- auto result = IntToString(0u, span(buffer_, 2));
+ auto result = IntToString(0u, std::span(buffer_, 2));
EXPECT_EQ(1u, result.size());
EXPECT_TRUE(result.ok());
EXPECT_STREQ("0", buffer_);
- result = IntToString(9u, span(buffer_, 2));
+ result = IntToString(9u, std::span(buffer_, 2));
EXPECT_EQ(1u, result.size());
EXPECT_TRUE(result.ok());
EXPECT_STREQ("9", buffer_);
}
TEST_F(IntToStringTest, Unsigned_2Chars_FitsExactly) {
- auto result = IntToString(10u, span(buffer_, 3));
+ auto result = IntToString(10u, std::span(buffer_, 3));
EXPECT_EQ(2u, result.size());
EXPECT_STREQ("10", buffer_);
}
@@ -155,44 +155,44 @@
TEST_F(IntToStringTest, Unsigned_MaxFitsExactly) {
EXPECT_EQ(20u,
IntToString(std::numeric_limits<uint64_t>::max(),
- span(buffer_, sizeof(kUint64Max)))
+ std::span(buffer_, sizeof(kUint64Max)))
.size());
EXPECT_STREQ(kUint64Max, buffer_);
}
TEST_F(IntToStringTest, SignedPositive_EmptyBuffer_WritesNothing) {
- auto result = IntToString(9, span(buffer_, 0));
+ auto result = IntToString(9, std::span(buffer_, 0));
EXPECT_EQ(0u, result.size());
EXPECT_FALSE(result.ok());
EXPECT_STREQ(kStartingString, buffer_);
}
TEST_F(IntToStringTest, SignedPositive_TooSmall_NullTerminates) {
- auto result = IntToString(9, span(buffer_, 1));
+ auto result = IntToString(9, std::span(buffer_, 1));
EXPECT_EQ(0u, result.size());
EXPECT_FALSE(result.ok());
EXPECT_STREQ("", buffer_);
}
TEST_F(IntToStringTest, SignedPositive_TooSmall_DoesNotWritePastEnd) {
- EXPECT_EQ(0u, IntToString(9, span(buffer_, 1)).size());
+ EXPECT_EQ(0u, IntToString(9, std::span(buffer_, 1)).size());
EXPECT_EQ(0, std::memcmp("\0@#$%^&*()!@#$%^&*()", buffer_, sizeof(buffer_)));
}
TEST_F(IntToStringTest, SignedPositive_1Char_FitsExactly) {
- auto result = IntToString(0, span(buffer_, 2));
+ auto result = IntToString(0, std::span(buffer_, 2));
EXPECT_EQ(1u, result.size());
EXPECT_TRUE(result.ok());
EXPECT_STREQ("0", buffer_);
- result = IntToString(9, span(buffer_, 2));
+ result = IntToString(9, std::span(buffer_, 2));
EXPECT_EQ(1u, result.size());
EXPECT_TRUE(result.ok());
EXPECT_STREQ("9", buffer_);
}
TEST_F(IntToStringTest, SignedPositive_2Chars_FitsExactly) {
- auto result = IntToString(10, span(buffer_, 4));
+ auto result = IntToString(10, std::span(buffer_, 4));
EXPECT_EQ(2u, result.size());
EXPECT_TRUE(result.ok());
EXPECT_STREQ("10", buffer_);
@@ -200,20 +200,20 @@
TEST_F(IntToStringTest, SignedPositive_MaxFitsExactly) {
auto result = IntToString(std::numeric_limits<int64_t>::max(),
- span(buffer_, sizeof(kInt64Min)));
+ std::span(buffer_, sizeof(kInt64Min)));
EXPECT_EQ(19u, result.size());
EXPECT_STREQ(kInt64Max, buffer_);
}
TEST_F(IntToStringTest, SignedNegative_EmptyBuffer_WritesNothing) {
- auto result = IntToString(-9, span(buffer_, 0));
+ auto result = IntToString(-9, std::span(buffer_, 0));
EXPECT_EQ(0u, result.size());
EXPECT_FALSE(result.ok());
EXPECT_STREQ(kStartingString, buffer_);
}
TEST_F(IntToStringTest, SignedNegative_TooSmall_NullTerminates) {
- auto result = IntToString(-9, span(buffer_, 1));
+ auto result = IntToString(-9, std::span(buffer_, 1));
EXPECT_EQ(0u, result.size());
EXPECT_FALSE(result.ok());
EXPECT_STREQ("", buffer_);
@@ -221,25 +221,25 @@
TEST_F(IntToStringTest, SignedNegative_TooSmall_DoesNotWritePastEnd) {
// Note that two \0 are written due to the unsigned IntToString call.
- EXPECT_EQ(0u, IntToString(-9, span(buffer_, 2)).size());
+ EXPECT_EQ(0u, IntToString(-9, std::span(buffer_, 2)).size());
EXPECT_EQ(0, std::memcmp("\0\0#$%^&*()!@#$%^&*()", buffer_, sizeof(buffer_)));
}
TEST_F(IntToStringTest, SignedNegative_FitsExactly) {
- auto result = IntToString(-9, span(buffer_, 3));
+ auto result = IntToString(-9, std::span(buffer_, 3));
EXPECT_EQ(2u, result.size());
EXPECT_STREQ("-9", buffer_);
- result = IntToString(-99, span(buffer_, 4));
+ result = IntToString(-99, std::span(buffer_, 4));
EXPECT_EQ(3u, result.size());
EXPECT_STREQ("-99", buffer_);
- result = IntToString(-123, span(buffer_, 5));
+ result = IntToString(-123, std::span(buffer_, 5));
EXPECT_EQ(4u, result.size());
EXPECT_STREQ("-123", buffer_);
}
TEST_F(IntToStringTest, SignedNegative_MinFitsExactly) {
auto result = IntToString(std::numeric_limits<int64_t>::min(),
- span(buffer_, sizeof(kInt64Min)));
+ std::span(buffer_, sizeof(kInt64Min)));
EXPECT_EQ(20u, result.size());
EXPECT_STREQ(kInt64Min, buffer_);
}
@@ -304,14 +304,14 @@
}
TEST_F(IntToHexStringTest, EmptyBuffer_WritesNothing) {
- auto result = IntToHexString(0xbeef, span(buffer_, 0));
+ auto result = IntToHexString(0xbeef, std::span(buffer_, 0));
EXPECT_EQ(0u, result.size());
EXPECT_FALSE(result.ok());
EXPECT_STREQ(kStartingString, buffer_);
}
TEST_F(IntToHexStringTest, TooSmall_Truncates) {
- auto result = IntToHexString(0xbeef, span(buffer_, 3));
+ auto result = IntToHexString(0xbeef, std::span(buffer_, 3));
EXPECT_EQ(0u, result.size());
EXPECT_FALSE(result.ok());
EXPECT_STREQ("", buffer_);
@@ -381,21 +381,21 @@
}
TEST_F(FloatAsIntToStringTest, TooSmall_Numeric_NullTerminates) {
- auto result = FloatAsIntToString(-3.14e20f, span(buffer_, 1));
+ auto result = FloatAsIntToString(-3.14e20f, std::span(buffer_, 1));
EXPECT_EQ(0u, result.size());
EXPECT_FALSE(result.ok());
EXPECT_STREQ("", buffer_);
}
TEST_F(FloatAsIntToStringTest, TooSmall_Infinity_NullTerminates) {
- auto result = FloatAsIntToString(-INFINITY, span(buffer_, 3));
+ auto result = FloatAsIntToString(-INFINITY, std::span(buffer_, 3));
EXPECT_EQ(0u, result.size());
EXPECT_FALSE(result.ok());
EXPECT_STREQ("", buffer_);
}
TEST_F(FloatAsIntToStringTest, TooSmall_NaN_NullTerminates) {
- auto result = FloatAsIntToString(NAN, span(buffer_, 2));
+ auto result = FloatAsIntToString(NAN, std::span(buffer_, 2));
EXPECT_EQ(0u, result.size());
EXPECT_FALSE(result.ok());
EXPECT_STREQ("", buffer_);
@@ -411,28 +411,28 @@
}
TEST_F(CopyStringTest, EmptyBuffer_WritesNothing) {
- auto result = CopyString("Hello", span(buffer_, 0));
+ auto result = CopyString("Hello", std::span(buffer_, 0));
EXPECT_EQ(0u, result.size());
EXPECT_FALSE(result.ok());
EXPECT_STREQ(kStartingString, buffer_);
}
TEST_F(CopyStringTest, TooSmall_Truncates) {
- auto result = CopyString("Hi!", span(buffer_, 3));
+ auto result = CopyString("Hi!", std::span(buffer_, 3));
EXPECT_EQ(2u, result.size());
EXPECT_FALSE(result.ok());
EXPECT_STREQ("Hi", buffer_);
}
TEST_F(CopyStringTest, ExactFit) {
- auto result = CopyString("Hi!", span(buffer_, 4));
+ auto result = CopyString("Hi!", std::span(buffer_, 4));
EXPECT_EQ(3u, result.size());
EXPECT_TRUE(result.ok());
EXPECT_STREQ("Hi!", buffer_);
}
TEST_F(CopyStringTest, NullTerminatorsInString) {
- ASSERT_EQ(4u, CopyString("\0!\0\0"sv, span(buffer_, 5)).size());
+ ASSERT_EQ(4u, CopyString("\0!\0\0"sv, std::span(buffer_, 5)).size());
EXPECT_EQ("\0!\0\0"sv, std::string_view(buffer_, 4));
}
@@ -444,35 +444,35 @@
}
TEST_F(CopyEntireStringTest, EmptyBuffer_WritesNothing) {
- auto result = CopyEntireString("Hello", span(buffer_, 0));
+ auto result = CopyEntireString("Hello", std::span(buffer_, 0));
EXPECT_EQ(0u, result.size());
EXPECT_FALSE(result.ok());
EXPECT_STREQ(kStartingString, buffer_);
}
TEST_F(CopyEntireStringTest, TooSmall_WritesNothing) {
- auto result = CopyEntireString("Hi!", span(buffer_, 3));
+ auto result = CopyEntireString("Hi!", std::span(buffer_, 3));
EXPECT_EQ(0u, result.size());
EXPECT_FALSE(result.ok());
EXPECT_STREQ("", buffer_);
}
TEST_F(CopyEntireStringTest, ExactFit) {
- auto result = CopyEntireString("Hi!", span(buffer_, 4));
+ auto result = CopyEntireString("Hi!", std::span(buffer_, 4));
EXPECT_EQ(3u, result.size());
EXPECT_TRUE(result.ok());
EXPECT_STREQ("Hi!", buffer_);
}
TEST_F(CopyEntireStringTest, NullTerminatorsInString) {
- ASSERT_EQ(4u, CopyEntireString("\0!\0\0"sv, span(buffer_, 5)).size());
+ ASSERT_EQ(4u, CopyEntireString("\0!\0\0"sv, std::span(buffer_, 5)).size());
EXPECT_EQ("\0!\0\0"sv, std::string_view(buffer_, 4));
}
class PointerToStringTest : public TestWithBuffer {};
TEST_F(PointerToStringTest, Nullptr_WritesNull) {
- EXPECT_EQ(6u, PointerToString(nullptr, span(buffer_, 7)).size());
+ EXPECT_EQ(6u, PointerToString(nullptr, std::span(buffer_, 7)).size());
EXPECT_STREQ("(null)", buffer_);
}
@@ -485,32 +485,32 @@
class BoolToStringTest : public TestWithBuffer {};
TEST_F(BoolToStringTest, ExactFit) {
- EXPECT_EQ(4u, BoolToString(true, span(buffer_, 5)).size());
+ EXPECT_EQ(4u, BoolToString(true, std::span(buffer_, 5)).size());
EXPECT_STREQ("true", buffer_);
- EXPECT_EQ(5u, BoolToString(false, span(buffer_, 6)).size());
+ EXPECT_EQ(5u, BoolToString(false, std::span(buffer_, 6)).size());
EXPECT_STREQ("false", buffer_);
}
TEST_F(BoolToStringTest, True_TooSmall_WritesNullTerminator) {
- auto result = BoolToString(true, span(buffer_, 4));
+ auto result = BoolToString(true, std::span(buffer_, 4));
EXPECT_EQ(0u, result.size());
EXPECT_FALSE(result.ok());
EXPECT_STREQ("", buffer_);
}
TEST_F(BoolToStringTest, False_TooSmall_WritesNullTerminator) {
- auto result = BoolToString(false, span(buffer_, 5));
+ auto result = BoolToString(false, std::span(buffer_, 5));
EXPECT_EQ(0u, result.size());
EXPECT_FALSE(result.ok());
EXPECT_STREQ("", buffer_);
}
TEST_F(BoolToStringTest, EmptyBuffer_WritesNothing) {
- EXPECT_EQ(0u, BoolToString(true, span(buffer_, 0)).size());
+ EXPECT_EQ(0u, BoolToString(true, std::span(buffer_, 0)).size());
EXPECT_STREQ(kStartingString, buffer_);
- EXPECT_EQ(0u, BoolToString(false, span(buffer_, 0)).size());
+ EXPECT_EQ(0u, BoolToString(false, std::span(buffer_, 0)).size());
EXPECT_STREQ(kStartingString, buffer_);
}
diff --git a/pw_unit_test/public/pw_unit_test/framework.h b/pw_unit_test/public/pw_unit_test/framework.h
index 7462f1a..cee6a70 100644
--- a/pw_unit_test/public/pw_unit_test/framework.h
+++ b/pw_unit_test/public/pw_unit_test/framework.h
@@ -21,6 +21,7 @@
#include <cstdint>
#include <cstring>
#include <new>
+#include <span>
#include "pw_polyfill/standard.h"
#include "pw_preprocessor/concat.h"
@@ -115,11 +116,13 @@
// ASSERT statements in tests.
//
// You can add support for displaying custom types by defining a ToString
-// overload. For example:
+// template specialization. For example:
//
// namespace pw {
//
-// StatusWithSize ToString(const MyType& value, const span<char>& buffer) {
+// template <>
+// StatusWithSize ToString<MyType>(const MyType& value,
+// std::span<char> buffer) {
// return string::Format("<MyType|%d>", value.id);
// }
//
@@ -127,7 +130,7 @@
//
// See the documentation in pw_string/string_builder.h for more information.
template <typename T>
-StatusWithSize UnknownTypeToString(const T& value, const span<char>& buffer) {
+StatusWithSize UnknownTypeToString(const T& value, std::span<char> buffer) {
StringBuilder sb(buffer);
sb << '<' << sizeof(value) << "-byte object at 0x" << &value << '>';
return sb.status_with_size();