Skip to content

Commit

Permalink
pw_string: Rework Guide in docs
Browse files Browse the repository at this point in the history
Change-Id: I81045bf83240bba1df6960f7d2a260f6734b9fdf
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/133610
Commit-Queue: Keir Mierle <[email protected]>
Reviewed-by: Chad Norvell <[email protected]>
  • Loading branch information
keir authored and CQ Bot Account committed Mar 11, 2023
1 parent 85cbe67 commit 7485834
Showing 1 changed file with 128 additions and 76 deletions.
204 changes: 128 additions & 76 deletions pw_string/guide.rst
Original file line number Diff line number Diff line change
@@ -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 <string>
#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<kMaxNameLen>(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
Expand Down Expand Up @@ -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<kMaxNameLen>(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<kMaxNameLen>(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<kMaxNameLen>(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.

Expand All @@ -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.
Expand All @@ -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:
Expand Down

0 comments on commit 7485834

Please sign in to comment.