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