Do not require string_t to be convertible from std::string

Three places built a std::string and handed it to something expecting a
string_t: the UBJSON high-precision number reader, which every binary reader
instantiates, and the BSON writer's array element size calculation and write.
That silently required string_t to be implicitly convertible from std::string,
which std::string itself and types with a string_view conversion satisfy, but
many string types do not.

Construct the string_t explicitly from the data and size, which the
requirements already cover. This makes boost::container::string, eastl::string,
std::pmr::string, and std::basic_string with a custom allocator work as
StringType, none of which could previously be used with any binary format.

Add binary format coverage to the alt_string test, which had none, including a
UBJSON high-precision number -- the case that goes through the reader path.
BSON stays uncovered there: it additionally needs string_t::find(value_type),
which alt_string does not provide.

Also record which containers from Boost, Abseil, and EASTL work for each
template parameter, and correct two claims: std::pmr::string is usable after
this change, and tsl::ordered_map is not usable at all, because its iterators
expose the mapped value as const while basic_json modifies it in place.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann
2026-08-28 17:38:56 +00:00
parent 5d93f35463
commit d386e0aa52
7 changed files with 87 additions and 14 deletions
+11 -3
View File
@@ -13531,7 +13531,10 @@ class binary_reader
number_string,
out_of_range::create(406, concat("number overflow parsing '", number_string, '\''), nullptr));
}
return sax->number_float(parsed_float, std::move(number_string));
// number_string is a std::string, while the SAX interface takes a
// string_t; convert explicitly, as the two are only implicitly
// convertible for some string types
return sax->number_float(parsed_float, string_t(number_string.data(), number_string.size()));
}
case token_type::uninitialized:
case token_type::literal_true:
@@ -18138,7 +18141,8 @@ class binary_writer
const std::size_t embedded_document_size = std::accumulate(std::begin(value), std::end(value), static_cast<std::size_t>(0), [&array_index](std::size_t result, const typename BasicJsonType::array_t::value_type & el)
{
return result + calc_bson_element_size(std::to_string(array_index++), el);
const auto key = std::to_string(array_index++);
return result + calc_bson_element_size(string_t(key.data(), key.size()), el);
});
return sizeof(std::int32_t) + embedded_document_size + 1ul;
@@ -18165,7 +18169,11 @@ class binary_writer
for (const auto& el : value)
{
write_bson_element(std::to_string(array_index++), el);
// the index is built as a std::string, while write_bson_element takes
// a string_t; convert explicitly, as the two are only implicitly
// convertible for some string types
const auto key = std::to_string(array_index++);
write_bson_element(string_t(key.data(), key.size()), el);
}
oa->write_character(to_char_type(0x00));