Unify float marker selection and fix the long double compile error

Four formats picked between a float32 and float64 marker through four
different helper styles: dummy-argument overloads for CBOR and
MessagePack, an std::is_same template for BON8, and a runtime if-chain
on input_format_t for write_compact_float(). With number_float_t set
to long double, to_cbor, to_msgpack and to_ubjson failed inside the
library with "call to 'get_cbor_float_prefix' is ambiguous", while
to_bson kept working because write_bson_double() takes a plain double.

Change write_compact_float() to take the two marker bytes directly
(each of its three callers already knows them at compile time) instead
of an input_format_t it only forwarded, and delete the now-unused
get_cbor_float_prefix(), get_msgpack_float_prefix(),
get_bon8_float_prefix() and get_compact_float_prefix() helpers. Turn
the two get_ubjson_float_prefix() overloads into one template. Both
write_compact_float() and get_ubjson_float_prefix() now report an
unsupported number_float_t with a static_assert naming the requirement,
rather than an ambiguous-overload error; the assert lives in the
function body, not the class scope, so to_bson with long double is
unaffected.

Verified with a probe basic_json<..., long double>: to_bson still
compiles and round-trips, while to_cbor/to_msgpack/to_ubjson now fail
to compile with the new static_assert message.

This changes the text of an existing compile error for users with an
unsupported number_float_t (documented as a public-API-visible change
in #5710).

#5710 item 1

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann
2026-09-30 18:18:42 +02:00
parent 35a3b0f3de
commit edd18d071e
2 changed files with 48 additions and 118 deletions
@@ -204,7 +204,7 @@ class binary_writer
}
else
{
write_compact_float(j.m_data.m_value.number_float, detail::input_format_t::cbor);
write_compact_float(j.m_data.m_value.number_float, to_char_type(0xFA), to_char_type(0xFB));
}
break;
}
@@ -428,7 +428,7 @@ class binary_writer
case value_t::number_float:
{
write_compact_float(j.m_data.m_value.number_float, detail::input_format_t::msgpack);
write_compact_float(j.m_data.m_value.number_float, to_char_type(0xCA), to_char_type(0xCB));
break;
}
@@ -1410,52 +1410,6 @@ class binary_writer
}
}
static constexpr CharType get_cbor_float_prefix(float /*unused*/)
{
return to_char_type(0xFA); // Single-Precision Float
}
static constexpr CharType get_cbor_float_prefix(double /*unused*/)
{
return to_char_type(0xFB); // Double-Precision Float
}
/////////////
// MsgPack //
/////////////
static constexpr CharType get_msgpack_float_prefix(float /*unused*/)
{
return to_char_type(0xCA); // float 32
}
static constexpr CharType get_msgpack_float_prefix(double /*unused*/)
{
return to_char_type(0xCB); // float 64
}
/// @return the BON8 type marker for binary32 (float) or binary64 (double)
template<typename FloatType>
static constexpr CharType get_bon8_float_prefix()
{
return to_char_type(std::is_same<FloatType, float>::value ? 0x8E : 0x8F);
}
/// @return the type marker for a FloatType value in @a format (CBOR, MessagePack, or BON8)
template<typename FloatType>
static CharType get_compact_float_prefix(const detail::input_format_t format)
{
if (format == detail::input_format_t::cbor)
{
return get_cbor_float_prefix(FloatType{});
}
if (format == detail::input_format_t::bon8)
{
return get_bon8_float_prefix<FloatType>();
}
return get_msgpack_float_prefix(FloatType{});
}
////////////
// UBJSON //
////////////
@@ -1645,14 +1599,16 @@ class binary_writer
|| marker == 'T' || marker == 'F' || marker == 'N' || marker == 'Z';
}
static constexpr CharType get_ubjson_float_prefix(float /*unused*/)
/// @return the UBJSON/BJData type marker for a float or double value
///
/// number_float_t must be float or double; a static_assert (rather than
/// an ambiguous overload) reports an unsupported number_float_t clearly.
template<typename FloatType>
static constexpr CharType get_ubjson_float_prefix(FloatType /*unused*/)
{
return 'd'; // float 32
}
static constexpr CharType get_ubjson_float_prefix(double /*unused*/)
{
return 'D'; // float 64
static_assert(std::is_same<FloatType, float>::value || std::is_same<FloatType, double>::value,
"number_float_t must be float or double for the UBJSON/BJData writer");
return std::is_same<FloatType, float>::value ? 'd' : 'D'; // float 32 / float 64
}
/*!
@@ -2240,7 +2196,7 @@ class binary_writer
}
else
{
write_compact_float(n, detail::input_format_t::bon8);
write_compact_float(n, to_char_type(0x8E), to_char_type(0x8F));
}
#ifdef __GNUC__
JSON_HEDLEY_DIAGNOSTIC_POP
@@ -2362,8 +2318,17 @@ class binary_writer
oa.write_characters(vec.data(), sizeof(NumberType));
}
void write_compact_float(const number_float_t n, detail::input_format_t format)
/// @brief write @a n using @a float32_marker if it round-trips through
/// float, otherwise using @a float64_marker
///
/// @a float32_marker and @a float64_marker are the format-specific type
/// markers (CBOR: 0xFA/0xFB, MessagePack: 0xCA/0xCB, BON8: 0x8E/0x8F);
/// each caller already knows them at compile time, so the format itself
/// no longer needs to be passed in.
void write_compact_float(const number_float_t n, const CharType float32_marker, const CharType float64_marker)
{
static_assert(std::is_same<number_float_t, float>::value || std::is_same<number_float_t, double>::value,
"number_float_t must be float or double for the CBOR/MessagePack/BON8 writer");
#ifdef __GNUC__
JSON_HEDLEY_DIAGNOSTIC_PUSH
JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal")
@@ -2381,12 +2346,12 @@ class binary_writer
static_cast<double>(n) <= static_cast<double>((std::numeric_limits<float>::max)()) &&
static_cast<double>(static_cast<float>(n)) == static_cast<double>(n))))
{
oa.write_character(get_compact_float_prefix<float>(format));
oa.write_character(float32_marker);
write_number(static_cast<float>(n));
}
else
{
oa.write_character(get_compact_float_prefix<number_float_t>(format));
oa.write_character(float64_marker);
write_number(n);
}
#ifdef __GNUC__
+24 -59
View File
@@ -20497,7 +20497,7 @@ class binary_writer
}
else
{
write_compact_float(j.m_data.m_value.number_float, detail::input_format_t::cbor);
write_compact_float(j.m_data.m_value.number_float, to_char_type(0xFA), to_char_type(0xFB));
}
break;
}
@@ -20721,7 +20721,7 @@ class binary_writer
case value_t::number_float:
{
write_compact_float(j.m_data.m_value.number_float, detail::input_format_t::msgpack);
write_compact_float(j.m_data.m_value.number_float, to_char_type(0xCA), to_char_type(0xCB));
break;
}
@@ -21703,52 +21703,6 @@ class binary_writer
}
}
static constexpr CharType get_cbor_float_prefix(float /*unused*/)
{
return to_char_type(0xFA); // Single-Precision Float
}
static constexpr CharType get_cbor_float_prefix(double /*unused*/)
{
return to_char_type(0xFB); // Double-Precision Float
}
/////////////
// MsgPack //
/////////////
static constexpr CharType get_msgpack_float_prefix(float /*unused*/)
{
return to_char_type(0xCA); // float 32
}
static constexpr CharType get_msgpack_float_prefix(double /*unused*/)
{
return to_char_type(0xCB); // float 64
}
/// @return the BON8 type marker for binary32 (float) or binary64 (double)
template<typename FloatType>
static constexpr CharType get_bon8_float_prefix()
{
return to_char_type(std::is_same<FloatType, float>::value ? 0x8E : 0x8F);
}
/// @return the type marker for a FloatType value in @a format (CBOR, MessagePack, or BON8)
template<typename FloatType>
static CharType get_compact_float_prefix(const detail::input_format_t format)
{
if (format == detail::input_format_t::cbor)
{
return get_cbor_float_prefix(FloatType{});
}
if (format == detail::input_format_t::bon8)
{
return get_bon8_float_prefix<FloatType>();
}
return get_msgpack_float_prefix(FloatType{});
}
////////////
// UBJSON //
////////////
@@ -21938,14 +21892,16 @@ class binary_writer
|| marker == 'T' || marker == 'F' || marker == 'N' || marker == 'Z';
}
static constexpr CharType get_ubjson_float_prefix(float /*unused*/)
/// @return the UBJSON/BJData type marker for a float or double value
///
/// number_float_t must be float or double; a static_assert (rather than
/// an ambiguous overload) reports an unsupported number_float_t clearly.
template<typename FloatType>
static constexpr CharType get_ubjson_float_prefix(FloatType /*unused*/)
{
return 'd'; // float 32
}
static constexpr CharType get_ubjson_float_prefix(double /*unused*/)
{
return 'D'; // float 64
static_assert(std::is_same<FloatType, float>::value || std::is_same<FloatType, double>::value,
"number_float_t must be float or double for the UBJSON/BJData writer");
return std::is_same<FloatType, float>::value ? 'd' : 'D'; // float 32 / float 64
}
/*!
@@ -22533,7 +22489,7 @@ class binary_writer
}
else
{
write_compact_float(n, detail::input_format_t::bon8);
write_compact_float(n, to_char_type(0x8E), to_char_type(0x8F));
}
#ifdef __GNUC__
JSON_HEDLEY_DIAGNOSTIC_POP
@@ -22655,8 +22611,17 @@ class binary_writer
oa.write_characters(vec.data(), sizeof(NumberType));
}
void write_compact_float(const number_float_t n, detail::input_format_t format)
/// @brief write @a n using @a float32_marker if it round-trips through
/// float, otherwise using @a float64_marker
///
/// @a float32_marker and @a float64_marker are the format-specific type
/// markers (CBOR: 0xFA/0xFB, MessagePack: 0xCA/0xCB, BON8: 0x8E/0x8F);
/// each caller already knows them at compile time, so the format itself
/// no longer needs to be passed in.
void write_compact_float(const number_float_t n, const CharType float32_marker, const CharType float64_marker)
{
static_assert(std::is_same<number_float_t, float>::value || std::is_same<number_float_t, double>::value,
"number_float_t must be float or double for the CBOR/MessagePack/BON8 writer");
#ifdef __GNUC__
JSON_HEDLEY_DIAGNOSTIC_PUSH
JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal")
@@ -22674,12 +22639,12 @@ class binary_writer
static_cast<double>(n) <= static_cast<double>((std::numeric_limits<float>::max)()) &&
static_cast<double>(static_cast<float>(n)) == static_cast<double>(n))))
{
oa.write_character(get_compact_float_prefix<float>(format));
oa.write_character(float32_marker);
write_number(static_cast<float>(n));
}
else
{
oa.write_character(get_compact_float_prefix<number_float_t>(format));
oa.write_character(float64_marker);
write_number(n);
}
#ifdef __GNUC__