.. _module-pw_string-guide:

====================
Get Started & Guides
====================
.. pigweed-module-subpage::
   :name: pw_string

.. _module-pw_string-get-started:

Get Started
===========
.. tab-set::

   .. tab-item:: Bazel

      Add ``@pigweed//pw_string`` to the ``deps`` list in your Bazel target:

      .. code-block::

         cc_library("...") {
           # ...
           deps = [
             # ...
             "@pigweed//pw_string",
             # ...
           ]
         }

      If only one part of the module is needed, depend only on it; for example
      ``@pigweed//pw_string:format``.

      This assumes ``@pigweed`` is the name you pulled Pigweed into your Bazel
      ``WORKSPACE`` as.

   .. tab-item:: GN

      Add ``$dir_pw_string`` to the ``deps`` list in your ``pw_executable()``
      build target:

      .. code-block::

         pw_executable("...") {
           # ...
           deps = [
             # ...
             "$dir_pw_string",
             # ...
           ]
         }

      See `//source/BUILD.gn <https://pigweed.googlesource.com/pigweed/sample_project/+/refs/heads/main/source/BUILD.gn>`_
      in the Pigweed Sample Project for an example.

   .. tab-item:: CMake

      Add ``pw_string`` to your ``pw_add_library`` or similar CMake target:

      .. code-block::

         pw_add_library(my_library STATIC
           HEADERS
             ...
           PRIVATE_DEPS
             # ...
             pw_string
             # ...
         )

      For a narrower dependency, depend on subtargets like
      ``pw_string.builder``, etc.

   .. tab-item:: Zephyr

      There are two ways to use ``pw_string`` from a Zephyr project:

      #. Depend on ``pw_string`` in your CMake target (see CMake tab). This is
         Pigweed Team's suggested approach since it enables precise CMake
         dependency analysis.

      #. Add ``CONFIG_PIGWEED_STRING=y`` to the Zephyr project's configuration,
         which causes ``pw_string`` to become a global dependency and have the
         includes exposed to all targets. Pigweed team does not recommend this
         approach, though it is the typical Zephyr solution.

Choose between pw::InlineString and pw::StringBuilder
=====================================================
`pw::InlineString` is intended to replace typical null terminated character
arrays in embedded data structures. 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

`pw::StringBuilder` is intended to ease constructing strings in external data;
typically created on the stack and disposed of in the same function. 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();
   }

.. _module-pw_string-guide-stringbuilder:

Build a string with pw::StringBuilder
=====================================
The following shows basic use of a :cpp:class:`pw::StringBuilder`.

.. code-block:: cpp

   #include "pw_log/log.h"
   #include "pw_string/string_builder.h"

   pw::Status LogProducedData(std::string_view func_name,
                              span<const std::byte> data) {
     // pw::StringBuffer allocates a pw::StringBuilder with a built-in buffer.
     pw::StringBuffer<42> sb;

     // Append a std::string_view to the buffer.
     sb << func_name;

     // Append a format string to the buffer.
     sb.Format(" produced %d bytes of data: ", static_cast<int>(data.data()));

     // Append bytes as hex to the buffer.
     sb << data;

     // Log the final string.
     PW_LOG_DEBUG("%s", sb.c_str());

     // Errors encountered while mutating the string builder are tracked.
     return sb.status();
   }

Build a string with pw::InlineString
====================================
:cpp:type:`pw::InlineString` objects must be constructed by specifying a fixed
capacity for the string.

.. code-block:: c++

   #include "pw_string/string.h"

   // Initialize from a C string.
   pw::InlineString<32> inline_string = "Literally";
   inline_string.append('?', 3);   // contains "Literally???"

   // Supports copying into known-capacity strings.
   pw::InlineString<64> other = inline_string;

   // Supports various helpful std::string functions
   if (inline_string.starts_with("Lit") || inline_string == "not\0literally"sv) {
     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;
   }

   // 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);

   FunctionThatTakesAStringView(inline_string);

   FunctionThatTakesAnInlineString(std::string_view("1234", 4));

Build a string inside an pw::InlineString with a pw::StringBuilder
==================================================================
:cpp:class:`pw::StringBuilder` can build a string in a
:cpp:type:`pw::InlineString`:

.. code-block:: c++

   #include "pw_string/string.h"

   void DoFoo() {
     InlineString<32> inline_str;
     StringBuilder sb(inline_str);
     sb << 123 << "456";
     // inline_str contains "456"
   }

Pass an pw::InlineString object as a parameter
==============================================
: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.

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++

   // 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!");

     pw::InlineString<40> str2 = "Good";
     RemoveSuffix(str2, " morning!");

     PW_ASSERT(str1 == str2);
   }

However, generically sized :cpp:type:`pw::InlineString` objects don't work in
``constexpr`` contexts.

Known size strings
------------------
:cpp:type:`pw::InlineString` operations on known-size strings may be used in
``constexpr`` expressions.

.. code-block:: c++

   static constexpr pw::InlineString<64> kMyString = [] {
     pw::InlineString<64> string;

     for (int i = 0; i < 10; ++i) {
       string += "Hello";
     }

     return string;
   }();

Initialization of pw::InlineString objects
===========================================
: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.

.. code-block:: c++

   // Deduces a capacity of 5 characters to match the 5-character string literal
   // (not counting the null terminator).
   pw::InlineBasicString inline_string = "12345";

   // In C++20, CTAD may be used with the pw::InlineString alias.
   pw::InlineString my_other_string("123456789");

Custom types with pw::StringBuilder
===================================
As with ``std::ostream``, pw::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:

.. code-block:: cpp

   namespace my_project {

   struct MyType {
     int foo;
     const char* bar;
   };

   pw::StringBuilder& operator<<(pw::StringBuilder& sb, const MyType& value) {
     return sb << "MyType(" << value.foo << ", " << value.bar << ')';
   }

   }  // namespace my_project

Internally, ``StringBuilder`` uses the ``ToString`` function to print. The
``ToString`` template function can be specialized to support custom types with
``StringBuilder``, though it is recommended to overload ``operator<<`` instead.
This example shows how to specialize ``pw::ToString``:

.. code-block:: cpp

   #include "pw_string/to_string.h"

   namespace pw {

   template <>
   StatusWithSize ToString<MyStatus>(MyStatus value, span<char> buffer) {
     return Copy(MyStatusString(value), buffer);
   }

   }  // namespace pw
