format: allow %! specifier for all types
If we allow the %! specifier for all types it greatly simplifies handling of format strings in templated code, or code with non-trivial typedefs.
This commit is contained in:
parent
b7d141322c
commit
fad44bd1f7
38
format.ipp
38
format.ipp
@ -88,6 +88,11 @@ namespace util { namespace format { namespace detail {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
std::ostream&
|
||||||
|
operator<< (std::ostream &os, specifier::kind k);
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// provides the kind, a conversion specifier, and expected length for a
|
// provides the kind, a conversion specifier, and expected length for a
|
||||||
// given type.
|
// given type.
|
||||||
@ -609,7 +614,7 @@ namespace util { namespace format { namespace detail {
|
|||||||
if (spec.k == specifier::kind::POINTER)
|
if (spec.k == specifier::kind::POINTER)
|
||||||
return write (os, spec, reinterpret_cast<const void*> (t));
|
return write (os, spec, reinterpret_cast<const void*> (t));
|
||||||
|
|
||||||
if (spec.k != specifier::kind::STRING)
|
if (spec.k != specifier::kind::STRING && spec.k != specifier::kind::OSTREAM)
|
||||||
throw conversion_error ("invalid specifier kind for string argumetn");
|
throw conversion_error ("invalid specifier kind for string argumetn");
|
||||||
|
|
||||||
const auto len = spec.precision < 0 ? spec.precision :
|
const auto len = spec.precision < 0 ? spec.precision :
|
||||||
@ -651,10 +656,10 @@ namespace util { namespace format { namespace detail {
|
|||||||
//-------------------------------------------------------------------------
|
//-------------------------------------------------------------------------
|
||||||
template <typename OutputT>
|
template <typename OutputT>
|
||||||
OutputT
|
OutputT
|
||||||
write (OutputT os, const specifier s, const char t)
|
write (OutputT os, const specifier spec, const char t)
|
||||||
{
|
{
|
||||||
if (s.k != specifier::kind::CHARACTER)
|
if (spec.k != specifier::kind::CHARACTER && spec.k != specifier::kind::OSTREAM)
|
||||||
throw conversion_error ("invalid specifier kind for char argument");
|
throw conversion_error (render ("invalid specifier kind for char argument: %!", spec.k));
|
||||||
|
|
||||||
*os = t;
|
*os = t;
|
||||||
return ++os;
|
return ++os;
|
||||||
@ -692,7 +697,7 @@ namespace util { namespace format { namespace detail {
|
|||||||
>
|
>
|
||||||
write (OutputT &os, const specifier &spec, const T t)
|
write (OutputT &os, const specifier &spec, const T t)
|
||||||
{
|
{
|
||||||
if (spec.k != specifier::kind::POINTER)
|
if (spec.k != specifier::kind::POINTER && spec.k != specifier::kind::OSTREAM)
|
||||||
throw conversion_error ("invalid conversion specifier for pointer value");
|
throw conversion_error ("invalid conversion specifier for pointer value");
|
||||||
|
|
||||||
// glibc at least uses a special form for null pointers
|
// glibc at least uses a special form for null pointers
|
||||||
@ -730,15 +735,18 @@ namespace util { namespace format { namespace detail {
|
|||||||
>
|
>
|
||||||
write (OutputT os, const specifier spec, ValueT t)
|
write (OutputT os, const specifier spec, ValueT t)
|
||||||
{
|
{
|
||||||
if (spec.k == specifier::kind::POINTER && !t)
|
if (spec.k == specifier::kind::POINTER && !t) {
|
||||||
{
|
|
||||||
return write (os, spec, reinterpret_cast<void*> (t));
|
return write (os, spec, reinterpret_cast<void*> (t));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spec.k != (std::is_unsigned<ValueT>::value ? specifier::kind::UNSIGNED : specifier::kind::SIGNED))
|
if (!(spec.k == specifier::kind::UNSIGNED && std::is_unsigned<ValueT>::value ||
|
||||||
throw conversion_error ("invalid conversion specifier for integer value");
|
spec.k == specifier::kind::SIGNED && std::is_signed <ValueT>::value ||
|
||||||
|
spec.k == specifier::kind::OSTREAM))
|
||||||
|
{
|
||||||
|
throw conversion_error ("invalid conversion specifier for integer");
|
||||||
|
}
|
||||||
|
|
||||||
if (sizeof (ValueT) > spec.length)
|
if (sizeof (ValueT) > spec.length && spec.k != specifier::kind::OSTREAM)
|
||||||
throw length_error ("overlength value parameter");
|
throw length_error ("overlength value parameter");
|
||||||
|
|
||||||
const auto numerals = digits (t, spec.base);
|
const auto numerals = digits (t, spec.base);
|
||||||
@ -801,8 +809,13 @@ namespace util { namespace format { namespace detail {
|
|||||||
std::is_floating_point<T>::value,
|
std::is_floating_point<T>::value,
|
||||||
OutputT
|
OutputT
|
||||||
>
|
>
|
||||||
write (OutputT os, const specifier spec, T t)
|
write (OutputT os, specifier spec, T t)
|
||||||
{
|
{
|
||||||
|
if (spec.k == specifier::kind::OSTREAM) {
|
||||||
|
spec = specifier {};
|
||||||
|
spec.k = specifier::kind::REAL;
|
||||||
|
}
|
||||||
|
|
||||||
if (spec.k != specifier::kind::REAL)
|
if (spec.k != specifier::kind::REAL)
|
||||||
throw conversion_error ("invalid conversion specifier for real value");
|
throw conversion_error ("invalid conversion specifier for real value");
|
||||||
|
|
||||||
@ -820,7 +833,8 @@ namespace util { namespace format { namespace detail {
|
|||||||
if (spec.left_adjusted) *cursor++ = '-';
|
if (spec.left_adjusted) *cursor++ = '-';
|
||||||
if (spec.positive_char) *cursor++ = spec.positive_char;
|
if (spec.positive_char) *cursor++ = spec.positive_char;
|
||||||
|
|
||||||
cursor += sprintf (cursor, "%u", spec.width);
|
if (spec.width)
|
||||||
|
cursor += sprintf (cursor, "%u", spec.width);
|
||||||
|
|
||||||
if (spec.precision >= 0) {
|
if (spec.precision >= 0) {
|
||||||
*cursor++ = '.';
|
*cursor++ = '.';
|
||||||
|
@ -55,6 +55,7 @@ main (void)
|
|||||||
CHECK_RENDER ("%ju", "1", (uintmax_t)1);
|
CHECK_RENDER ("%ju", "1", (uintmax_t)1);
|
||||||
CHECK_RENDER ("%zu", "0", (size_t)0);
|
CHECK_RENDER ("%zu", "0", (size_t)0);
|
||||||
CHECK_RENDER ("%zu", "1", (size_t)1);
|
CHECK_RENDER ("%zu", "1", (size_t)1);
|
||||||
|
CHECK_RENDER ("%!", "1", 1u);
|
||||||
|
|
||||||
CHECK_RENDER ("%o", "1", 01u);
|
CHECK_RENDER ("%o", "1", 01u);
|
||||||
CHECK_RENDER ("%o", "13", 013u);
|
CHECK_RENDER ("%o", "13", 013u);
|
||||||
@ -122,7 +123,10 @@ main (void)
|
|||||||
CHECK_RENDER ("%3.2f", "1.23", 1.2345678);
|
CHECK_RENDER ("%3.2f", "1.23", 1.2345678);
|
||||||
CHECK_RENDER ("%3.2f", "1234.57", 1234.5678);
|
CHECK_RENDER ("%3.2f", "1234.57", 1234.5678);
|
||||||
|
|
||||||
|
CHECK_RENDER ("%!", "1", 1.);
|
||||||
|
|
||||||
CHECK_RENDER ("%c", "A", 'A');
|
CHECK_RENDER ("%c", "A", 'A');
|
||||||
|
CHECK_RENDER ("%!", "A", 'A');
|
||||||
|
|
||||||
CHECK_RENDER ("%s", "foo", "foo");
|
CHECK_RENDER ("%s", "foo", "foo");
|
||||||
CHECK_RENDER ("%s", "foo", std::string ("foo"));
|
CHECK_RENDER ("%s", "foo", std::string ("foo"));
|
||||||
@ -134,6 +138,8 @@ main (void)
|
|||||||
CHECK_RENDER ("%.64s", "foo", "foo");
|
CHECK_RENDER ("%.64s", "foo", "foo");
|
||||||
CHECK_RENDER ("%3.1s", " f", "foo");
|
CHECK_RENDER ("%3.1s", " f", "foo");
|
||||||
CHECK_RENDER ("%-3.1s", "f ", "foo");
|
CHECK_RENDER ("%-3.1s", "f ", "foo");
|
||||||
|
CHECK_RENDER ("%!", "foo", "foo");
|
||||||
|
|
||||||
CHECK_RENDER ("%!", "userobj", userobj {});
|
CHECK_RENDER ("%!", "userobj", userobj {});
|
||||||
|
|
||||||
CHECK_RENDER ("%p", "0x1234567", (void*)0x01234567);
|
CHECK_RENDER ("%p", "0x1234567", (void*)0x01234567);
|
||||||
@ -141,6 +147,7 @@ main (void)
|
|||||||
CHECK_RENDER ("%p", "0x1234567", (char*)0x01234567);
|
CHECK_RENDER ("%p", "0x1234567", (char*)0x01234567);
|
||||||
CHECK_RENDER ("%p", "(nil)", nullptr);
|
CHECK_RENDER ("%p", "(nil)", nullptr);
|
||||||
CHECK_RENDER ("%p", "(nil)", NULL);
|
CHECK_RENDER ("%p", "(nil)", NULL);
|
||||||
|
CHECK_RENDER ("%!", "0x1234567", (void*)0x01234567);
|
||||||
|
|
||||||
CHECK_RENDER ("%%", "%");
|
CHECK_RENDER ("%%", "%");
|
||||||
CHECK_RENDER ("%10%", "%");
|
CHECK_RENDER ("%10%", "%");
|
||||||
@ -202,6 +209,4 @@ main (void)
|
|||||||
|
|
||||||
CHECK_THROW("%c", conversion_error, 1u);
|
CHECK_THROW("%c", conversion_error, 1u);
|
||||||
CHECK_THROW("%c", conversion_error, "foo");
|
CHECK_THROW("%c", conversion_error, "foo");
|
||||||
|
|
||||||
CHECK_THROW("%!", conversion_error, 1u);
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user