xref: /aosp_15_r20/external/pigweed/pw_string/guide.rst (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1.. _module-pw_string-guide:
2
3====================
4Get Started & Guides
5====================
6.. pigweed-module-subpage::
7   :name: pw_string
8
9.. _module-pw_string-get-started:
10
11Get Started
12===========
13.. tab-set::
14
15   .. tab-item:: Bazel
16
17      Add ``@pigweed//pw_string`` to the ``deps`` list in your Bazel target:
18
19      .. code-block::
20
21         cc_library("...") {
22           # ...
23           deps = [
24             # ...
25             "@pigweed//pw_string",
26             # ...
27           ]
28         }
29
30      If only one part of the module is needed, depend only on it; for example
31      ``@pigweed//pw_string:format``.
32
33      This assumes ``@pigweed`` is the name you pulled Pigweed into your Bazel
34      ``WORKSPACE`` as.
35
36   .. tab-item:: GN
37
38      Add ``$dir_pw_string`` to the ``deps`` list in your ``pw_executable()``
39      build target:
40
41      .. code-block::
42
43         pw_executable("...") {
44           # ...
45           deps = [
46             # ...
47             "$dir_pw_string",
48             # ...
49           ]
50         }
51
52      See `//source/BUILD.gn <https://pigweed.googlesource.com/pigweed/sample_project/+/refs/heads/main/source/BUILD.gn>`_
53      in the Pigweed Sample Project for an example.
54
55   .. tab-item:: CMake
56
57      Add ``pw_string`` to your ``pw_add_library`` or similar CMake target:
58
59      .. code-block::
60
61         pw_add_library(my_library STATIC
62           HEADERS
63             ...
64           PRIVATE_DEPS
65             # ...
66             pw_string
67             # ...
68         )
69
70      For a narrower dependency, depend on subtargets like
71      ``pw_string.builder``, etc.
72
73   .. tab-item:: Zephyr
74
75      There are two ways to use ``pw_string`` from a Zephyr project:
76
77      #. Depend on ``pw_string`` in your CMake target (see CMake tab). This is
78         Pigweed Team's suggested approach since it enables precise CMake
79         dependency analysis.
80
81      #. Add ``CONFIG_PIGWEED_STRING=y`` to the Zephyr project's configuration,
82         which causes ``pw_string`` to become a global dependency and have the
83         includes exposed to all targets. Pigweed team does not recommend this
84         approach, though it is the typical Zephyr solution.
85
86Choose between pw::InlineString and pw::StringBuilder
87=====================================================
88`pw::InlineString` is intended to replace typical null terminated character
89arrays in embedded data structures. Use :cpp:type:`pw::InlineString` if you
90need:
91
92* Compatibility with ``std::string``
93* Storage internal to the object
94* A string object to persist in other data structures
95* Lower code size overhead
96
97`pw::StringBuilder` is intended to ease constructing strings in external data;
98typically created on the stack and disposed of in the same function. Use
99:cpp:class:`pw::StringBuilder` if you need:
100
101* Compatibility with ``std::ostringstream``, including custom object support
102* Storage external to the object
103* Non-fatal handling of failed append/format operations
104* Tracking of the status of a series of operations
105* A temporary stack object to aid string construction
106* Medium code size overhead
107
108An example of when to prefer :cpp:type:`pw::InlineString` is wrapping a
109length-delimited string (e.g. ``std::string_view``) for APIs that require null
110termination:
111
112.. code-block:: cpp
113
114   #include <string>
115   #include "pw_log/log.h"
116   #include "pw_string/string_builder.h"
117
118   void ProcessName(std::string_view name) {
119     // %s format strings require null terminated strings, so create one on the
120     // stack with size up to kMaxNameLen, copy the string view `name` contents
121     // into it, add a null terminator, and log it.
122     PW_LOG_DEBUG("The name is %s",
123                  pw::InlineString<kMaxNameLen>(name).c_str());
124   }
125
126An example of when to prefer :cpp:class:`pw::StringBuilder` is when
127constructing a string for external use.
128
129.. code-block:: cpp
130
131   #include "pw_string/string_builder.h"
132
133   pw::Status FlushSensorValueToUart(int32_t sensor_value) {
134     pw::StringBuffer<42> sb;
135     sb << "Sensor value: ";
136     sb << sensor_value;  // Formats as int.
137     FlushCStringToUart(sb.c_str());
138
139     if (!sb.status().ok) {
140       format_error_metric.Increment();  // Track overflows.
141     }
142     return sb.status();
143   }
144
145.. _module-pw_string-guide-stringbuilder:
146
147Build a string with pw::StringBuilder
148=====================================
149The following shows basic use of a :cpp:class:`pw::StringBuilder`.
150
151.. code-block:: cpp
152
153   #include "pw_log/log.h"
154   #include "pw_string/string_builder.h"
155
156   pw::Status LogProducedData(std::string_view func_name,
157                              span<const std::byte> data) {
158     // pw::StringBuffer allocates a pw::StringBuilder with a built-in buffer.
159     pw::StringBuffer<42> sb;
160
161     // Append a std::string_view to the buffer.
162     sb << func_name;
163
164     // Append a format string to the buffer.
165     sb.Format(" produced %d bytes of data: ", static_cast<int>(data.data()));
166
167     // Append bytes as hex to the buffer.
168     sb << data;
169
170     // Log the final string.
171     PW_LOG_DEBUG("%s", sb.c_str());
172
173     // Errors encountered while mutating the string builder are tracked.
174     return sb.status();
175   }
176
177Build a string with pw::InlineString
178====================================
179:cpp:type:`pw::InlineString` objects must be constructed by specifying a fixed
180capacity for the string.
181
182.. code-block:: c++
183
184   #include "pw_string/string.h"
185
186   // Initialize from a C string.
187   pw::InlineString<32> inline_string = "Literally";
188   inline_string.append('?', 3);   // contains "Literally???"
189
190   // Supports copying into known-capacity strings.
191   pw::InlineString<64> other = inline_string;
192
193   // Supports various helpful std::string functions
194   if (inline_string.starts_with("Lit") || inline_string == "not\0literally"sv) {
195     other += inline_string;
196   }
197
198   // Like std::string, InlineString is always null terminated when accessed
199   // through c_str(). InlineString can be used to null-terminate
200   // length-delimited strings for APIs that expect null-terminated strings.
201   std::string_view file(".gif");
202   if (std::fopen(pw::InlineString<kMaxNameLen>(file).c_str(), "r") == nullptr) {
203     return;
204   }
205
206   // pw::InlineString integrates well with std::string_view. It supports
207   // implicit conversions to and from std::string_view.
208   inline_string = std::string_view("not\0literally", 12);
209
210   FunctionThatTakesAStringView(inline_string);
211
212   FunctionThatTakesAnInlineString(std::string_view("1234", 4));
213
214Build a string inside an pw::InlineString with a pw::StringBuilder
215==================================================================
216:cpp:class:`pw::StringBuilder` can build a string in a
217:cpp:type:`pw::InlineString`:
218
219.. code-block:: c++
220
221   #include "pw_string/string.h"
222
223   void DoFoo() {
224     InlineString<32> inline_str;
225     StringBuilder sb(inline_str);
226     sb << 123 << "456";
227     // inline_str contains "456"
228   }
229
230Pass an pw::InlineString object as a parameter
231==============================================
232:cpp:type:`pw::InlineString` objects can be passed to non-templated functions
233via type erasure. This saves code size in most cases, since it avoids template
234expansions triggered by string size differences.
235
236Unknown size strings
237--------------------
238To operate on :cpp:type:`pw::InlineString` objects without knowing their type,
239use the ``pw::InlineString<>`` type, shown in the examples below:
240
241.. code-block:: c++
242
243   // Note that the first argument is a generically-sized InlineString.
244   void RemoveSuffix(pw::InlineString<>& string, std::string_view suffix) {
245     if (string.ends_with(suffix)) {
246        string.resize(string.size() - suffix.size());
247     }
248   }
249
250   void DoStuff() {
251     pw::InlineString<32> str1 = "Good morning!";
252     RemoveSuffix(str1, " morning!");
253
254     pw::InlineString<40> str2 = "Good";
255     RemoveSuffix(str2, " morning!");
256
257     PW_ASSERT(str1 == str2);
258   }
259
260However, generically sized :cpp:type:`pw::InlineString` objects don't work in
261``constexpr`` contexts.
262
263Known size strings
264------------------
265:cpp:type:`pw::InlineString` operations on known-size strings may be used in
266``constexpr`` expressions.
267
268.. code-block:: c++
269
270   static constexpr pw::InlineString<64> kMyString = [] {
271     pw::InlineString<64> string;
272
273     for (int i = 0; i < 10; ++i) {
274       string += "Hello";
275     }
276
277     return string;
278   }();
279
280Initialization of pw::InlineString objects
281===========================================
282:cpp:type:`pw::InlineBasicString` supports class template argument deduction
283(CTAD) in C++17 and newer. Since :cpp:type:`pw::InlineString` is an alias, CTAD
284is not supported until C++20.
285
286.. code-block:: c++
287
288   // Deduces a capacity of 5 characters to match the 5-character string literal
289   // (not counting the null terminator).
290   pw::InlineBasicString inline_string = "12345";
291
292   // In C++20, CTAD may be used with the pw::InlineString alias.
293   pw::InlineString my_other_string("123456789");
294
295Custom types with pw::StringBuilder
296===================================
297As with ``std::ostream``, pw::StringBuilder supports printing custom types by
298overriding the ``<<`` operator. This is is done by defining ``operator<<`` in
299the same namespace as the custom type. For example:
300
301.. code-block:: cpp
302
303   namespace my_project {
304
305   struct MyType {
306     int foo;
307     const char* bar;
308   };
309
310   pw::StringBuilder& operator<<(pw::StringBuilder& sb, const MyType& value) {
311     return sb << "MyType(" << value.foo << ", " << value.bar << ')';
312   }
313
314   }  // namespace my_project
315
316Internally, ``StringBuilder`` uses the ``ToString`` function to print. The
317``ToString`` template function can be specialized to support custom types with
318``StringBuilder``, though it is recommended to overload ``operator<<`` instead.
319This example shows how to specialize ``pw::ToString``:
320
321.. code-block:: cpp
322
323   #include "pw_string/to_string.h"
324
325   namespace pw {
326
327   template <>
328   StatusWithSize ToString<MyStatus>(MyStatus value, span<char> buffer) {
329     return Copy(MyStatusString(value), buffer);
330   }
331
332   }  // namespace pw
333