omni::string is an ABI-safe, DLL-Boundary-safe string class intended to be used in Omniverse Native Interfaces that provides all of the
std::string. The layout of
std::is_standard_layout and will be the same
across all compilers. Using
carb.dll/.so because of the
Memory functions used, which are provided by the Carbonite Framework.
Strings are frequently used in Carbonite and Omniverse interfaces, however there is not an ABI safe string
implementation available for interfaces to use. This leads to
size parameters being used, which are both
error prone and cumbersome to use. For example:
// This creates a URL from the pieces provided. // "bufferSize" is an in-out parameter; set it to the size of the buffer before calling this function // and it will be set to the actual size when the function returns. If the size required is more // than the size provided, this function returns null, otherwise it returns 'buffer'. char* omniClientMakeUrl(struct OmniClientUrl const* url, char* buffer, size_t* bufferSize);
This interface requires a confusing combination of
size_t* parameters. It requires users to preallocate a
character array which may or may not be large enough, and then may returns a
nullptr, which could lead to program
crashes if users don’t check the return value. With
omni::string, this interface could be simplified to:
// This creates a URL from the pieces provided. omni::string omniClientMakeUrl(struct OmniClientUrl const* url);
omni::string, there’s no more dealing with buffer sizes or returning
nullptr which may or may not actually
Why not std::string?
Why go through the trouble of implementing and maintaining our own string class instead of using
There are two major reasons that
std::string is not suitable for use in Omniverse interfaces. First,
not ABI safe. The ABI of
std::string was broken in C++11, and is not guaranteed to not be broken again in the future.
std::string is not safe to pass across DLL boundaries due to memory allocation. A
std::string that is
created with allocated storage in one module should not be passed to another module which may free the memory.
Small String Optimization
omni::string provides small string optimization (SSO), so that an allocation is not required for small string. In
omni::string, strings up to 31 characters long will be stored in the string’s internal buffer rather than in
allocated storage. Note that
omni::string is still only 32 bytes in size, so user’s do not have to pay a penalty in
stack space to get this optimization. Profiling typical workflows indicates that close to 50% of strings used in Kit
will be able to take advantage of being small string optimized.
Unlike the Standard Library which provides a templated
std::basic_string<CharT, Traits, Allocator> class and then
omni::string has no template arguments. This was done primarily for ABI safety and complexity
concerns. Omniverse only uses UTF-8 characters, so the
CharT template option was removed because
char is the only
type that should be used with
omni::string. Similarly then, there was no need for a
Traits type. Finally, to
maintain DLL boundary safety,
omni::string uses the allocation methods provided by Memory,
Allocator template parameter was also omitted.
A template parameter for changing the size of the SSO buffer was also originally considered, however it was ultimately rejected. This parameter would allow for the ABI of an interface to be broken simply by changing that template parameter, which could lead to difficult to diagnose issues. This parameter also led to more complex implementation, and it was decided that the benefits did not outweigh the costs.
omni::string provides an interface as close to that of
std::string as possible. All member functions and non-member
functions provided by
std::string up to C++20 are available for
omni::string. This should make it easy and familiar
to use. There are a few subtle differences however.
First, in C++17, an overload was added to most functions that took a generic
T parameter and implicitly converted it
std::string_view, and performed the operation with that
omni::string does not currently
implement these overloads because Carbonite headers must be compatible with C++14, which does not have the
std::string_view type. These functions can be added in the future.
Second, most functions in
constexpr in C++20.
omni::string makes an effort to make as many
functions as possible
constexpr, but it cannot match
std::string in this regard.
std::string takes advantage of
C++20 features that allow transient allocations via
operator new to be
constexpr, which allows any
method that may grow the string, and thus trigger a reallocation, to be
omni::string uses the
Memory management functions, which are not
constexpr, it is unable to make functions
that may grow the string
omni::string is only intended to be used in C++, and therefore will not have Python bindings generated for the class.
Python users should continue to use Python’s native strings. Omni.bind will be extended to automatically convert Python
omni::string for interfaces that use
omni::string and have Python bindings. This will allow Python users
to seamlessly interact with interfaces using
omni::string without requiring bindings for the