Strings and UTF-8

StringType and StringViewType

Every string in the API is UTF-8. RealtimeCore::Common::StringType is an alias for std::u8string, StringViewType for std::u8string_view and CharType for char8_t, plain std::string never appears in a signature.

The aliases are defined in RealtimeCore/Common/StringType.h in the RealtimeCore::Common namespace and are used consistently across the whole API surface. Every string parameter, return value and data-type field, room names, player names, property keys, error messages, is a StringType or a StringViewType.

Building on char8_t makes the encoding part of the type system: a StringType is UTF-8 by construction, on every platform and on the wire. This removes the classic ambiguity of std::string, whose encoding depends on platform and locale, and turns accidental mixing of encodings into a compile error instead of corrupted text.

Writing String Literals

Use the PHOTON_STR("...") macro for string literals. It expands to a u8"..." literal, so it produces char8_t text that converts directly to StringType and StringViewType. Writing u8"..." yourself is equivalent, but discuraged as your code won't break if you use the PHOTON_STR("...") macro if there are internal changes on our string data types.

Literals are used wherever the API takes a string.

C++
C

C++

using namespace RealtimeCore::Matchmaking;

ClientConstructOptions options;
options.AppId = PHOTON_STR("your-app-id");

RealtimeClient client(options);
client.SetPlayerName(PHOTON_STR("Alice"));

C

/* Every string on the ABI is a plain, null-terminated UTF-8 const char*. */
RealtimeHandle client = realtime_create("your-app-id", "1.0", queue);

realtime_set_player_name(client, "Alice");

/* There is no StringType, no PHOTON_STR and no char8_t over the C API: an
   ordinary string literal is already the right type, as long as the source
   file is saved as UTF-8. The encoding guarantee moves from the type
   system to a convention, so validate text that comes from a file, a
   socket or a platform SDK before passing it in. */

Converting To and From Other String Types

RealtimeCore::Common::ToStringType(...) builds a StringType from the common sources: any integral value, a bool (formatted as True or False) and a raw CharType* pointer. It is handy when composing property values or log messages from numeric game state.

C++20 makes char8_t a distinct character type, so a UTF-8 std::string and a StringType do not convert into each other implicitly even when their bytes are identical. Interfacing with code that stores UTF-8 in std::string therefore needs an explicit conversion at the boundary. The conversion is a plain byte copy and is lossless in both directions, because both sides hold the same UTF-8 data. It is important to make sure that any std::string you convert is encoded as UTF-8 (or ASCII which is a subset of UTF-8).

Both directions are a byte-wise copy.

C++
C

C++

// std::string (holding UTF-8) to StringType:
std::string utf8Name = "Alice";
RealtimeCore::Common::StringType photonName(utf8Name.begin(), utf8Name.end());

// StringType back to std::string, e.g. for display or serialization:
std::string display(reinterpret_cast<const char*>(photonName.data()), photonName.size());

C

/* Nothing to convert on the way in: a UTF-8 char* is the ABI's string type. */
const char* utf8Name = "Alice";
realtime_set_player_name(client, utf8Name);

/* On the way out, strings from a blob are length-prefixed and NOT terminated,
   so copy them into a buffer to get a C string. */
int32_t     nameLen = 0;
const char* name    = rt_get_str(&reader, &nameLen);

char display[64];
if (nameLen > (int32_t)sizeof(display) - 1)
{
    nameLen = (int32_t)sizeof(display) - 1;
}
memcpy(display, name, (size_t)nameLen);
display[nameLen] = '\0';

/* Or print in place, without the copy: */
printf("%.*s\n", (int)nameLen, name);

/* Text that a realtime_* function writes into a caller buffer, such as
   realtime_room_name or realtime_user_id, arrives null-terminated and
   needs neither the copy nor the precision. Blob strings are the ones that
   do not, because their length prefix is the only terminator they have. */
Back to top