format: reimplement format rendering

requires literal string arrays, and implements more of the specifier
specification. does not implement 'n' or '$' specifiers. falls back to
snprintf for real arguments.
This commit is contained in:
Danny Robson 2016-07-28 13:36:23 +10:00
parent f9fb0873d3
commit 8cc4c1e82a
28 changed files with 1301 additions and 410 deletions

View File

@ -286,8 +286,9 @@
///////////////////////////////////////////////////////////////////////////////
constexpr void panic [[noreturn]] (const char*);
template <typename ...Args>
constexpr void panic [[noreturn]] (const char *fmt, const Args&...);
template <typename ...Args, size_t N>
constexpr
void panic [[noreturn]] (const char (&fmt)[N], const Args&...);
///////////////////////////////////////////////////////////////////////////////

View File

@ -28,9 +28,13 @@
namespace util { namespace debug { namespace detail {
void panic [[noreturn]] (const char *msg);
template <typename ...Args>
void panic [[noreturn]] (const char *msg, const Args& ...args)
{ panic (util::format::render (msg, args...).c_str ()); }
template <typename ...Args, size_t N>
constexpr
void panic [[noreturn]] (const char (&fmt)[N], const Args& ...args)
{
auto msg = util::format::render (fmt, args...);
panic (msg.c_str ());
}
void not_implemented [[noreturn]] (const char *msg);
void unreachable [[noreturn]] (const char *msg);
@ -92,10 +96,10 @@ constexpr void panic [[noreturn]] (const char *msg)
//-----------------------------------------------------------------------------
template <typename ...Args>
template <typename ...Args, size_t N>
constexpr
void
panic [[noreturn]] (const char *fmt, const Args& ...args)
panic [[noreturn]] (const char (&fmt)[N], const Args& ...args)
{
! fmt
? panic ("unreachable constexpr panic helper")

View File

@ -17,3 +17,59 @@
#include "format.hpp"
#include <utility>
namespace util { namespace format { namespace detail {
//-------------------------------------------------------------------------
std::ostream&
operator<< (std::ostream &os, specifier::repr r)
{
switch (r) {
case specifier::repr::FIXED: return os << "FIXED";
case specifier::repr::SCIENTIFIC: return os << "SCIENTIFIC";
case specifier::repr::AUTO: return os << "AUTO";
}
unreachable ();
}
//-------------------------------------------------------------------------
std::ostream&
operator<< (std::ostream &os, specifier::kind t)
{
switch (t) {
case specifier::kind::UNSIGNED: return os << "UNSIGNED";
case specifier::kind::SIGNED: return os << "SIGNED";
case specifier::kind::REAL: return os << "REAL";
case specifier::kind::STRING: return os << "STRING";
case specifier::kind::POINTER: return os << "POINTER";
case specifier::kind::CHARACTER: return os << "CHARACTER";
case specifier::kind::ESCAPE: return os << "ESCAPE";
case specifier::kind::OSTREAM: return os << "OSTREAM";
}
unreachable ();
}
//-------------------------------------------------------------------------
std::ostream&
operator<< (std::ostream &os, const specifier &s)
{
return os << "specifier {"
"alternate_form: " << s.alternate_form << ", "
"left_adjusted: " << s.left_adjusted << ", "
"thousands_grouping: " << s.thousands_grouping << ", "
"padding_char: '" << s.padding_char << "', "
"positive_char: '" << s.positive_char << "', "
"uppercase: " << s.uppercase << ", "
"base: " << s.base << ", "
"repr: " << s.r << ", "
"kind: " << s.k << ", "
"width: " << s.width << ", "
"precision: " << s.precision << ", "
"length: " << s.length <<
" }";
}
} } }

View File

@ -11,7 +11,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright 2015-2016 Danny Robson <danny@nerdcruft.net>
* Copyright 2016 Danny Robson <danny@nerdcruft.net>
*/
#ifndef __UTIL_FORMAT_HPP
@ -20,12 +20,17 @@
#include <stdexcept>
#include <string>
namespace util {
namespace format {
template <typename ...Args>
namespace util { namespace format {
//-------------------------------------------------------------------------
// render a format string using the provided values.
//
// we deliberately only take char[] formats so as to promote the use of
// only literal strings as format strings.
template <typename ...Args, size_t N>
std::string
render (const std::string &fmt, Args&&...);
render (const char (&fmt)[N], const Args&...);
//-------------------------------------------------------------------------
class error : public std::runtime_error
{ using runtime_error::runtime_error; };
@ -33,12 +38,18 @@ namespace util {
class value_error : public error
{ using error::error; };
struct conversion_error : public error
{ using error::error; };
struct length_error : public error
{ using error::error; };
// malformed format specifier
class format_error : public error
class syntax_error : public error
{ using error::error; };
template <typename ValueT>
class invalid_specifier : public format_error {
class invalid_specifier : error {
public:
using value_type = ValueT;
@ -52,9 +63,13 @@ namespace util {
// missing format specifier
class missing_error : public error
{ using error::error; };
}
}
{
public:
missing_error ():
error ("missing argument for specifier")
{ ; }
};
} }
#include "format.ipp"

1051
format.ipp

File diff suppressed because it is too large Load Diff

View File

@ -61,8 +61,8 @@ namespace util {
///////////////////////////////////////////////////////////////////////////
void log (level_t, const std::string &msg);
template <typename ...tail>
void log (level_t, const std::string &format, tail&& ..._tail);
template <typename ...Args, size_t N>
void log (level_t, const char (&fmt)[N], const Args&...);
//-------------------------------------------------------------------------

View File

@ -24,11 +24,11 @@
//-----------------------------------------------------------------------------
namespace util {
template <typename ...tail>
template <typename ...Args, size_t N>
void
log (level_t l, const std::string &format, tail&& ..._tail)
log (level_t l, const char (&fmt)[N], const Args& ...args)
{
log (l, format::render (format, std::forward<tail> (_tail)...));
log (l, format::render (fmt, args...));
}
}

View File

@ -57,26 +57,6 @@ template uint32_t util::log2 (uint32_t);
template uint64_t util::log2 (uint64_t);
///////////////////////////////////////////////////////////////////////////////
namespace util {
template <>
unsigned
digits (const uint32_t &v)
{
return (v >= 1000000000) ? 10 :
(v >= 100000000) ? 9 :
(v >= 10000000) ? 8 :
(v >= 1000000) ? 7 :
(v >= 100000) ? 6 :
(v >= 10000) ? 5 :
(v >= 1000) ? 4 :
(v >= 100) ? 3 :
(v >= 10) ? 2 :
1;
}
}
///////////////////////////////////////////////////////////////////////////////
template <typename T>
std::enable_if_t<

View File

@ -17,10 +17,14 @@
#ifndef __MATHS_HPP
#define __MATHS_HPP
#include "./debug.hpp"
// DO NOT INCLUDE debug.hpp
// it triggers a circular dependency; debug -> format -> maths -> debug
// instead, just use cassert
#include "./types/traits.hpp"
#include "./float.hpp"
#include <cassert>
#include <cmath>
#include <cstdint>
#include <limits>
@ -281,9 +285,40 @@ namespace util {
//-----------------------------------------------------------------------------
template <typename T>
constexpr
unsigned
digits (const T& value);
digits10 (uint32_t v) noexcept
{
return (v >= 1000000000) ? 10 :
(v >= 100000000) ? 9 :
(v >= 10000000) ? 8 :
(v >= 1000000) ? 7 :
(v >= 100000) ? 6 :
(v >= 10000) ? 5 :
(v >= 1000) ? 4 :
(v >= 100) ? 3 :
(v >= 10) ? 2 :
1;
}
template <typename ValueT, typename BaseT>
constexpr
std::enable_if_t<
std::is_integral<ValueT>::value && std::is_unsigned<BaseT>::value,
unsigned
>
digits (ValueT value, BaseT base) noexcept
{
if (value < 0)
return digits (-value, base);
unsigned tally = 1;
while (value /= base)
++tally;
return tally;
}
///----------------------------------------------------------------------------
@ -326,8 +361,8 @@ namespace util {
constexpr T
gcd (T a, T b)
{
CHECK_NEZ (a);
CHECK_NEZ (b);
assert (a);
assert (b);
while (a != b) {
if (a > b)
@ -506,7 +541,7 @@ namespace util {
constexpr T
limit (const T val, const U lo, const V hi)
{
CHECK_LE (lo, hi);
assert (lo <= hi);
return val > hi ? hi:
val < lo ? lo:
@ -520,7 +555,7 @@ namespace util {
T
smoothstep (T a, T b, T x)
{
CHECK_LE(a, b);
assert (a <= b);
x = limit ((x - a) / (b - a), T{0}, T{1});
return x * x * (3 - 2 * x);
}

44
tap.hpp
View File

@ -37,42 +37,42 @@ namespace util { namespace TAP {
~logger ();
//---------------------------------------------------------------------
template <typename ...Args>
void expect (bool, const std::string &fmt, Args&&...);
template <typename ...Args, size_t N>
void expect (bool, const char (&fmt)[N], Args&&...);
template <typename ...Args>
void expect (const std::function<bool(Args...)>&, Args&&..., const std::string& msg);
template <typename ...Args, size_t N>
void expect (const std::function<bool(Args...)>&, Args&&..., const char (&msg)[N]);
//---------------------------------------------------------------------
template <typename T, typename U, typename ...Args>
void expect_eq (const T&, const U&, const std::string &fmt, Args&&...);
template <typename T, typename U, typename ...Args, size_t N>
void expect_eq (const T&, const U&, const char (&fmt)[N], Args&&...);
template <typename T, typename U, typename ...Args>
void expect_neq (const T&, const U&, const std::string &fmt, Args&&...);
template <typename T, typename U, typename ...Args, size_t N>
void expect_neq (const T&, const U&, const char (&fmt)[N], Args&&...);
//---------------------------------------------------------------------
template <typename T, typename U, typename ...Args>
void expect_gt (const T&, const U&, const std::string &fmt, Args&&...);
template <typename T, typename U, typename ...Args, size_t N>
void expect_gt (const T&, const U&, const char (&fmt)[N], Args&&...);
template <typename T, typename U, typename ...Args>
void expect_ge (const T&, const U&, const std::string &fmt, Args&&...);
template <typename T, typename U, typename ...Args, size_t N>
void expect_ge (const T&, const U&, const char (&fmt)[N], Args&&...);
template <typename T, typename U, typename ...Args>
void expect_lt (const T&, const U&, const std::string &fmt, Args&&...);
template <typename T, typename U, typename ...Args, size_t N>
void expect_lt (const T&, const U&, const char (&fmt)[N], Args&&...);
template <typename T, typename U, typename ...Args>
void expect_le (const T&, const U&, const std::string &fmt, Args&&...);
template <typename T, typename U, typename ...Args, size_t N>
void expect_le (const T&, const U&, const char (&fmt)[N], Args&&...);
//---------------------------------------------------------------------
template <typename T, typename ...Args>
void expect_nan (const T&, const std::string &fmt, Args&&...);
template <typename T, typename ...Args, size_t N>
void expect_nan (const T&, const char (&fmt)[N], Args&&...);
//---------------------------------------------------------------------
template <typename T, typename ...Args>
void expect_nothrow (T&&, const std::string &fmt, Args&&...);
template <typename T, typename ...Args, size_t N>
void expect_nothrow (T&&, const char (&fmt)[N], Args&&...);
template <typename E, typename T, typename ...Args>
void expect_throw (T&&, const std::string &fmt, Args&&...);
template <typename E, typename T, typename ...Args, size_t N>
void expect_throw (T&&, const char (&fmt)[N], Args&&...);
//---------------------------------------------------------------------
void skip (const std::string &msg);

84
tap.ipp
View File

@ -29,9 +29,9 @@
///////////////////////////////////////////////////////////////////////////////
template <typename ...Args>
template <typename ...Args, size_t N>
void
util::TAP::logger::expect (bool test, const std::string &fmt, Args&&... args)
util::TAP::logger::expect (bool test, const char (&fmt)[N], Args&&... args)
{
std::cout << (test ? "ok " : "not ok ") << ++m_size
<< " - "
@ -43,61 +43,42 @@ util::TAP::logger::expect (bool test, const std::string &fmt, Args&&... args)
//-----------------------------------------------------------------------------
template <typename... Args>
template <typename ...Args, size_t N>
void
util::TAP::logger::expect (const std::function<bool(Args...)> &test, Args&&... args, const std::string &msg)
util::TAP::logger::expect (const std::function<bool(Args...)> &test, Args&&... args, const char (&fmt)[N])
{
expect (test (std::forward<Args> (args)...), msg);
expect (test (std::forward<Args> (args)...), fmt);
}
///////////////////////////////////////////////////////////////////////////////
template <typename T, typename U, typename ...Args, size_t N>
void
util::TAP::logger::expect_eq (const T&a, const U &b, const char (&fmt)[N], Args&&... args)
{
expect (almost_equal (a, b), fmt, std::forward<Args> (args)...);
}
//-----------------------------------------------------------------------------
template <typename T, typename U, typename ...Args>
template <typename T, typename U, typename ...Args, size_t N>
void
util::TAP::logger::expect_eq (const T&a, const U &b, const std::string &fmt, Args&&... args)
util::TAP::logger::expect_neq (const T&a, const U &b, const char (&fmt)[N], Args&&... args)
{
static const std::function<bool(const T&,const U&)> TEST = [] (const T &t, const U &u) -> bool {
return almost_equal (t, u);
};
expect<const T&, const U&> (TEST, a, b, util::format::render (fmt, std::forward<Args> (args)...));
expect (!almost_equal (a, b), fmt, std::forward<Args> (args)...);
}
//-----------------------------------------------------------------------------
template <typename T, typename U, typename ...Args>
void
util::TAP::logger::expect_neq (const T&a, const U &b, const std::string &fmt, Args&&... args)
{
static const std::function<bool(const T&,const U&)> TEST = [] (const T &t, const U &u) -> bool {
return !almost_equal (t, u);
};
expect<const T&, const U&> (TEST, a, b, util::format::render (fmt, std::forward<Args> (args)...));
}
//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
#define TAP_TEST(SUFFIX,OP) \
template <typename T, typename U, typename ...Args> \
template <typename T, typename U, typename ...Args, size_t N> \
void \
util::TAP::logger::expect_ ## SUFFIX (const T &a, \
const U &b, \
const std::string &fmt, \
const char (&fmt)[N], \
Args&&... args) \
{ \
static const std::function< \
bool(const T&,const U&) \
> TEST = [] (const T&t, const U&u) { return t OP u; }; \
\
expect<const T&, const U&> ( \
TEST, \
a, b, \
util::format::render ( \
fmt, \
std::forward<Args> (args)... \
) \
); \
expect ((a) OP (b), fmt, std::forward<Args> (args)...); \
}
TAP_TEST(gt, > )
@ -109,23 +90,18 @@ TAP_TEST(le, <=)
//-----------------------------------------------------------------------------
template <typename T, typename ...Args>
template <typename T, typename ...Args, size_t N>
void
util::TAP::logger::expect_nan (const T &t, const std::string &fmt, Args&&... args)
util::TAP::logger::expect_nan (const T &t, const char (&fmt)[N], Args&&... args)
{
bool(*func)(T) = std::isnan;
expect<const T&> (
std::function<bool(const T&)> (func),
t,
util::format::render (fmt, std::forward<Args> (args)...)
);
expect (std::isnan (t), fmt, std::forward<Args> (args)...);
}
//-----------------------------------------------------------------------------
template <typename T, typename ...Args>
template <typename T, typename ...Args, size_t N>
void
util::TAP::logger::expect_nothrow (T &&t, const std::string &fmt, Args&&... args)
util::TAP::logger::expect_nothrow (T &&t, const char (&fmt)[N], Args&&... args)
{
bool success = true;
@ -135,14 +111,14 @@ util::TAP::logger::expect_nothrow (T &&t, const std::string &fmt, Args&&... args
success = false;
}
expect (success, util::format::render (fmt, std::forward<Args> (args)...));
expect (success, fmt, std::forward<Args> (args)...);
}
//-----------------------------------------------------------------------------
template <typename E, typename T, typename ...Args>
template <typename E, typename T, typename ...Args, size_t N>
void
util::TAP::logger::expect_throw (T &&t, const std::string &fmt, Args&&... args)
util::TAP::logger::expect_throw (T &&t, const char (&fmt)[N], Args&&... args)
{
bool success = false;
@ -154,5 +130,5 @@ util::TAP::logger::expect_throw (T &&t, const std::string &fmt, Args&&... args)
success = false;
}
expect (success, util::format::render (fmt, std::forward<Args> (args)...));
expect (success, fmt, std::forward<Args> (args)...);
}

View File

@ -86,13 +86,13 @@ test_bool (util::TAP::logger &tap)
for (auto i: positive) {
argv[2] = i;
p.scan (argv.size (), argv.data ());
tap.expect_eq (value, true, i, "read bool, %s", i);
tap.expect_eq (value, true, "read bool, %s", i);
}
for (auto i: negative) {
argv[2] = i;
p.scan (argv.size (), argv.data ());
tap.expect_eq (value, false, i, "read bool, %s", i);
tap.expect_eq (value, false, "read bool, %s", i);
}
// Check that invalid forms of boolean all throw exceptions

View File

@ -45,8 +45,8 @@ main (int, char**)
};
for (auto i: TESTS) {
tap.expect_eq (util::rgb_to_hsv (i.rgb), i.hsv, i.name);
tap.expect_eq (util::hsv_to_rgb (i.hsv), i.rgb, i.name);
tap.expect_eq (util::rgb_to_hsv (i.rgb), i.hsv, "rgb-to-hsv %s", i.name);
tap.expect_eq (util::hsv_to_rgb (i.hsv), i.rgb, "hsv-to-rgb %s", i.name);
}
}
}

View File

@ -2,6 +2,7 @@
#include "tap.hpp"
#include "types.hpp"
int
main ()
{
@ -384,8 +385,6 @@ main ()
success = success && std::equal (std::begin (data), std::end (data), std::begin (t.data[j]));
};
std::ostringstream os;
os << "ARC4: " << i;
tap.expect (success, os.str ());
tap.expect (success, "ARC4 %zu", i);
}
}

View File

@ -50,17 +50,8 @@ main ()
std::array<uint32_t,2> dec (t.enc);
gen.decrypt (dec.data (), dec.size ());
{
std::ostringstream os;
os << "TEA_enc " << i;
tap.expect (enc == t.enc, os.str ());
}
{
std::ostringstream os;
os << "TEA_dec " << i;
tap.expect (dec == t.dec, os.str ());
}
tap.expect (enc == t.enc, "TEA_enc %zu", i);
tap.expect (dec == t.dec, "TEA_dec %zu", i);
}
return tap.status ();

View File

@ -49,17 +49,8 @@ main ()
std::array<uint32_t,2> dec (t.enc);
gen.decrypt (dec.data (), dec.size ());
{
std::ostringstream os;
os << "XTEA_enc " << i;
tap.expect (enc == t.enc, os.str ());
}
{
std::ostringstream os;
os << "XTEA_dec " << i;
tap.expect (dec == t.dec, os.str ());
}
tap.expect (enc == t.enc, "XTEA_enc %zu", i);
tap.expect (dec == t.dec, "XTEA_dec %zu", i);
}
return tap.status ();

View File

@ -104,17 +104,8 @@ main ()
std::vector<uint32_t> dec (enc);
gen.decrypt (dec.data (), dec.size ());
{
std::ostringstream os;
os << "XXTEA_enc " << i;
tap.expect (enc == t.enc, os.str ());
}
{
std::ostringstream os;
os << "XXTEA_dec " << i;
tap.expect (dec == t.dec, os.str ());
}
tap.expect (enc == t.enc, "XXTEA_enc %zu", i);
tap.expect (dec == t.dec, "XXTEA_dec %zu", i);
}
return tap.status ();

View File

@ -16,19 +16,19 @@ test_simple (util::TAP::logger &tap)
std::ostringstream os;
os << "fixed<" << type_to_string<T> () << ',' << I << ',' << E << '>';
tap.expect_eq (lo, lo, os.str () + " self equality");
tap.expect_eq (hi, hi, os.str () + " self equality");
tap.expect_eq (lo, lo, "%s self equality", os.str ());
tap.expect_eq (hi, hi, "%s self equality", os.str ());
tap.expect_neq (hi, lo, os.str () + " inequality");
tap.expect_neq (lo, hi, os.str () + " inequality");
tap.expect_neq (hi, lo, "%s inequality", os.str ());
tap.expect_neq (lo, hi, "%s inequality", os.str ());
tap.expect_lt (lo, hi, os.str () + " less than");
tap.expect_le (lo, hi, os.str () + " less than equal");
tap.expect_le (lo, lo, os.str () + " less than equal");
tap.expect_lt (lo, hi, "%s less than", os.str ());
tap.expect_le (lo, hi, "%s less than equal", os.str ());
tap.expect_le (lo, lo, "%s less than equal", os.str ());
tap.expect_gt (hi, lo, os.str () + " greater than");
tap.expect_ge (lo, lo, os.str () + " greater than equal");
tap.expect_ge (hi, lo, os.str () + " greater than equal");
tap.expect_gt (hi, lo, "%s greater than", os.str ());
tap.expect_ge (lo, lo, "%s greater than equal", os.str ());
tap.expect_ge (hi, lo, "%s greater than equal", os.str ());
}

View File

@ -5,25 +5,186 @@
int
main (void)
{
using namespace std::string_literals;
util::TAP::logger tap;
tap.expect_eq (util::format::render ("identity"), "identity"s, "identity literal");
tap.expect_eq (util::format::render ("%s", "identity"s), "identity"s, "identity string substitution");
tap.expect_eq (util::format::render ("%s", "identity" ), "identity"s, "identity char[] substitution");
#define CHECK_RENDER(fmt,res,...) do { \
auto val = util::format::render (fmt, ##__VA_ARGS__); \
tap.expect_eq (val, res, "render '%s'", fmt); \
} while (0)
tap.expect_throw<util::format::missing_error> ([] (void) {
util::format::render ("%s");
}, "missing value");
CHECK_RENDER ("foo", "foo");
tap.expect_throw<util::format::invalid_specifier<int>> ([] (void) {
util::format::render ("%<", 42);
}, "invalid specifier");
CHECK_RENDER ("%i", "1", 1 );
CHECK_RENDER ("%3i", " 1", 1 );
CHECK_RENDER ("%03i", "001", 1 );
CHECK_RENDER ("%.i", "", 0);
CHECK_RENDER ("% .i", " ", 0); // zero precision still requires a space
tap.expect_throw<util::format::format_error> ([] (void) {
util::format::render ("%", 42);
}, "truncated specifier");
CHECK_RENDER ("%hhi", "1", (signed char)1);
CHECK_RENDER ("%hi", "1", (signed short)1);
CHECK_RENDER ("%li", "1", (signed long)1);
CHECK_RENDER ("%lli", "1", (signed long long)1);
CHECK_RENDER ("%ji", "1", (intmax_t)1);
CHECK_RENDER ("%zi", "1", (ssize_t)1);
CHECK_RENDER ("%ti", "1", (ptrdiff_t)1);
return tap.status ();
CHECK_RENDER ("%u", "1", 1u);
CHECK_RENDER ("%03u", "001", 1u);
CHECK_RENDER ("% u", " 1", 1u);
CHECK_RENDER ("% 3u", " 1", 1u);
CHECK_RENDER ("% 03u", " 01", 1u);
CHECK_RENDER ("%-3u", "1 ", 1u);
CHECK_RENDER ("%64u", " 1", 1u);
CHECK_RENDER ("%hhu", "1", (unsigned char)1);
CHECK_RENDER ("%hu", "1", (unsigned short)1);
CHECK_RENDER ("%lu", "1", (unsigned long)1);
CHECK_RENDER ("%llu", "1", (unsigned long long)1);
CHECK_RENDER ("%ju", "1", (uintmax_t)1);
CHECK_RENDER ("%zu", "1", (size_t)1);
CHECK_RENDER ("%o", "1", 01u);
CHECK_RENDER ("%o", "13", 013u);
CHECK_RENDER ("%o", "13", 013u);
CHECK_RENDER ("%#o", "013", 013u);
CHECK_RENDER ("%x", "1", 0x1u);
CHECK_RENDER ("%x", "fe", 0xfeu);
CHECK_RENDER ("%X", "FE", 0xFEu);
CHECK_RENDER ("%#x", "0xfe", 0xfeu);
CHECK_RENDER ("%#X", "0XFE", 0xFEu);
CHECK_RENDER ("%e", "1.000000e+00", 1.);
CHECK_RENDER ("%e", "1.200000e+00", 1.2);
CHECK_RENDER ("%e", "1.234568e+00", 1.2345678);
CHECK_RENDER ("%E", "1.234568E+00", 1.2345678);
CHECK_RENDER ("%f", "1.000000", 1.);
CHECK_RENDER ("%f", "1.200000", 1.2);
CHECK_RENDER ("%f", "1.234560", 1.23456);
CHECK_RENDER ("%f", "1.234567", 1.234567);
CHECK_RENDER ("%f", "1.234568", 1.2345678);
CHECK_RENDER ("%g", "1", 1.);
CHECK_RENDER ("%g", "1.2", 1.2);
CHECK_RENDER ("%g", "1.23457", 1.2345678);
CHECK_RENDER ("%g", "0.000123457", 0.00012345678);
CHECK_RENDER ("%g", "1.23457e-05", 0.000012345678);
CHECK_RENDER ("%G", "1.23457E-05", 0.000012345678);
CHECK_RENDER ("%+e", "+1.234568e+00", 1.2345678);
CHECK_RENDER ("%+f", "+1.234568", 1.2345678);
CHECK_RENDER ("%+g", "+1.23457", 1.2345678);
CHECK_RENDER ("%+a", "+0x1.3c0ca2a5b1d5dp+0", 1.2345678);
CHECK_RENDER ("%#.e", "1.e+00", 1.2345678);
CHECK_RENDER ("%#.f", "1.", 1.2345678);
CHECK_RENDER ("%#.g", "1.", 1.2345678);
//CHECK_RENDER ("%#.a", "0x1.p+0", 1.2345678);
CHECK_RENDER ("%a", "0x1.3c0ca2a5b1d5dp+0", 1.2345678);
CHECK_RENDER ("%A", "0X1.3C0CA2A5B1D5DP+0", 1.2345678);
CHECK_RENDER ("%e", "inf", std::numeric_limits<double>::infinity ());
CHECK_RENDER ("%E", "INF", std::numeric_limits<double>::infinity ());
CHECK_RENDER ("%f", "inf", std::numeric_limits<double>::infinity ());
CHECK_RENDER ("%F", "INF", std::numeric_limits<double>::infinity ());
CHECK_RENDER ("%g", "inf", std::numeric_limits<double>::infinity ());
CHECK_RENDER ("%G", "INF", std::numeric_limits<double>::infinity ());
CHECK_RENDER ("%a", "inf", std::numeric_limits<double>::infinity ());
CHECK_RENDER ("%A", "INF", std::numeric_limits<double>::infinity ());
CHECK_RENDER ("%e", "nan", std::numeric_limits<double>::quiet_NaN ());
CHECK_RENDER ("%E", "NAN", std::numeric_limits<double>::quiet_NaN ());
CHECK_RENDER ("%f", "nan", std::numeric_limits<double>::quiet_NaN ());
CHECK_RENDER ("%F", "NAN", std::numeric_limits<double>::quiet_NaN ());
CHECK_RENDER ("%g", "nan", std::numeric_limits<double>::quiet_NaN ());
CHECK_RENDER ("%G", "NAN", std::numeric_limits<double>::quiet_NaN ());
CHECK_RENDER ("%a", "nan", std::numeric_limits<double>::quiet_NaN ());
CHECK_RENDER ("%A", "NAN", std::numeric_limits<double>::quiet_NaN ());
CHECK_RENDER ("%.f", "1", 1.2345678);
CHECK_RENDER ("%3.f", " 1", 1.2345678);
CHECK_RENDER ("%3.2f", "1.23", 1.2345678);
CHECK_RENDER ("%3.2f", "1234.57", 1234.5678);
CHECK_RENDER ("%c", "A", 'A');
CHECK_RENDER ("%s", "foo", "foo");
CHECK_RENDER ("%.s", "", "foo");
CHECK_RENDER ("%.0s", "", "foo");
CHECK_RENDER ("%.2s", "fo", "foo");
CHECK_RENDER ("%.02s", "fo", "foo");
CHECK_RENDER ("%.64s", "foo", "foo");
CHECK_RENDER ("%3.1s", " f", "foo");
CHECK_RENDER ("%-3.1s", "f ", "foo");
CHECK_RENDER ("%p", "0x1234567", (void*)0x01234567);
CHECK_RENDER ("%p", "0x1234567", (int*)0x01234567);
CHECK_RENDER ("%p", "(nil)", nullptr);
CHECK_RENDER ("%p", "(nil)", NULL);
CHECK_RENDER ("%%", "%");
CHECK_RENDER ("%10%", "%");
CHECK_RENDER ("%.%", "%");
CHECK_RENDER ("% 0-+#12.34%", "%"); // escaped conversions should largely ignore flags, width, and precision.
// multiple components
CHECK_RENDER ("%%%%", "%%");
CHECK_RENDER (" %%", " %");
CHECK_RENDER ("%% ", "% ");
CHECK_RENDER ("%% %%", "% %");
CHECK_RENDER (" %% %% ", " % % ");
CHECK_RENDER ("%u %u", "1 2", 1u, 2u);
CHECK_RENDER ("%#o %o", "010 10", 8u, 8u);
CHECK_RENDER ("%#o %o %#o", "010 10 010", 8u, 8u, 8u);
CHECK_RENDER ("%X%x%X", "FfF", 0xfu, 0xfu, 0xfu);
tap.expect_eq (util::format::render ("%u\n", 1u), "1\n", "newline");
#define CHECK_THROW(fmt,except,...) do { \
tap.expect_throw<util::format::except> ([&] { \
util::format::render (fmt, ##__VA_ARGS__); \
}, "exception '%s' for format '%s'", #except, fmt); \
} while (0)
CHECK_THROW("%", syntax_error);
CHECK_THROW("%_", syntax_error);
CHECK_THROW("%_u", syntax_error);
CHECK_THROW("%u", missing_error);
CHECK_THROW("%!", missing_error);
CHECK_THROW("%d", conversion_error, 1u);
CHECK_THROW("%i", conversion_error, 1u);
CHECK_THROW("%i", conversion_error, nullptr);
CHECK_THROW("%hhi", length_error, (long long)1);
CHECK_THROW("%lli", length_error, (signed char)1);
CHECK_THROW("%u", conversion_error, 1.);
CHECK_THROW("%u", conversion_error, "foo");
CHECK_THROW("%u", conversion_error, (void*)0);
CHECK_THROW("%u", conversion_error, 1);
CHECK_THROW("%u", conversion_error, nullptr);
CHECK_THROW("%hhu", length_error, (unsigned long long)1);
CHECK_THROW("%llu", length_error, (unsigned char)1);
CHECK_THROW("%f", conversion_error, 1u);
CHECK_THROW("%f", conversion_error, "foo");
CHECK_THROW("%f", conversion_error, nullptr);
CHECK_THROW("%s", conversion_error, 1u);
CHECK_THROW("%s", conversion_error, '_');
CHECK_THROW("%s", conversion_error, nullptr);
CHECK_THROW("%c", conversion_error, 1u);
CHECK_THROW("%c", conversion_error, "foo");
CHECK_THROW("%!", conversion_error, 1u);
}

View File

@ -274,7 +274,7 @@ main (int, char**)
util::TAP::logger tap;
for (size_t i = 0; i < elems (TESTS); ++i)
tap.expect (TESTS[i].fun (TESTS[i].key, TESTS[i].dat, TESTS[i].res), "standard test vector %u", i);
tap.expect (TESTS[i].fun (TESTS[i].key, TESTS[i].dat, TESTS[i].res), "standard test vector %zu", i);
return tap.status ();
}

View File

@ -116,7 +116,7 @@ main(int, char**) {
obj.update (reinterpret_cast<const uint8_t*> (i.data), strlen (i.data));
obj.finish ();
tap.expect_eq (obj.digest (), i.output, i.msg);
tap.expect_eq (obj.digest (), i.output, "%s", i.msg);
}
// Perform 'million-a' check

View File

@ -66,7 +66,7 @@ main (int, char**)
obj.update (reinterpret_cast<const uint8_t*> (i.input), strlen (i.input));
obj.finish ();
tap.expect_eq (obj.digest (), i.output, i.msg);
tap.expect_eq (obj.digest (), i.output, "%s", i.msg);
}
return tap.status ();

View File

@ -55,7 +55,7 @@ main (int, char **) {
obj.update (reinterpret_cast<const uint8_t*> (i.input), strlen (i.input));
obj.finish ();
tap.expect_eq (obj.digest (), i.output, i.msg);
tap.expect_eq (obj.digest (), i.output, "%s", i.msg);
}
return tap.status ();

View File

@ -23,9 +23,8 @@ test_good (util::TAP::logger &tap)
{ "127.0.0.1", { 127, 0, 0, 1 }, "localhost" }
};
for (const auto &i: TESTS) {
tap.expect_eq (ipv4::ip::parse (i.str), i.ip, i.msg);
}
for (const auto &i: TESTS)
tap.expect_eq (ipv4::ip::parse (i.str), i.ip, "%s", i.msg);
}
@ -43,9 +42,8 @@ test_bad (util::TAP::logger &tap)
{ "256.0.0.1", "overflow" }
};
for (const auto &i: TESTS) {
tap.expect_throw<ipv4::error> ([&] { ipv4::ip::parse (i.str); }, i.msg);
}
for (const auto &i: TESTS)
tap.expect_throw<ipv4::error> ([&] { ipv4::ip::parse (i.str); }, "%s", i.msg);
}

View File

@ -52,7 +52,7 @@ main (int, char**)
ok = false;
}
test.expect (ok, i.name);
test.expect (ok, "%s", i.name);
}
return 0;

View File

@ -33,7 +33,7 @@ main (void)
for (const auto &t: TESTS) {
constexpr float TOLERANCE = 0.00001f;
auto root = util::roots::bisection (t.lo, t.hi, t.func, TOLERANCE);
tap.expect_eq (root, t.root, t.msg);
tap.expect_eq (root, t.root, "%s", t.msg);
}
return tap.status ();

View File

@ -47,7 +47,7 @@ test_polar (util::TAP::logger &tap)
auto in_cart = t.cartesian;
auto to_cart = util::polar_to_cartesian (t.polar);
tap.expect_lt ((in_cart - to_cart).magnitude (), 0.00001f, t.desc);
tap.expect_lt ((in_cart - to_cart).magnitude (), 0.00001f, "%s", t.desc);
// Compare polar representations. Make sure to normalise them first.
auto in_polar = t.polar;
@ -56,7 +56,7 @@ test_polar (util::TAP::logger &tap)
in_polar[1] = std::fmod (in_polar[1], 2 * util::PI<float>);
to_polar[1] = std::fmod (to_polar[1], 2 * util::PI<float>);
tap.expect_eq (in_polar, to_polar, t.desc);
tap.expect_eq (in_polar, to_polar, "%s", t.desc);
}
}

View File

@ -52,7 +52,7 @@ main () {
for (const auto &i: PARSE_TESTS) {
util::version v (i.str);
tap.expect (std::equal (v.begin (), v.end (), i.parts) && v.release == i.release, i.msg);
tap.expect (std::equal (v.begin (), v.end (), i.parts) && v.release == i.release, "%s", i.msg);
}