Flow-IPC 3.0.0
Flow-IPC project: Full implementation reference.
shared_name.hpp
Go to the documentation of this file.
1/* Flow-IPC: Core
2 * Copyright 2023 Akamai Technologies, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the
5 * "License"); you may not use this file except in
6 * compliance with the License. You may obtain a copy
7 * of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in
12 * writing, software distributed under the License is
13 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
14 * CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing
16 * permissions and limitations under the License. */
17
18/// @file
19#pragma once
20
22#include "ipc/common.hpp"
23#include <type_traits>
24#include <charconv>
25#include <cassert>
26#include <cstdint>
27#include <functional>
28#include <string>
29#include <string_view>
30#include <typeinfo>
31
32namespace ipc::util
33{
34
35/**
36 * String-wrapping abstraction representing a name uniquely distinguishing a kernel-persistent
37 * entity from all others in the system, or a fragment of such a name. Conceptually it relates to `std::string`
38 * similarly to how `filesystem::path` does.
39 *
40 * This is a very simple class in terms of what logic it actually adds: it encapsulates an `std::string` and allows for,
41 * basically, string operations like concatenation, with some syntactic sugar added for a simple *folder* convention.
42 * However, the design *context* (namely, how shared resources are named and why) is less trivial, and this doc header
43 * is a good place to get into those topics as well. Hence, we first cover practical aspects, with the architectural
44 * context referenced only as needed. Then, below that, there's an architecture discussion about naming in general.
45 *
46 * @todo It would be good to `constexpr`-ify Shared_name to the extent possible; it might even make a dent in
47 * perf in some contexts. Internally this is annoying until C++20, where `std::string` itself got `constexpr`-ified,
48 * as we use `std::string` internally; best left alone until then.
49 *
50 * @todo Consider adding a `Shared_name_view` which would be to Shared_name as `String_view` is to `string`; and
51 * that could itself be constructed from a `String_view` (and therefore from `const char*` [+ length], etc.).
52 * The main work would arguably not be the creation of that class itself but rather (1) ensuring proper implicit
53 * conversion between the main class and the new one and (2) conversion of the many Flow-IPC APIs that take
54 * a `const Shared_name&` that would (hopefully in essentially non-user-code-breaking ways) instead
55 * take `Shared_name_view`.
56 *
57 * ### Construction/assignment from and conversion to strings/similar ###
58 * Internally it stores an `std::string`.
59 *
60 * Conversion: That `string` is accessible by `const&` via str() and similarly the NUL-terminated native_str().
61 * Also there is an `ostream<<` printer; do note it does not simply print str() but rather a beautified version.
62 * (`istream>>` input is also provided but is identical to `istream >> string`.)
63 *
64 * Construction/assignment: It comes with standard default/copy/move ctors and assignment. There is also a
65 * constructor that takes two iterators-to-`char` (including 2 `const char*`); use it for other `char` containers such
66 * as `vector<char>`. However, construction from `string` (including destructive move-construction),
67 * util::String_view, or NUL-terminated `const char*` is available exclusively in the form of `static` quasi-ctors ct().
68 * Lastly: To assign please use move ctor: `existing_sh_name = Shared_name::ct(src)`.
69 *
70 * Rationale: C++ ADL semantics cause a ton of super-annoying problems -- especially inside ipc::transport
71 * namespace itself -- where compilers will auto-convert, e.g., a `string` to `Shared_name` without being asked
72 * to do so at all. For example, one sees `operator << string` output the `string` in "beautified" form, because
73 * a `Shared_name` is constructed implicitly and then printed via its `<<`. I (ygoldfel), then, took a cue from
74 * boost.asio's IP address classes which use `static` quasi-ctors to avoid any such issues. (The alternative
75 * was to fine-tune the ctor set a-la `filesystem::path`, with conversion traits and all kinds of craziness.)
76 *
77 * ### Practical Shared_name matters ###
78 * A *shared resource* in this context is some entity (such as a SHM-mapped addressable area; or a POSIX message queue)
79 * that can be *opened* for further access by potentially 2+ processes simultaneously. When opening (at least),
80 * the shared resource is referred to -- in the associated opening sys call or similar -- by a string name, and the name
81 * works equally from those 2+ processes. Shared_name stores such a string. It is also suitable for *fragments* of
82 * these names, including prefixes, suffixes, or middle parts. Therefore -- much like `boost::filesystem::path` is
83 * basically a string wrapper -- Shared_name is just a string wrapper. In fact the `std::string` it stores is
84 * accessible through str() by reference.
85 *
86 * All standard string-like operations (generally, the `+=`, `/=`, `+`, and `/` operations) are equally performant and
87 * not *any* smarter than the corresponding string concatenation ops (the `/` variants just add a separator character).
88 * The same holds for all operations, except see the next section. In particular, all characters are allowed in any
89 * position, and there is no max length enforced. Hence, the user may assume max possible performance and zero
90 * restrictions, with the exception of:
91 *
92 * ### Conventions understood/enforced by Shared_name ###
93 * As noted, generally the class allows everything and does nothing smart that `std::string` wouldn't do. This is for
94 * flexibility and performance and is inspired by `boost::filesystem::path`. However, there is *optional* support
95 * for some simple conventions. First let's discuss those conventions:
96 *
97 * In Flow-IPC, Shared_name is to be used for all shared resource *types* needed. (These are not enumerated here.)
98 * Different resource types might have different rules for (1) characters allowed; and (2) max length allowed; so that
99 * if this is violated, a creation/opening sys call (or similar) will fail with an error. Therefore, the following
100 * *conventions* are understood and represent the *union* of known system restrictions, so that sanitized()
101 * names will work for *all* known resource types. Also, some conventions are for human usability.
102 * In total, these conventions are understood:
103 * - Only certain characters are allowed. Namely, only alphanumerics [A-Za-z0-9] are allowed as of this writing.
104 * - There is a *folder* convention: We anticipate a file tree-like organization of various items; hence a
105 * folder separator character, #S_SEPARATOR, is allowed also.
106 * - A complete, usable (in a sys call or similar) name is to start with one leading #S_SEPARATOR.
107 * - We anticipate no empty "folder" names; and therefore no sequences of 2+ adjacent #S_SEPARATOR chars.
108 * - Names shall not exceed a certain max length. #S_MAX_LENGTH is chosen to be low enough to work for all supported
109 * resource types.
110 *
111 * How does Shared_name actually *use* the knowledge of these conventions? To reiterate: normally, it does not care.
112 * The only parts of its API that *do* care are as follows:
113 * - absolute() will return `true` if and only if the first char is #S_SEPARATOR. By convention, you should not
114 * pass str() to a sys call/similar, unless `absolute() == true`.
115 * - sanitized() will return `true` if and only if str() is a valid name or name fragment (no illegal characters;
116 * does not exceed #S_MAX_LENGTH; no multi-separator sequences).
117 * - sanitize() will attempt to non-destructively modify (if needed) name in such a way as to make sanitized() return
118 * `true`.
119 *
120 * You should call them only when you specifically need some conventions checked or enforced. Otherwise Shared_name
121 * ops will be as dumb/fast as possible.
122 *
123 * @note The Flow-IPC library *user* is unlikely to directly pass a Shared_name into a sys call or similar. Probably
124 * Flow-IPC internals will do it for the user. Therefore, the likeliest pattern for the user to encounter in
125 * public Flow-IPC APIs is: When initially naming some `transport` object, a constructor or factory will take
126 * a *relative* (`absolute() == false`) Shared_name prepared by the user. Within that relative fragment,
127 * the user is to use the folder conventions above (via `/=` or `/` operators perhaps) if needed. Inside
128 * Flow-IPC, the impl can then construct an absolute name by internally pre-pending stuff to the
129 * user-constructed fragment. If you have ensured the name is sanitized(), then it will not fail on account of a
130 * bad name. If you have *not*, then it may still not fail: sanitized() is a conservative criterion and may be
131 * too stringent for some resource types and OS calls. It is up to *you* to either ensure sanitized() or
132 * otherwise worry about the possibility of a bad name (illegal characters, excessive length).
133 *
134 * ### Thread safety ###
135 * Shared_name has the same safety under concurrency as `std::string` (i.e., if you intend on writing while
136 * reading/writing same `*this` concurrently, synchronized access is necessary to avoid corruption and other standard
137 * thread safety violations).
138 *
139 * @internal
140 *
141 * ### Architecture discussion / shared name convention synopsis ###
142 * As promised, we've covered the practical aspects of Shared_name first. Now let's discuss the larger design.
143 * Note that while this does inform some of Shared_name API (which is quite simple), really the discussion has wider
144 * implications, and this doc header merely seems like a good place for it. It would've been okay to split off
145 * much of the below info into ipc::session somewhere, but... maybe that's better... but the human in me (ygoldfel)
146 * feels you'll want to know this now, here, even though for formal appropriateness maybe it should be moved.
147 * In any case here it is:
148 *
149 * As noted before, there are N shared resource types, and Shared_name is to be used for all of them. At minimum,
150 * then:
151 * - 2 names are to be equal, if and only if they refer to the same object of the same type, or they're 2 different
152 * objects but of *different* types.
153 * - This must be the case at each given point in time.
154 *
155 * When designing Flow-IPC, we decided that it would be helpful for humans if we immediately made this more stringent
156 * by enforcing the following stronger requirement as well:
157 * - 2 names are to be equal if and only if they are referring to objects of the same (resource) type. So then,
158 * e.g., if a SHM-area is named "X" then a POSIX queue cannot also be named "X" at the same point in time.
159 *
160 * Further, some things are made easier if the distinctness restriction (2 distinct names <=> 2 distinct objects
161 * at each given point in time) is made more stringent by extending it to *all time*; resulting in:
162 * - 2 names are to be equal if and only if they refer to the same object of the same type.
163 * - This must be the case over all time (not merely at each *given* point in time).
164 * - In other words, if a name "X" is used for some object of some type, then it's only *ever* used for that
165 * object and never another (until reboot).
166 *
167 * @note Shared resources are assumed to be *kernel-persistent*, which means the slate is wiped with each system boot.
168 * Hence "all time" really means "within the relevant uptime."
169 *
170 * Practically speaking, then, how do we enable these distinctness guarantees, in absolute names (i.e., names to be
171 * passed, usually internally by Flow-IPC, into sys calls/similar)? Firstly, the resource type for the object
172 * referred to by each absolute name should be encoded in a standard prefix inside the name; a simple distinct constant
173 * for each resource type works. There should be a handful at most of these possible values. This will guarantee
174 * non-clashing between different types of resource.
175 *
176 * That only leaves the distinct-across-all-time requirement. However, it's outside the scope of this header to
177 * explain how that's achieved, as it involves ipc::session and other concepts and is just a large topic.
178 *
179 * For convenience, we now summarize the resulting absolute shared name convention, as of this writing, without a full
180 * rationale and with references to other doc headers if appropriate. In order, the characters of a shared name
181 * in the Flow-IPC convention are as follows. Use session::build_conventional_shared_name() to construct a
182 * path with these semantics.
183 *
184 * -# Leading #S_SEPARATOR. Indicates a full (absolute) name or prefix thereof.
185 * -# A certain constant magic string, #S_ROOT_MAGIC, indicating ::ipc is operating.
186 * (E.g., if... uh... emacs likes to put stuff in, say, SHM, at a certain SHM path, hopefully it won't clash
187 * with us accidentally.)
188 * -# A #S_SEPARATOR.
189 * -# Hard-coded resource type constant. Indicates resource type. E.g., something like "posixq" might mean a
190 * POSIX message queue. The (quite few) possible values for this are stored internally in Flow-IPC.
191 * -# A #S_SEPARATOR.
192 * -# Session-server application name. This brief name is 1-1 with all distinct executables supported by Flow-IPC.
193 * See ipc::session for details.
194 * -# A #S_SEPARATOR.
195 * -# Server-namespace. This uniquely identifies the server application *instance* across all time, for a given
196 * session-server application name. See ipc::session for how this value is computed.
197 * In the rare case where the resource applies to *all* server-namespaces, use special value #S_SENTINEL.
198 * -# A #S_SEPARATOR.
199 * -# What comes next depends on the *scope* of this particular resource.
200 * - Relevant to a given session A-B (where A and B are processes that can converse with each other),
201 * a/k/a *per-session scope*:
202 * -# Session-client application name. Analogous to session-server application name but for the supported
203 * clients. See ipc::session for details.
204 * -# A #S_SEPARATOR.
205 * -# Client-namespace. Given everything up to this point, uniquely identifies client application *instance*
206 * across all time (like server-namespace above, basically). See ipc::session for how it's computed.
207 * - Relevant to no particular process pair A-B, but does apply only to a given session-client application,
208 * a/k/a *per-client-app scope*:
209 * -# Session-client application name, as just above.
210 * -# A #S_SEPARATOR.
211 * -# The reserved client-namespace, #S_SENTINEL, indicating no particular session-client.
212 * - Global (not limited to any smaller scope above), a/k/a *global scope*. This is intended largely for
213 * internal Flow-IPC needs, as of this writing.
214 * -# The reserved session-client application name, #S_SENTINEL, indicating no particular client application.
215 * -# A #S_SEPARATOR.
216 * -# The rest of the name depends on the convention decided for that particular resource type and hence outside
217 * our scope here; see doc header for the relevant class corresponding to a shared resource type of interest.
218 * - For each object, determine the *scope* (the 3 possibilities and resulting naming conventions are just above).
219 * - Keep using the basic folder convention, with non-empty folder names and #S_SEPARATOR as separator.
220 * - When uniqueness is desired, the `1`->`2`->`3`... technique is typically used (always remembering that
221 * such an auto-ID must be safely generated across all participating processes in system).
222 *
223 * In rare cases a Shared_name will be used outside the ipc::session paradigm, such as for a lower-level purpose
224 * that nevertheless requires a shared-resource name (e.g., for a lower-level-use SHM pool). In that case
225 * the following shall be equal to #S_SENTINEL: session-server application name; server-namespace. The components
226 * subsequent to that shall be as-if it's a global-scope resource; hence: #S_SENTINEL.
227 * build_conventional_non_session_based_shared_name() shall construct such a name.
228 *
229 * @note *camelCase*: As of this writing, #S_SEPARATOR is `_` (because `/` is not available for native reasons).
230 * So, when it comes up, the convention is to use camelCase for tokens between nearby separators. Briefly:
231 * start with lower case; do not word-smoosh; each new word after word 1 is to begin with exactly 1 capital or
232 * number; an acronym is to be treated as a single word. Examples of compliant tokens:
233 * `httpUrlConnectionV44Beta`, `tableIdent`. These are bad: `httpURLConnection` (wrong acronym convention),
234 * `HTTPURLConnection` (ditto), `tableident` (word-smooshing).
235 *
236 * ### Rationale for max length handling ###
237 * As noted in the public section of this doc header: #S_MAX_LENGTH is a part of sanitized() criteria; it is not
238 * enforced by Shared_name itself outside of sanitized() and sanitize(). It is up to the Shared_name user (which
239 * may be, and usually is, internal Flow-IPC code) to actually ensure sanitized() is `true` -- or otherwise avoid system
240 * API errors on account of bad names. All *we* choose to do, regarding the length aspect, is choose one conservatively
241 * low so that:
242 * - At least it is no greater than the *lowest* inherent limit out of all the known resource types to which
243 * Shared_name applies.
244 * - Additionally, because in some cases a given `public` API takes a relative Shared_name and pre-pends an
245 * absolute "folder" path of known length, further leave slack to make it so that that prefix can still comfortably
246 * fit without hitting the limit from the previous bullet point.
247 *
248 * To wit, then, current tally of relevant resource types:
249 * - POSIX message queue (`man mq_overview`): Linux: An internal forward-slash, included in `NAME_MAX = 255`
250 * characters including NUL; so apparently: 253. (The `man` text could be interpreted as meaning 253, 254, 255;
251 * it is ambiguous. Experiments show it's 253.)
252 * - SHM pool (ultimately: `man shm_open`): Linux: Same as preceding: 253.
253 * - bipc message queue (search: boost.interprocess `message_queue`): It just opens a SHM pool per MQ; so
254 * same as preceding: 253.
255 * - Unix domain sockets (ultimately: `man unix`): Linux, abstract namespace: It's a hairy topic. `sun_path[108]`
256 * is the case as of this writing; minus 1 leading and trailing NUL; and there's a note some (non-Linux?) Unix
257 * domain socket implementations go as low as `sun_path[92]`. But in Linux: 106.
258 *
259 * So that puts us at 106 without the "prefix slack." Applying a prefix slack of 32 characters gives us: 106 - 32 =
260 * 74. We round it up slightly to 75.
261 */
263{
264public:
265 // Constants.
266
267 /// A (default-cted) Shared_name. May be useful for functions returning `const Shared_name&`.
268 static const Shared_name S_EMPTY;
269
270 /**
271 * Max value of size() such that, if str() used to name a supported shared resource, sys call safely won't barf.
272 *
273 * @todo Shared_name::S_MAX_LENGTH currently applies to all shared resource types, but it'd be a useful feature to
274 * have different limits depending on OS/whatever limitations for particular resources types such as SHM
275 * object names versus queue names versus whatever.
276 *
277 * @internal
278 *
279 * @see class doc header section: "Rationale for max length handling."
280 */
281 static constexpr size_t S_MAX_LENGTH = 75;
282
283 /**
284 * Character we use, by convention, to separate conceptual folders within str().
285 *
286 * @internal
287 *
288 * Underscores are allowed for all applicable shared resource types. Ideally we'd use them to separate words, but 2
289 * factors are responsible for making `_` the folder separator and using camelCase to separate words between pairs of
290 * nearby underscores: 1, characters are at a premium against #S_MAX_LENGTH, so we shouldn't waste them on cosmetic
291 * concerns if possible; and 2, it's unclear what other special characters (not an alphanumeric) would be suitable. */
292 static constexpr char S_SEPARATOR = '_';
293
294 /**
295 * A Shared_name fragment, with no #S_SEPARATOR characters inside, to be used in any Shared_name maintained by
296 * ::ipc itself as the leading path component.
297 */
299
300 /**
301 * A Shared_name fragment, with no #S_SEPARATOR characters inside, that represents a path component that shall
302 * be different from any other generated string at the same path depth in the same context; and represents
303 * a sentinel. In actual fact it equals `0`; and typically (though not necessarily -- other conventions may exist)
304 * generated strings shall be `1`, `2`, ..., of which the former is #S_1ST_OR_ONLY.
305 */
307
308 /**
309 * A Shared_name fragment, with no #S_SEPARATOR characters inside, that represents a path component that
310 * (1) is not #S_SENTINEL and (2) is suggested as the *first* or *only* unique ID of
311 * items at the same depth in the same context. In actual fact it equals `1`; and typically (though not
312 * necessarily -- other conventions may exist) generated strings shall be `1`, `2`, ..., of which
313 * the former is #S_1ST_OR_ONLY.
314 *
315 * So if #S_SENTINEL means "not a thing" or "special thing to be treated not like a normal thing,"
316 * then #S_1ST_OR_ONLY means "a normal thing, of which there may be only one, and this is the first one
317 * so created."
318 *
319 * ### Suggested use pattern ###
320 * This is most useful when, at this path level, you currently only have reason to have one object.
321 * Then you can name it #S_1ST_OR_ONLY without relying on a magic string `1`. However, if in the future
322 * there is reason to add more objects at the same level, then remove the use of `S_1ST_OR_ONLY` and instead
323 * use a `uint` or `atomic<uint>` data member that starts at simply the numeric `1`, `++`ing it each time a
324 * new object is added (and converting it via `Shared_name::ct(std::to_string(x))` to string).
325 * The first object will have the same name as before, compatibly, while subsequent ones will be
326 * efficiently named uniquely.
327 *
328 * Is this cheesy and reliant on the fact that this constant is, in fact, a string conversion of the number one?
329 * Yes, but the above pattern works and is reasonably efficient. I (ygoldfel) considered making this
330 * a super-general API that keeps generating unique IDs, but it just seemed like overkill given the simplicity
331 * of the task. Converting back from Shared_name, generating a new `++`ed one, then converting again -- while
332 * providing a simple-enough API to ensure atomicity -- seemed inferior to just letting people
333 * maintain a compatible `atomic<uint>`, when and if desired, and using #S_1ST_OR_ONLY until then.
334 */
336
337 /// Relative-folder fragment (no separators) identifying the resource type for: SHM pools.
339
340 /// Relative-folder fragment (no separators) identifying the resource type for: boost.interprocess named mutex.
342
343 // Constructors/destructor.
344
345 /// Constructs empty() name.
347
348 /**
349 * Copy-constructs from an existing Shared_name.
350 *
351 * @param src
352 * Source object.
353 */
355
356 /**
357 * Move-constructs from an existing Shared_name, which is made empty() if not already so.
358 *
359 * @param src_moved
360 * Source object, which is potentially modified.
361 */
363
364 /**
365 * Copy-constructs from a `char` range given as a pair of random-iterators; in particular `const char*`s work.
366 *
367 * @tparam Input_it
368 * An STL-compliant random iterator type. In particular `const char*` works.
369 * @param begin
370 * Start of range to copy.
371 * @param end
372 * One past last element in range to copy (`begin` to copy nothing).
373 */
374 template<typename Input_it>
375 explicit Shared_name(Input_it begin, Input_it end);
376
377 // `static` ctors.
378
379 /**
380 * Copy-constructs from a `char`-sequence container such as `string` or util::String_view.
381 * This overload shall be used on a non-`&&` arg if `ct(const char*)` does not apply.
382 *
383 * Specifically the returned object's internal `std::string` is constructed as: `std::string s(raw_name)`.
384 * As a result, whatever most-performant available single-arg ctor `basic_string` makes available is forwarded-to.
385 * (E.g., C++17 has a `String_view_like` ctor which is overload-resolved-to only when it most makes sense.)
386 * Hence `Source` must be something `std::string` can be constructed from in that way; e.g., `vector<char>`
387 * is not: use the 2-iterator ctor for that.
388 *
389 * ### Rationale ###
390 * See class doc header for rationale as-to why this is a `static` ctor as opposed to a regular ctor.
391 *
392 * @tparam Source
393 * See above.
394 * @param src
395 * String to copy.
396 * @return The new object.
397 */
398 template<typename Source>
399 static Shared_name ct(const Source& src);
400
401 /**
402 * Copy-constructs from a NUL-terminated `const char*` string.
403 *
404 * ### Rationale ###
405 * See class doc header for rationale as-to why this is a `static` ctor as opposed to a regular ctor.
406 *
407 * @param src
408 * String to copy.
409 * @return The new object.
410 */
411 static Shared_name ct(const char* src);
412
413 /**
414 * Destructively move-constructs from an `std::string`, emptying that source object.
415 *
416 * ### Rationale ###
417 * See class doc header for rationale as-to why this is a `static` ctor as opposed to a regular ctor.
418 *
419 * @param src_moved
420 * String to move (make-`.empty()`).
421 * @return The new object.
422 */
423 static Shared_name ct(std::string&& src_moved);
424
425 /**
426 * Constructs from a value of an integer type, efficiently converting to a string representation of the number
427 * in base 10. Note that a negative value yields a leading `-`, so the result is then not sanitized().
428 *
429 * @tparam Integer
430 * Signed or unsigned integer type of bit width 8, 16, 32, or 64.
431 * In particular `bool` is not supported (will not compile); `char`/`int8_t` and `unsigned char`/`uint8_t`
432 * are supported.
433 * @param src
434 * Integer to convert.
435 * @return The new object.
436 */
437 template<typename Integer>
438 static Shared_name ct_from_int(Integer src);
439
440 // Methods.
441
442 /**
443 * Copy-assigns from an existing Shared_name.
444 *
445 * @param src
446 * Source object.
447 * @return `*this`.
448 */
450
451 /**
452 * Move-assigns from an existing Shared_name.
453 *
454 * @param src_moved
455 * Source object, which is potentially modified.
456 * @return `*this`.
457 */
459
460 /**
461 * Returns (sans copying) ref to immutable entire wrapped name string, suitable to pass into sys calls when naming
462 * supported shared resources assuming `absolute() == true`. If you require a NUL-terminated string (such as for
463 * a native call), use native_str().
464 *
465 * @note Returning a util::String_view seemed pointless, as one can always be constructed easily by the caller, and
466 * in any case -- for the time being at least -- some/many APIs that take non-C-strings will take `std::string`
467 * as opposed to a util::String_view.
468 *
469 * @return See above.
470 */
471 const std::string& str() const;
472
473 /**
474 * Returns (sans copying) pointer to NUL-terminated wrapped name string, suitable to pass into sys calls when naming
475 * supported shared resources assuming `absolute() == true`. If you require an `std::string` (such as for some fancy
476 * boost.interprocess call), use str().
477 *
478 * ### Why isn't it named `c_str()`? ###
479 * It was, but `capnp::StringPtr` (and similar/derived classes such as `capnp::Text::Reader`) has a "clever"
480 * `operator T()` conversion operator that is enabled for all `T` that have `.c_str()`; and its implementation
481 * relies on being able to symmetrically construct `T(const char*)` -- like `std::string`. We intentionally
482 * lack that. Hence renamed this away from `.c_str()`. This may appear like kow-towing to capnp's quirks,
483 * but actually conceivably that's a pattern people use, so let's not break it.
484 *
485 * @return See above.
486 */
487 const char* native_str() const;
488
489 /**
490 * Returns `str().size()`.
491 * @return See above.
492 */
493 size_t size() const;
494
495 /**
496 * Returns `true` if and only if `str().empty() == true`.
497 * @return See above.
498 */
499 bool empty() const;
500
501 /**
502 * Returns `true` if and only if `!this->empty()`, and str() ends with the #S_SEPARATOR character.
503 * @return See above.
504 */
505 bool has_trailing_separator() const;
506
507 /**
508 * Returns `true` if and only if the first character is #S_SEPARATOR. By Flow-IPC convention, any name actually
509 * passed to a sys call in order to name a shared resource has `absolute() == true`. Note, however, that absolute()
510 * being `true` does not necessarily mean str() is the full name of a resource: it may well still be a fragment
511 * (a prefix) of some eventual name passed to a sys call. To obtain the full name one would append more stuff.
512 *
513 * @return See above.
514 */
515 bool absolute() const;
516
517 /// Makes it so `empty() == true`.
518 void clear();
519
520 /**
521 * Returns `true` if and only if the contained name/fragment is *sanitized* according to length, legal characters,
522 * and similar. More precisely, a sanitized `*this` satisfies all of the following:
523 * - There are no illegal characters. (E.g., in particular, some characters would not be accepted when naming
524 * a SHM object in Linux.)
525 * - In particular, only #S_SEPARATOR is used as a folder separator.
526 * - There is no more than 1 instance of #S_SEPARATOR character in a row.
527 * - size() does not exceed #S_MAX_LENGTH.
528 *
529 * ### Performance ###
530 * It is linear-time, with at most one scan through str().
531 *
532 * @return See above.
533 */
534 bool sanitized() const;
535
536 /**
537 * Best-effort attempt to turn sanitized() from `false` to `true`, unless it is already `true`; returns the final
538 * value of sanitized() indicating whether it was successful. If `false` returned, then the final value of str()
539 * will equal the initial value.
540 *
541 * This utility should be used very judiciously and with full knowledge of what it actually does. It should *not* be
542 * used "just in case" or "prophylactically" but only with full knowledge of where the current value of str() might
543 * originate. For example, it might come from some user input. Another example involves, perhaps, concatenating
544 * two path fragments in such a way as to potentially yield a double-#S_SEPARATOR situation: sanitize() after this
545 * would collapse the separators into just 1 separator. Yet another example is simply that if one combines
546 * two paths which don't exceed #S_MAX_LENGTH, but the result might exceed it: running sanitize() and ensuring it
547 * returns `true` guaranteed one didn't accidentally exceed #S_MAX_LENGTH.
548 *
549 * In other words, use it when you want it to do what it, in fact, does. And that is:
550 * - Any '/' (forward-slash) character, as a special case, is transformed into #S_SEPARATOR.
551 * - After any character replacements above: Any sequence of 2 or more #S_SEPARATOR characters is collapsed into
552 * one #S_SEPARATOR.
553 *
554 * Things it does *not* do, modulo the above:
555 * - No legal or illegal character is changed (except '/' and separator collapsing).
556 * - It doesn't truncate to try to bring length to #S_MAX_LENGTH or less.
557 *
558 * However, the method's return value is still significant, in that if it is "still" `false`, then you know you have
559 * a real problem -- yet if it's `true`, then it didn't do anything destructive to make it so.
560 *
561 * Note, again, that the above replacements are "undone" if `false` is returned. In other words, the function
562 * won't sanitize "halfway."
563 *
564 * ### Performance ###
565 * There is at most one linear scan through str(), an allocation of `size()`d buffer, and a dealloc of ~similar.
566 *
567 * @return What sanitized() would return just before returning from the present function.
568 */
569 bool sanitize();
570
571 /**
572 * Appends a folder separator followed by the given other Shared_name.
573 * Functionally equivalent to `return *this += ((string(1, S_SEPARATOR) + src_to_append.str()));`.
574 * If there's any reason to suspect `this->str()` already ends in #S_SEPARATOR, and/or that the resulting name
575 * might be too long, execute sanitize() afterwards and ensure that returns `true`.
576 *
577 * @note The corner case, wherein `&src_to_append == this`, is handled properly.
578 *
579 * It is stylistically (and possibly for performance) better to use this rather than manually appending
580 * a separator and then `src_to_append` (with `+=`).
581 *
582 * ### Rationale for not doing something smarter like avoiding a double-separator due to concatenation ###
583 * Basically we want things to be as fast as possible by default; and that is to make it as close to trivial
584 * string concatenation as possible. If more paranoia is required, we want the user to intentionally use
585 * sanitize() or sanitized().
586 *
587 * @param src_to_append
588 * Thing to append after appending separator.
589 * @return `*this`.
590 *
591 * @internal
592 *
593 * ### Rationale for having this `/=` as opposed to only the version taking `Source`, or vice versa ###
594 * If we had only the one taking Shared_name, then tried to use `+= X`, where `X` is `std::string` or `const char*`
595 * or util::String_view, it'd have to (implicitly) construct a Shared_name first, and that is a bit slower.
596 *
597 * If we had only the one taking `Source` (e.g., `String_view`), and user wanted to append a Shared_name, they'd
598 * have to use `X.str()` explicitly, losing a bit of syntactic sugar.
599 *
600 * Same deal with `+=` and the binary (`+`, `/`) derivations of these.
601 */
602 Shared_name& operator/=(const Shared_name& src_to_append);
603
604 /**
605 * Simply appends a folder separator followed by `raw_name_to_append` to the current value of str().
606 * Specifically the internal `std::string` is modified as: `s += S_SEPARATOR; s += raw_name_to_append`.
607 * As a result, whatever most-performant available (single-arg by definition) `operator+=` that `basic_string`
608 * makes available is forwarded-to for the 2nd `+=`. (E.g., C++17 has a `String_view_like` appender which is
609 * overload-resolved-to only when it most makes sense.)
610 *
611 * If there's any reason to suspect `this->str()` already ends in #S_SEPARATOR, and/or that the resulting name
612 * might be too long, execute sanitize() afterwards and ensure that returns `true`.
613 *
614 * It is stylistically (and possibly for performance) better to use this rather than manually appending
615 * a separator and then `src_to_append` (with `+=`).
616 *
617 * See Rationale(s) for the other operator/=(), as they apply here.
618 *
619 * ### Caveat (handled) ###
620 * If `raw_name_to_append` represents a range of characters that overlaps that of pre-call str() -- including
621 * their being equal -- this API will act functionally as-if one made a `string` copy of `raw_name_to_append`
622 * first and appended (a separator and) that copy. (This is similar to the guarantees made by `string` append APIs.)
623 *
624 * @tparam Source
625 * See above.
626 * @param raw_name_to_append
627 * Thing to append after appending separator.
628 * @return `*this`.
629 */
630 template<typename Source>
631 Shared_name& operator/=(const Source& raw_name_to_append);
632
633 /**
634 * Appends the given other Shared_name.
635 * Functionally equivalent to `return *this += src_to_append.str();`.
636 * If there's any reason to suspect the resulting name might be too long, execute sanitize() afterwards and ensure
637 * that returns `true`.
638 *
639 * It is stylistically (and possibly for performance) better to use `/=` rather than manually appending
640 * a separator and then `src_to_append` (with this `+=`).
641 *
642 * See Rationale(s) for operator/=(), as they apply here.
643 *
644 * @param src_to_append
645 * Thing to append.
646 * @return `*this`.
647 */
648 Shared_name& operator+=(const Shared_name& src_to_append);
649
650 /**
651 * Simply appends `raw_name_to_append` to the current value of str(). Specifically the internal
652 * `std::string` is modified as: `s += raw_name_to_append`. As a result, whatever most-performant available
653 * (single-arg by definition) `operator+=` that `basic_string` makes available is forwarded-to.
654 * (E.g., C++17 has a `String_view_like` appender which is overload-resolved-to only when it most makes sense.)
655 *
656 * If there's any reason to suspect the resulting name might be too long, execute sanitize() afterwards and ensure
657 * that returns `true`.
658 *
659 * It is stylistically (and possibly for performance) better to use `/=` rather than manually appending
660 * a separator and then `src_to_append` (with this `+=`).
661 *
662 * See Rationale(s) for operator/=(), as they apply here.
663 *
664 * @tparam Source
665 * See above.
666 * @param raw_name_to_append
667 * Thing to append.
668 * @return `*this`.
669 */
670 template<typename Source>
671 Shared_name& operator+=(const Source& raw_name_to_append);
672
673private:
674 // Friends.
675
676 // Friend for access to Shared_name.
677 friend void swap(Shared_name& val1, Shared_name& val2);
678
679 // Methods.
680
681 /**
682 * Helper that returns `true` if and only if: `ch` is a valid character for purposes of `.sanitized() == true`
683 * but is not #S_SEPARATOR.
684 *
685 * @param ch
686 * Character.
687 * @return See above.
688 */
689 static bool sanitized_non_sep(char ch);
690
691 // Data.
692
693 /**
694 * The name or name fragment; see str().
695 * @todo Consider providing a ref-to-mutable accessor to Shared_name::m_raw_name (or just make `public`).
696 * There are pros and cons; the basic pro being that Shared_name is meant to be a very thin wrapper around
697 * `std::string`, so it might make sense to allow for in-place modification without supplying some kind of reduced
698 * subset of `string` API. Suggest doing this to-do if a practical desire comes about.
699 */
700 std::string m_raw_name;
701}; // class Shared_name
702
703// Free functions: in *_fwd.hpp.
704
705// Template implementations.
706
707template<typename Input_it>
708Shared_name::Shared_name(Input_it begin, Input_it end) :
709 m_raw_name(begin, end) // Copy it.
710{
711 // Nothing.
712}
713
714template<typename Source>
715Shared_name Shared_name::ct(const Source& src) // Static.
716{
717 Shared_name result;
718
719 /* As advertised: invoke whatever `string` ctor best applies. Well, actually, to avoid needing to move-assign
720 * (which involves a few scalar copies), use .assign() instead; but its complement of overloads equals that
721 * of the relevant complement of ctor overloads. */
722 result.m_raw_name.assign(src);
723
724 return result;
725}
726
727template<typename Integer>
729{
730 using std::is_same_v;
731 using std::is_integral_v;
732 using std::make_unsigned_t;
733 using std::errc;
734 using std::to_chars;
735
736 using int_t = Integer;
737 static_assert(is_integral_v<int_t>, "Use an integer type please.");
738 static_assert(!is_same_v<int_t, bool>, "Just use actual integer types please (char/uchar/int8_t/uint8_t are fine).");
739 using unsigned_t = make_unsigned_t<int_t>;
740
741 // Use to_chars() which is mega-fast in a couple ways. Overkill? Maybe.
742
743 Shared_name result;
744 auto& name = result.m_raw_name;
745
746 /* Subtlety: is_same_v<> can yield surprising results such as `long` (or `int64_t`) and `long long` being
747 * "not the same type," even though they're both signed 64-bit types that behave identically in terms
748 * of arithmetic, storage, etc. So, e.g., different overloads/specializations might apply. That's why
749 * here we use sizeof() comparisons instead of is_same_v<>. */
750 static_assert(sizeof(unsigned_t) <= (64 / 8),
751 "Please use one of the integer types of bit width in [8, 64].");
752 /* Reserve space sufficient for signed and unsigned versions of each type-width.
753 * For the 1st 3: `2^n - 1` and `2^(n - 1)` have the same # of digits. Add for the possible minus sign. */
754 if constexpr(sizeof(unsigned_t) == sizeof(uint8_t)) { name.resize(sizeof("-128") - 1); }
755 else if constexpr(sizeof(unsigned_t) == sizeof(uint16_t)) { name.resize(sizeof("-32768") - 1); }
756 else if constexpr(sizeof(unsigned_t) == sizeof(uint32_t)) { name.resize(sizeof("-2147483648") - 1); }
757 /* Then 64-bit integers are extra fun (64th bit changes # of decimal digits):
758 * unsigned => `2^64 - 1` has 20 digits (and no minus sign).
759 * signed => -2^63 has 19 digits not including 1 char for the minus sign => 20. So the answer is 20. */
760 else if constexpr(sizeof(unsigned_t) == sizeof(uint64_t)) { name.resize(20); }
761
762 const auto start_ptr = &(name.front());
763 const auto to_chars_result = to_chars(start_ptr, &(name.back()) + 1, src);
764 assert((to_chars_result.ec == errc{}) && "Did we not reserve enough space in target string?!");
765
766 name.resize(to_chars_result.ptr - start_ptr);
767
768 return result;
769} // Shared_name::ct_from_int()
770
771template<typename Source>
772Shared_name& Shared_name::operator+=(const Source& raw_name_to_append)
773{
774 // Existence/impl rationale: This is faster than if they had to: `+= Shared_name::ct(raw_name_to_append)`.
775
776 m_raw_name += raw_name_to_append;
777 return *this;
778
779 /* Subtlety (avoided): All the plus-y APIs should invoke us here eventually, or do the same thing, but that's not
780 * the point. The point is:
781 *
782 * If raw_name_to_append happens to represent a range overlapping that of this->m_raw_name (such as
783 * `s += s`, but there are weirder possibilities like `const char*` inside .str()), it's all still fine, simply
784 * because std::string `+=` is officially guaranteed to work, and we forward directly to it without adding any
785 * more stuff. (In practice std::string would check for overlap and, if detected, judiciously do a proper thing.
786 * Point there being: it has to specifically handle the overlap case. So we don't have to.)
787 *
788 * The slash-y (/, /=) family is a different story however.... We deal with that over there. */
789}
790
791template<typename Source>
792Shared_name& Shared_name::operator/=(const Source& raw_name_to_append)
793{
794 using std::is_convertible_v;
795 using std::less;
796 using std::string;
797 using std::string_view;
798
799 // Existence/impl rationale: This is faster than if they had to: `/= Shared_name::ct(raw_name_to_append)`.
800
801 /* We really promised, and all else being equal (which it's not) would prefer to just do, the following:
802 * m_raw_name += S_SEPARATOR; return *this += raw_name_to_append;
803 * Note, incidentally, that all the / and /= APIs eventually use us, so this is the core impl as of now.
804 * If we get it right, then those APIs are good to go (though, potentially, some unnecessary computing might
805 * happen too; we'll get into that below).
806 *
807 * The above impl would work great in all cases, except when (and this case is mentioned in our public
808 * contract/doc header) raw_name_to_append represents a range that overlaps m_raw_name's.
809 * - A: If Source=string, and raw_name_to_append *is* m_raw_name, then `m_raw_name += SEP` modifies
810 * raw_name_to_append; then `m_raw_name += <itself>` adds an unwanted SEP at the end.
811 * - B: If Source=std::string_view or String_view (convertible to string_view) or other similar
812 * convertible-to-string_view type: `m_raw_name += SEP`, if string+= decides to realloc (we do not
813 * try to depend on whether it does so only when required; by standard in any case it can do it even if not),
814 * can invalidate raw_name_to_append (it would point at garbage). If not, it might still change the
815 * contents of that range.
816 * - C: Otherwise (Source=char, initializer_list<char>, ...): No problem; no overlap is possible.
817 *
818 * We promised not to mess this up. So:
819 * - A: Really this is just a case of B; string is convertible to string_view. With other impl tactics it could
820 * be its own thing, so we listed it separately, for clarity/posterity/completeness.
821 * - C: To handle it: just do the simple thing we'd prefer to do. To detect it: it's not B, so detect B
822 * and handle B properly; otherwise do the simple thing.
823 *
824 * That leaves B. To detect: use the traits-thing is_convertible_v + explicitly check for the range-overlap
825 * case. If detected: To handle it: We just make a copy of raw_name_to_append first,
826 * append SEP, then append the copy (instead of raw_name_to_append range as in the naive impl).
827 * (This does some unnecessary copying. Could instead copy directly within m_raw_name; but this adds
828 * cognitive difficulties/worries about overlap shape/longer code (and it would be subtly wrong, if the range
829 * includes the trailing NUL, which SEP would overwrite). That's too annoying given, at least, the rarity of the
830 * corner case. If really desired can tweak that part, alone, later.)
831 *
832 * So that's what we'll do.
833 *
834 * @todo We could do a tiny bit better by handling the ostensibly more-common sub-cases of (the already uncommon)
835 * B. The present function is fine; but: Case A above (Source=string) can get its own overload which would only
836 * need to check for `&m_raw_name == &raw_name_to_append` rather than the more-complex overlap check.
837 * /=(const Shared_name&) can forward to that overload also. The rest would be handled by the present function. */
838
839 if constexpr(is_convertible_v<const Source&, string_view>)
840 {
841 /* This is case B (currently including case A) above. Overlap (non-empty intersection) of the ranges is possible.
842 *
843 * less<const char*> (instead of < et al): gets formally well-defined ordering even for pointers into unrelated
844 * objects. In practice it would've been fine, but this is better/free. */
845 const string_view view_to_append{raw_name_to_append};
846 const less<const char*> lt;
847
848 if (view_to_append.empty())
849 {
851 }
852 else if (lt(view_to_append.data(), m_raw_name.data() + m_raw_name.size())
853 && lt(m_raw_name.data(), view_to_append.data() + view_to_append.size()))
854 {
855 const string copy_to_append{view_to_append}; // Overlap: append a copy.
856 m_raw_name.reserve(m_raw_name.size() + 1 + copy_to_append.size()); // Tiny optimization.
858 m_raw_name += copy_to_append;
859 }
860 else // if (no overlap)
861 {
862 // As a bonus can do the tiny .reserve() optimization.
863 m_raw_name.reserve(m_raw_name.size() + 1 + view_to_append.size());
865 m_raw_name += view_to_append;
866 }
867 return *this;
868 } // if constexpr(case B above: Source is string_view-like)
869 else // if constexpr(case C above: raw_name_to_append is char, initializer_list<char>, ... => cannot overlap)
870 {
872 return operator+=(raw_name_to_append);
873 }
874} // Shared_name::operator/=()
875
876template<typename Source>
877Shared_name operator+(const Shared_name& src1, const Source& raw_src2)
878{
879 // Existence/impl rationale: This is faster than if they had to: `src1 + Shared_name::ct(raw_src2)`.
880
881 return Shared_name{src1} += raw_src2;
882}
883
884template<typename Source>
885Shared_name operator+(const Source& raw_src1, const Shared_name& src2)
886{
887 // Existence rationale: For symmetry with overload: (src1, raw_src2).
888
889 return Shared_name::ct(raw_src1) += src2;
890}
891
892template<typename Source>
893Shared_name operator/(const Shared_name& src1, const Source& raw_src2)
894{
895 // Existence/impl rationale: This is faster than if they had to: `src1 / Shared_name::ct(raw_src2)`.
896
897 return Shared_name{src1} /= raw_src2;
898}
899
900template<typename Source>
901Shared_name operator/(const Source& raw_src1, const Shared_name& src2)
902{
903 // Existence rationale: For symmetry with overload: (src1, raw_src2).
904
905 return Shared_name::ct(raw_src1) /= src2;
906}
907
908template<typename Persistent_object, typename Filter_func>
909unsigned int remove_each_persistent_if(flow::log::Logger* logger_ptr, const Filter_func& filter_func)
910{
911 // This is pretty much completely described by the doc header; so keeping comments light.
912
913 Error_code err_code;
914 unsigned int count = 0;
915
916 Persistent_object::for_each_persistent([&](const Shared_name& name)
917 {
918 if (filter_func(name)) // filter_func() throws => for_each_persistent() stops iterating, throws (as promised).
919 {
920 Persistent_object::remove_persistent(logger_ptr, name, &err_code);
921 if (!err_code)
922 {
923 ++count;
924 }
925 // else { Probably permission error; but regardless just move past it as advertised. }
926 }
927 });
928
929 return count;
930} // remove_each_persistent_if()
931
932template<typename Persistent_object>
933unsigned int remove_each_persistent_with_name_prefix(flow::log::Logger* logger_ptr,
934 const Shared_name& name_prefix_or_empty)
935{
936 using util::String_view;
937
938 FLOW_LOG_SET_CONTEXT(logger_ptr, Log_component::S_TRANSPORT);
939 FLOW_LOG_TRACE("Will attempt to remove-persistent objects (type [" << typeid(Persistent_object).name() << "]) with "
940 "prefix [" << name_prefix_or_empty.str() << "] (<-- may be blank; then all are removed).");
941
942 const String_view name_prefix_view{name_prefix_or_empty.str()};
943 const auto count = remove_each_persistent_if<Persistent_object>(get_logger(), [&](const Shared_name& name)
944 {
945 return name_prefix_view.empty() || String_view{name.str()}.starts_with(name_prefix_view);
946 });
947
948 if (count != 0)
949 {
950 FLOW_LOG_INFO("Removed-persistent successfully [" << count << "] objects "
951 "(type [" << typeid(Persistent_object).name() << "]) with "
952 "prefix [" << name_prefix_or_empty << "] (<-- may be blank; then all were potentially removed). "
953 "Note this counts only successful removals (not ones that failed due to permissions, say). "
954 "Details potentially logged above (including any errors which are otherwise ignored).");
955 }
956
957 return count;
958} // remove_each_persistent_with_name_prefix()
959
960} // namespace ipc::util
String-wrapping abstraction representing a name uniquely distinguishing a kernel-persistent entity fr...
static Shared_name ct_from_int(Integer src)
Constructs from a value of an integer type, efficiently converting to a string representation of the ...
bool sanitized() const
Returns true if and only if the contained name/fragment is sanitized according to length,...
const char * native_str() const
Returns (sans copying) pointer to NUL-terminated wrapped name string, suitable to pass into sys calls...
Definition: shared_name.cpp:94
Shared_name & operator=(Shared_name &&src_moved)
Move-assigns from an existing Shared_name.
Shared_name & operator+=(const Shared_name &src_to_append)
Appends the given other Shared_name.
Definition: shared_name.cpp:66
Shared_name(const Shared_name &src)
Copy-constructs from an existing Shared_name.
Shared_name()
Constructs empty() name.
static constexpr size_t S_MAX_LENGTH
Max value of size() such that, if str() used to name a supported shared resource, sys call safely won...
static const Shared_name S_SENTINEL
A Shared_name fragment, with no S_SEPARATOR characters inside, that represents a path component that ...
static Shared_name ct(const Source &src)
Copy-constructs from a char-sequence container such as string or util::String_view.
static const Shared_name S_RESOURCE_TYPE_ID_SHM
Relative-folder fragment (no separators) identifying the resource type for: SHM pools.
std::string m_raw_name
The name or name fragment; see str().
static const Shared_name S_RESOURCE_TYPE_ID_MUTEX
Relative-folder fragment (no separators) identifying the resource type for: boost....
static bool sanitized_non_sep(char ch)
Helper that returns true if and only if: ch is a valid character for purposes of ....
static const Shared_name S_EMPTY
A (default-cted) Shared_name. May be useful for functions returning const Shared_name&.
bool has_trailing_separator() const
Returns true if and only if !this->empty(), and str() ends with the S_SEPARATOR character.
Shared_name(Shared_name &&src_moved)
Move-constructs from an existing Shared_name, which is made empty() if not already so.
void clear()
Makes it so empty() == true.
bool empty() const
Returns true if and only if str().empty() == true.
Shared_name & operator/=(const Shared_name &src_to_append)
Appends a folder separator followed by the given other Shared_name.
Definition: shared_name.cpp:72
friend void swap(Shared_name &val1, Shared_name &val2)
Swaps two objects.
static const Shared_name S_1ST_OR_ONLY
A Shared_name fragment, with no S_SEPARATOR characters inside, that represents a path component that ...
bool absolute() const
Returns true if and only if the first character is S_SEPARATOR.
Shared_name & operator=(const Shared_name &src)
Copy-assigns from an existing Shared_name.
bool sanitize()
Best-effort attempt to turn sanitized() from false to true, unless it is already true; returns the fi...
static constexpr char S_SEPARATOR
Character we use, by convention, to separate conceptual folders within str().
const std::string & str() const
Returns (sans copying) ref to immutable entire wrapped name string, suitable to pass into sys calls w...
Definition: shared_name.cpp:89
static const Shared_name S_ROOT_MAGIC
A Shared_name fragment, with no S_SEPARATOR characters inside, to be used in any Shared_name maintain...
size_t size() const
Returns str().size().
Definition: shared_name.cpp:99
Flow-IPC module containing miscellaneous general-use facilities that are ubiquitously used by ~all Fl...
Definition: util.cpp:31
unsigned int remove_each_persistent_if(flow::log::Logger *logger_ptr, const Filter_func &filter_func)
Utility that invokes Persistent_object::for_each_persistent() and synchronously invokes Persistent_ob...
Shared_name operator/(const Shared_name &src1, const Shared_name &src2)
Returns new object equal to Shared_name(src1) /= src2.
Definition: shared_name.cpp:84
Shared_name operator+(const Shared_name &src1, const Shared_name &src2)
Returns new object equal to Shared_name(src1) += src2.
Definition: shared_name.cpp:78
unsigned int remove_each_persistent_with_name_prefix(flow::log::Logger *logger_ptr, const Shared_name &name_prefix_or_empty)
Utility that invokes remove_each_persistent_if() with the filter that returns true (yes,...
flow::util::String_view String_view
Short-hand for Flow's String_view.
Definition: util_fwd.hpp:112
flow::Error_code Error_code
Short-hand for flow::Error_code which is very common.
Definition: common.hpp:313