mirror of
https://github.com/ETLCPP/etl.git
synced 2026-10-28 18:07:10 +08:00
Fix formatting of int8_t and uint8_t (#1551)
Fixes https://github.com/ETLCPP/etl/issues/1549
This commit is contained in:
parent
7ee5a661b6
commit
cc91f2ed78
@ -91,44 +91,70 @@ namespace etl
|
||||
POINTER // const void*
|
||||
};
|
||||
|
||||
// Map a type to its category. Decays and removes cv-qualifiers.
|
||||
// Trait mapping a type (with references and cv-qualifiers removed) to its type_category.
|
||||
//
|
||||
// NOTE: signed char/unsigned char (typically int8_t/uint8_t) are NOT
|
||||
// categorised as CHAR here - only plain 'char' gets character semantics.
|
||||
// signed/unsigned char are formatted as integers, matching basic_format_arg
|
||||
// storage and std::format's formatter<signed char>/<unsigned char>. This is
|
||||
// enforced structurally: the INTEGER partial specialisation is constrained
|
||||
// with is_integral<T> && !is_same<T, bool> && !is_same<T, char>, so
|
||||
// signed/unsigned char (is_integral == true) fall into INTEGER while the
|
||||
// dedicated bool/char full specialisations take priority for those exact types.
|
||||
template <class T, class = void>
|
||||
struct type_category_trait
|
||||
{
|
||||
static constexpr type_category value = type_category::NONE; // unknown type: custom formatter, be permissive
|
||||
};
|
||||
|
||||
template <>
|
||||
struct type_category_trait<bool>
|
||||
{
|
||||
static constexpr type_category value = type_category::BOOLEAN;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct type_category_trait<char>
|
||||
{
|
||||
static constexpr type_category value = type_category::CHAR;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct type_category_trait<
|
||||
T, typename etl::enable_if<etl::is_integral<T>::value && !etl::is_same<T, bool>::value && !etl::is_same<T, char>::value>::type>
|
||||
{
|
||||
static constexpr type_category value = type_category::INTEGER;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct type_category_trait<T, typename etl::enable_if<etl::is_floating_point<T>::value>::type>
|
||||
{
|
||||
static constexpr type_category value = type_category::FLOAT;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct type_category_trait<T,
|
||||
typename etl::enable_if<etl::is_same<T, char*>::value || etl::is_same<T, const char*>::value
|
||||
|| etl::is_same<T, etl::string_view>::value || etl::is_base_of<etl::istring, T>::value>::type>
|
||||
{
|
||||
static constexpr type_category value = type_category::STRING;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct type_category_trait<
|
||||
T, typename etl::enable_if<etl::is_pointer<T>::value && !etl::is_same<T, char*>::value && !etl::is_same<T, const char*>::value>::type>
|
||||
{
|
||||
static constexpr type_category value = type_category::POINTER;
|
||||
};
|
||||
|
||||
// Map a type to its category. Removes references and cv-qualifiers, then defers
|
||||
// entirely to the type_category_trait specialisations above.
|
||||
template <class T>
|
||||
constexpr type_category get_type_category()
|
||||
{
|
||||
using U = typename etl::remove_cv<typename etl::remove_reference<T>::type>::type;
|
||||
|
||||
// Order matters: bool before integral, char before integral
|
||||
if (etl::is_same<U, bool>::value)
|
||||
return type_category::BOOLEAN;
|
||||
if (etl::is_same<U, char>::value)
|
||||
return type_category::CHAR;
|
||||
if (etl::is_same<U, signed char>::value)
|
||||
return type_category::CHAR;
|
||||
if (etl::is_same<U, unsigned char>::value)
|
||||
return type_category::CHAR;
|
||||
if (etl::is_integral<U>::value)
|
||||
return type_category::INTEGER;
|
||||
if (etl::is_same<U, float>::value)
|
||||
return type_category::FLOAT;
|
||||
if (etl::is_same<U, double>::value)
|
||||
return type_category::FLOAT;
|
||||
if (etl::is_same<U, long double>::value)
|
||||
return type_category::FLOAT;
|
||||
if (etl::is_same<U, const char*>::value)
|
||||
return type_category::STRING;
|
||||
if (etl::is_same<U, char*>::value)
|
||||
return type_category::STRING;
|
||||
if (etl::is_same<U, etl::string_view>::value)
|
||||
return type_category::STRING;
|
||||
if (etl::is_base_of<etl::istring, U>::value)
|
||||
return type_category::STRING;
|
||||
if (etl::is_pointer<U>::value)
|
||||
return type_category::POINTER;
|
||||
if (etl::is_same<U, const void*>::value)
|
||||
return type_category::POINTER;
|
||||
if (etl::is_same<U, void*>::value)
|
||||
return type_category::POINTER;
|
||||
return type_category::NONE; // unknown type: custom formatter, be permissive
|
||||
return type_category_trait<U>::value;
|
||||
}
|
||||
|
||||
// Check if a format type character is valid for a given type category.
|
||||
@ -727,13 +753,16 @@ namespace etl
|
||||
{
|
||||
}
|
||||
|
||||
// int8_t / uint8_t are typically defined as signed char / unsigned char, but
|
||||
// (unlike plain char) they are formatted as integers by default, matching
|
||||
// std::format's formatter<signed char>/formatter<unsigned char> behaviour.
|
||||
basic_format_arg(const signed char v)
|
||||
: data(static_cast<char>(v))
|
||||
: data(static_cast<int>(v))
|
||||
{
|
||||
}
|
||||
|
||||
basic_format_arg(const unsigned char v)
|
||||
: data(static_cast<char>(v))
|
||||
: data(static_cast<unsigned int>(v))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@ -218,10 +218,9 @@ namespace
|
||||
{
|
||||
etl::string<100> s;
|
||||
|
||||
// mapped to unsigned char
|
||||
// CHECK_EQUAL("34", test_format(s, "{}", static_cast<uint8_t>(34)));
|
||||
// mapped to signed char
|
||||
// CHECK_EQUAL("-14", test_format(s, "{}", static_cast<int8_t>(-14)));
|
||||
// int8_t/uint8_t are formatted as integers (issue #1549), not as characters
|
||||
CHECK_EQUAL("34", test_format(s, "{}", static_cast<uint8_t>(34)));
|
||||
CHECK_EQUAL("-14", test_format(s, "{}", static_cast<int8_t>(-14)));
|
||||
CHECK_EQUAL("6534", test_format(s, "{}", static_cast<uint16_t>(6534)));
|
||||
CHECK_EQUAL("-9414", test_format(s, "{}", static_cast<int16_t>(-9414)));
|
||||
CHECK_EQUAL("236534", test_format(s, "{}", static_cast<uint32_t>(236534)));
|
||||
@ -619,45 +618,35 @@ namespace
|
||||
//*************************************************************************
|
||||
TEST(test_format_signed_char)
|
||||
{
|
||||
// signed char (typically int8_t) is formatted as an integer by default
|
||||
// (issue #1549), matching std::format. Only {:c} converts it to a character.
|
||||
// '?' (debug-escape) is NOT a valid presentation type for signed char.
|
||||
etl::string<100> s;
|
||||
|
||||
CHECK_EQUAL("a s b", test_format(s, "a {} b", static_cast<signed char>('s')));
|
||||
CHECK_EQUAL("a 115 b", test_format(s, "a {} b", static_cast<signed char>('s')));
|
||||
CHECK_EQUAL("a s b", test_format(s, "a {:c} b", static_cast<signed char>('s')));
|
||||
CHECK_EQUAL("a 's' b", test_format(s, "a {:?} b", static_cast<signed char>('s')));
|
||||
CHECK_EQUAL("a \t b", test_format(s, "a {} b", static_cast<signed char>('\t')));
|
||||
CHECK_EQUAL("a '\\t' b", test_format(s, "a {:?} b", static_cast<signed char>('\t')));
|
||||
CHECK_EQUAL("a '\\n' b", test_format(s, "a {:?} b", static_cast<signed char>('\n')));
|
||||
CHECK_EQUAL("a '\\r' b", test_format(s, "a {:?} b", static_cast<signed char>('\r')));
|
||||
CHECK_EQUAL("a '\\\"' b", test_format(s, "a {:?} b", static_cast<signed char>('"')));
|
||||
CHECK_EQUAL("a '\\'' b", test_format(s, "a {:?} b", static_cast<signed char>('\'')));
|
||||
CHECK_EQUAL("a '\\\\' b", test_format(s, "a {:?} b", static_cast<signed char>('\\')));
|
||||
CHECK_EQUAL("a '\\\\' b", test_format(s, "a {:?} b", static_cast<signed char>('\\')));
|
||||
CHECK_EQUAL("a 97 b", test_format(s, "a {:d} b", static_cast<signed char>('a')));
|
||||
CHECK_EQUAL("a 61 b", test_format(s, "a {:X} b", static_cast<signed char>('a')));
|
||||
CHECK_EQUAL("a 61 b", test_format(s, "a {:x} b", static_cast<signed char>('a')));
|
||||
CHECK_EQUAL("a 0x61 b", test_format(s, "a {:#x} b", static_cast<signed char>('a')));
|
||||
CHECK_EQUAL("a -12 b", test_format(s, "a {} b", static_cast<signed char>(-12)));
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_format_unsigned_char)
|
||||
{
|
||||
// unsigned char (typically uint8_t) is formatted as an integer by default
|
||||
// (issue #1549), matching std::format. Only {:c} converts it to a character.
|
||||
// '?' (debug-escape) is NOT a valid presentation type for unsigned char.
|
||||
etl::string<100> s;
|
||||
|
||||
CHECK_EQUAL("a s b", test_format(s, "a {} b", static_cast<unsigned char>('s')));
|
||||
CHECK_EQUAL("a 115 b", test_format(s, "a {} b", static_cast<unsigned char>('s')));
|
||||
CHECK_EQUAL("a s b", test_format(s, "a {:c} b", static_cast<unsigned char>('s')));
|
||||
CHECK_EQUAL("a 's' b", test_format(s, "a {:?} b", static_cast<unsigned char>('s')));
|
||||
CHECK_EQUAL("a \t b", test_format(s, "a {} b", static_cast<unsigned char>('\t')));
|
||||
CHECK_EQUAL("a '\\t' b", test_format(s, "a {:?} b", static_cast<unsigned char>('\t')));
|
||||
CHECK_EQUAL("a '\\n' b", test_format(s, "a {:?} b", static_cast<unsigned char>('\n')));
|
||||
CHECK_EQUAL("a '\\r' b", test_format(s, "a {:?} b", static_cast<unsigned char>('\r')));
|
||||
CHECK_EQUAL("a '\\\"' b", test_format(s, "a {:?} b", static_cast<unsigned char>('"')));
|
||||
CHECK_EQUAL("a '\\'' b", test_format(s, "a {:?} b", static_cast<unsigned char>('\'')));
|
||||
CHECK_EQUAL("a '\\\\' b", test_format(s, "a {:?} b", static_cast<unsigned char>('\\')));
|
||||
CHECK_EQUAL("a '\\\\' b", test_format(s, "a {:?} b", static_cast<unsigned char>('\\')));
|
||||
CHECK_EQUAL("a 97 b", test_format(s, "a {:d} b", static_cast<unsigned char>('a')));
|
||||
CHECK_EQUAL("a 61 b", test_format(s, "a {:X} b", static_cast<unsigned char>('a')));
|
||||
CHECK_EQUAL("a 61 b", test_format(s, "a {:x} b", static_cast<unsigned char>('a')));
|
||||
CHECK_EQUAL("a 0x61 b", test_format(s, "a {:#x} b", static_cast<unsigned char>('a')));
|
||||
CHECK_EQUAL("a 65 b", test_format(s, "a {} b", static_cast<unsigned char>(65)));
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user