![]() |
|
An immutable, interned string whose character data is guaranteed to remain valid at a fixed address for the remainder of the process. More...
#include <eternalString.h>
Public Member Functions | |
| TF_API | TfEternalString () |
| Construct the empty eternal string. | |
| std::string const & | GetString () const |
| Return the underlying string. | |
| std::string_view | GetStringView () const |
| Return a view of the characters. | |
std::string-compatible accessors | |
| char const * | c_str () const |
| char const * | data () const |
| size_t | size () const |
| size_t | length () const |
| bool | empty () const |
Implicit conversions | |
Both of these hand out a reference or a view of internal state, which for most types would be a dangling hazard. Here they are safe even when the std::string_view view = TF_FUNC_NAME(); // safe
#define TF_FUNC_NAME() Get the name of the current function as a TfEternalString. Definition diagnostic.h:293 Note the Providing both is a deliberate trade with one known cost. A callee overloaded on both If you hit that, the local fix is to pass | |
| operator std::string const & () const | |
| operator std::string_view () const | |
Static Public Member Functions | |
| static TF_API TfEternalString | Immortalize (std::string_view content) |
Return the TfEternalString naming content, interning its characters if they are not interned already. | |
Friends | |
Operators | |
These are hidden friends: findable only by argument-dependent lookup on They must be written out because the corresponding | |
| std::string | operator+ (TfEternalString a, std::string_view b) |
| std::string | operator+ (std::string_view a, TfEternalString b) |
| std::string | operator+ (TfEternalString a, TfEternalString b) |
| bool | operator== (TfEternalString a, TfEternalString b) |
| Compare by address, which interning makes a content identity in both directions – see the class documentation. | |
| bool | operator== (TfEternalString a, std::string_view b) |
| bool | operator== (std::string_view a, TfEternalString b) |
| bool | operator!= (TfEternalString a, TfEternalString b) |
| bool | operator!= (TfEternalString a, std::string_view b) |
| bool | operator!= (std::string_view a, TfEternalString b) |
| template<class HashState > | |
| void | TfHashAppend (HashState &h, TfEternalString s) |
| Hash by address, which interning makes a content identity – see the class documentation. | |
| TF_API friend std::ostream & | operator<< (std::ostream &o, TfEternalString s) |
An immutable, interned string whose character data is guaranteed to remain valid at a fixed address for the remainder of the process.
The point of this type is not to be a better string; it is to make a promise to the functions you pass it to, in a form the compiler can check. A callee that receives a TfEternalString may:
c_str() / data() indefinitely without copying, and The characters live in the TfToken table, which deduplicates by content, so the implication runs both ways and holds for the life of the process:
That is, two equal addresses denote the same characters, and two differing addresses denote different characters. Equality and hashing are therefore defined on the address, and are constant time regardless of length.
Interning also means there is only ever one copy of a given string. Two call sites that produce the same content share it, rather than each holding their own copy. TF_FUNC_NAME(), which returns a TfEternalString, does this whenever ArchGetPrettierFunctionName() truncates two names to the same text:
Two call sites, one string, one address. Content that an ordinary TfToken elsewhere in the process already names is shared as well.
Creating one permanently retains an entry in the TfToken table. Nothing reclaims it, deliberately, to satisfy the immortality guarantee. Creating one also costs a hash, a strcmp, and a lock on one of the token table's sets, which is paid even when the content is already interned and no allocation results.
This makes the type suitable for a bounded set of strings fixed by the program source code – one per call site, as TF_FUNC_NAME() does – and actively not suitable for anything dynamic or derived from data. Never call Immortalize() in a loop, per prim, per frame, or on user input. Repeating the same content costs no additional space, since it dedups; the hazard is unbounded distinct content. If you cannot name a static bound on how many distinct strings your code creates, this is the wrong type.
This type is implemented on the TfToken table: Immortalize() creates an immortal token and keeps the address of its characters. That is where the content identity and the sharing described above come from.
Given that, the reason a separate type exists is not that an immortal TfToken is less durable. It is the same storage and exactly as durable. The reasons are these:
TfToken's immortal constructors) but it is a property of how a particular token was made, not of the type. A callee handed a TfToken must consult IsImmortal() and handle the case when it is not. Handed a TfEternalString, it knows statically that retaining the pointer is safe. TfToken is not a drop-in for a string-like result. It has no c_str(), length(), or empty(), and no operator+ at all – and its implicit std::string const & conversion does not rescue concatenation, because std::operator+ is a template and deduction does not consider user-defined conversions. Every concatenating or c_str() call site would have to change. tf/diagnostic.h, which defines TF_FUNC_NAME(), is deliberately light and does not include tf/token.h. Making it do so would pull the token table's dependencies into nearly every translation unit. Only tf/eternalString.cpp includes tf/token.h, so depending on the token table for storage costs this header nothing. Definition at line 102 of file eternalString.h.
| TF_API TfEternalString | ( | ) |
Construct the empty eternal string.
|
inline |
Definition at line 126 of file eternalString.h.
|
inline |
Definition at line 127 of file eternalString.h.
|
inline |
Definition at line 130 of file eternalString.h.
|
inline |
Return the underlying string.
Definition at line 134 of file eternalString.h.
|
inline |
Return a view of the characters.
Definition at line 137 of file eternalString.h.
|
static |
Return the TfEternalString naming content, interning its characters if they are not interned already.
This is spelled as a named factory, not a converting constructor, so that callers make an explicit acknowledgement of intent at the call site. There is deliberately no implicit conversion from char const * or std::string for the same reason. See Cost.
Embedded NULs are not supported. content is trimmed at the first NUL, so Immortalize("a\\0b") and Immortalize("a") name the same string. This matches TfToken, which derives string identity from a NUL-terminated c-string and therefore cannot tell such a name from its prefix either. Consequently size() is always strlen(c_str()), and this type is for names, not for arbitrary binary content.
|
inline |
Definition at line 129 of file eternalString.h.
|
inline |
Definition at line 166 of file eternalString.h.
|
inline |
Definition at line 167 of file eternalString.h.
|
inline |
Definition at line 128 of file eternalString.h.
|
friend |
Definition at line 208 of file eternalString.h.
|
friend |
Definition at line 205 of file eternalString.h.
|
friend |
Definition at line 202 of file eternalString.h.
|
friend |
Definition at line 183 of file eternalString.h.
|
friend |
Definition at line 180 of file eternalString.h.
|
friend |
Definition at line 186 of file eternalString.h.
|
friend |
Definition at line 199 of file eternalString.h.
|
friend |
Definition at line 196 of file eternalString.h.
|
friend |
Compare by address, which interning makes a content identity in both directions – see the class documentation.
Constant time regardless of length.
Definition at line 193 of file eternalString.h.
|
friend |
Hash by address, which interning makes a content identity – see the class documentation.
Constant time regardless of length.
As with TfToken, which hashes its rep pointer for the same reason, this means hash values vary between runs and do not agree with the hash of an equal std::string. Do not persist them.
Definition at line 219 of file eternalString.h.