aboutsummaryrefslogtreecommitdiff
path: root/pw_string/guide.rst
blob: dfa83b39b5e29cc900c4c08e5cfb97c03e994366 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
.. _module-pw_string-guide:

====================
Get Started & Guides
====================
.. pigweed-module-subpage::
   :name: pw_string
   :tagline: pw_string: Efficient, easy, and safe string manipulation

.. _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