diff --git a/pw_string/guide.rst b/pw_string/guide.rst index 38916ee526..87ceec03e5 100644 --- a/pw_string/guide.rst +++ b/pw_string/guide.rst @@ -1,11 +1,67 @@ .. _module-pw_string-guide: -=============== -pw_string guide -=============== +================ +pw_string: Guide +================ -Building strings with StringBuilder ------------------------------------ +InlineString and StringBuilder? +=============================== +Use :cpp:type:`pw::InlineString` if you need: + +* Compatibility with ``std::string`` +* Storage internal to the object +* A string object to persist in other data structures +* Lower code size overhead + +Use :cpp:class:`pw::StringBuilder` if you need: + +* Compatibility with ``std::ostringstream``, including custom object support +* Storage external to the object +* Non-fatal handling of failed append/format operations +* Tracking of the status of a series of operations +* A temporary stack object to aid string construction +* Medium code size overhead + +An example of when to prefer :cpp:type:`pw::InlineString` is wrapping a +length-delimited string (e.g. ``std::string_view``) for APIs that require null +termination: + +.. code-block:: cpp + + #include + #include "pw_log/log.h" + #include "pw_string/string_builder.h" + + void ProcessName(std::string_view name) { + // %s format strings require null terminated strings, so create one on the + // stack with size up to kMaxNameLen, copy the string view `name` contents + // into it, add a null terminator, and log it. + PW_LOG_DEBUG("The name is %s", + pw::InlineString(name).c_str()); + } + +An example of when to prefer :cpp:class:`pw::StringBuilder` is when +constructing a string for external use. + +.. code-block:: cpp + + #include "pw_string/string_builder.h" + + pw::Status FlushSensorValueToUart(int32_t sensor_value) { + pw::StringBuffer<42> sb; + sb << "Sensor value: "; + sb << sensor_value; // Formats as int. + FlushCStringToUart(sb.c_str()); + + if (!sb.status().ok) { + format_error_metric.Increment(); // Track overflows. + } + return sb.status(); + } + + +Building strings with pw::StringBuilder +======================================= The following shows basic use of a :cpp:class:`pw::StringBuilder`. .. code-block:: cpp @@ -34,92 +90,94 @@ The following shows basic use of a :cpp:class:`pw::StringBuilder`. return sb.status(); } -Constructing pw::InlineString objects -------------------------------------- +Building strings with pw::InlineString +====================================== :cpp:type:`pw::InlineString` objects must be constructed by specifying a fixed capacity for the string. .. code-block:: c++ - // Initialize from a C string. - pw::InlineString<32> inline_string = "Literally"; - inline_string.append('?', 3); // contains "Literally???" + #include "pw_string/string.h" - // Supports copying into known-capacity strings. - pw::InlineString<64> other = inline_string; + // Initialize from a C string. + pw::InlineString<32> inline_string = "Literally"; + inline_string.append('?', 3); // contains "Literally???" - // Supports various helpful std::string functions - if (inline_string.starts_with("Lit") || inline_string == "not\0literally"sv) { - other += inline_string; - } + // Supports copying into known-capacity strings. + pw::InlineString<64> other = inline_string; - // Like std::string, InlineString is always null terminated when accessed - // through c_str(). InlineString can be used to null-terminate - // length-delimited strings for APIs that expect null-terminated strings. - std::string_view file(".gif"); - if (std::fopen(pw::InlineString(file).c_str(), "r") == nullptr) { - return; - } + // Supports various helpful std::string functions + if (inline_string.starts_with("Lit") || inline_string == "not\0literally"sv) { + other += inline_string; + } - // pw::InlineString integrates well with std::string_view. It supports - // implicit conversions to and from std::string_view. - inline_string = std::string_view("not\0literally", 12); + // Like std::string, InlineString is always null terminated when accessed + // through c_str(). InlineString can be used to null-terminate + // length-delimited strings for APIs that expect null-terminated strings. + std::string_view file(".gif"); + if (std::fopen(pw::InlineString(file).c_str(), "r") == nullptr) { + return; + } - FunctionThatTakesAStringView(inline_string); + // pw::InlineString integrates well with std::string_view. It supports + // implicit conversions to and from std::string_view. + inline_string = std::string_view("not\0literally", 12); - FunctionThatTakesAnInlineString(std::string_view("1234", 4)); + FunctionThatTakesAStringView(inline_string); -Choosing between InlineString and StringBuilder ------------------------------------------------ -:cpp:type:`pw::InlineString` is comparable to ``std::string``, while -:cpp:class:`pw::StringBuilder` is comparable to ``std::ostringstream``. -Because :cpp:class:`pw::StringBuilder` provides high-level stream functionality, -it has more overhead than :cpp:type:`pw::InlineString`. + FunctionThatTakesAnInlineString(std::string_view("1234", 4)); -Use :cpp:type:`pw::InlineString` unless :cpp:class:`pw::StringBuilder`'s -capabilities are needed. Features unique to :cpp:class:`pw::StringBuilder` -include: +Building strings inside InlineString with a StringBuilder +========================================================= +:cpp:class:`pw::StringBuilder` can build a string in a +:cpp:type:`pw::InlineString`: -* Polymorphic C++ stream-style output, potentially supporting custom types. -* Non-fatal handling of failed append/format operations. -* Tracking the status of a series of operations. -* Building a string in an external buffer. +.. code-block:: c++ -If those features are not required, use :cpp:type:`pw::InlineString`. A common -example of when to prefer :cpp:type:`pw::InlineString` is wrapping a -length-delimited string (e.g. ``std::string_view``) for APIs that require null -termination. + #include "pw_string/string.h" -.. code-block:: cpp + void DoFoo() { + InlineString<32> inline_str; + StringBuilder sb(inline_str); + sb << 123 << "456"; + // inline_str contains "456" + } - void ProcessName(std::string_view name) { - PW_LOG_DEBUG("The name is %s", pw::InlineString(name).c_str()); +Passing InlineStrings as parameters +=================================== +:cpp:type:`pw::InlineString` objects can be passed to non-templated functions +via type erasure. This saves code size in most cases, since it avoids template +expansions triggered by string size differences. -Operating on unknown size strings ---------------------------------- -All :cpp:type:`pw::InlineString` operations may be performed on strings without -specifying their capacity. +Unknown size strings +-------------------- +To operate on :cpp:type:`pw::InlineString` objects without knowing their type, +use the ``pw::InlineString<>`` type, shown in the examples below: .. code-block:: c++ - void RemoveSuffix(pw::InlineString<>& string, std::string_view suffix) { - if (string.ends_with(suffix)) { - string.resize(string.size() - suffix.size()); - } - } + // Note that the first argument is a generically-sized InlineString. + void RemoveSuffix(pw::InlineString<>& string, std::string_view suffix) { + if (string.ends_with(suffix)) { + string.resize(string.size() - suffix.size()); + } + } - void DoStuff() { - pw::InlineString<32> str1 = "Good morning!"; - RemoveSuffix(str1, " morning!"); + void DoStuff() { + pw::InlineString<32> str1 = "Good morning!"; + RemoveSuffix(str1, " morning!"); - pw::InlineString<40> str2 = "Good"; - RemoveSuffix(str2, " morning!"); + pw::InlineString<40> str2 = "Good"; + RemoveSuffix(str2, " morning!"); - PW_ASSERT(str1 == str2); - } + PW_ASSERT(str1 == str2); + } + +However, generically sized :cpp:type:`pw::InlineString` objects don't work in +``constexpr`` contexts. -Operating on known-size strings -------------------------------- +Known size strings +------------------ :cpp:type:`pw::InlineString` operations on known-size strings may be used in ``constexpr`` expressions. @@ -135,13 +193,8 @@ Operating on known-size strings return string; }(); -Building strings ----------------- -:cpp:class:`pw::StringBuilder` may be used to build a string in a -:cpp:type:`pw::InlineString`. - -Deducing class template arguments with pw::InlineBasicString ------------------------------------------------------------- +Compact initialization of InlineStrings +======================================= :cpp:type:`pw::InlineBasicString` supports class template argument deduction (CTAD) in C++17 and newer. Since :cpp:type:`pw::InlineString` is an alias, CTAD is not supported until C++20. @@ -155,9 +208,8 @@ is not supported until C++20. // In C++20, CTAD may be used with the pw::InlineString alias. pw::InlineString my_other_string("123456789"); - -Printing custom types ---------------------- +Supporting custom types with StringBuilder +========================================== As with ``std::ostream``, StringBuilder supports printing custom types by overriding the ``<<`` operator. This is is done by defining ``operator<<`` in the same namespace as the custom type. For example: