format: remove in favour of libfmt
This commit is contained in:
parent
a94cd677bd
commit
462776dafa
@ -29,7 +29,6 @@ endif()
|
||||
###############################################################################
|
||||
RAGEL_TARGET(uri uri.cpp.rl ${CMAKE_CURRENT_BINARY_DIR}/uri.cpp COMPILE_FLAGS -G2)
|
||||
RAGEL_TARGET(version version.cpp.rl ${CMAKE_CURRENT_BINARY_DIR}/version.cpp)
|
||||
RAGEL_TARGET(format.cpp format.cpp.rl ${CMAKE_CURRENT_BINARY_DIR}/format.cpp)
|
||||
RAGEL_TARGET(parse8601 time/parse8601.cpp.rl ${CMAKE_CURRENT_BINARY_DIR}/time/parse8601.cpp)
|
||||
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
@ -337,8 +336,6 @@ list (
|
||||
fixed_string.hpp
|
||||
float.cpp
|
||||
float.hpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/format.cpp
|
||||
format.hpp
|
||||
fourcc.cpp
|
||||
fourcc.hpp
|
||||
functor.hpp
|
||||
@ -703,7 +700,6 @@ if (TESTS)
|
||||
extent
|
||||
fixed
|
||||
float
|
||||
format
|
||||
geom/aabb
|
||||
geom/ellipse
|
||||
geom/frustum
|
||||
|
@ -10,9 +10,10 @@
|
||||
|
||||
#include "endian.hpp"
|
||||
#include "view.hpp"
|
||||
#include "format.hpp"
|
||||
#include "bitwise.hpp"
|
||||
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <ostream>
|
||||
|
||||
@ -158,10 +159,11 @@ cruft::cpu::operator<< (std::ostream &os, const x86 &val)
|
||||
};
|
||||
};
|
||||
|
||||
return os << cruft::format::printf (
|
||||
"{ name: { vendor: '%!', product: '%!' }"
|
||||
", cores: { logical: %!, physical: %!, hyper_threading: %! }"
|
||||
", simd: { sse: %!, sse2: %!, sse3: %!, ssse3: %!, sse41: %!, sse42: %!, avx: %! }"
|
||||
fmt::print (
|
||||
os,
|
||||
"{ name: { vendor: '{}', product: '{}' }"
|
||||
", cores: { logical: {}, physical: {}, hyper_threading: {} }"
|
||||
", simd: { sse: {}, sse2: {}, sse3: {}, ssse3: {}, sse41: {}, sse42: {}, avx: {} }"
|
||||
" }",
|
||||
to_string (val.vendor_name),
|
||||
to_string (val.product_name),
|
||||
@ -176,4 +178,6 @@ cruft::cpu::operator<< (std::ostream &os, const x86 &val)
|
||||
(val.simd.sse42 ? "true" : "false"),
|
||||
(val.simd.avx ? "true" : "false")
|
||||
);
|
||||
|
||||
return os;
|
||||
}
|
||||
|
@ -8,9 +8,10 @@
|
||||
|
||||
#include "./system.hpp"
|
||||
|
||||
#include "debugger.hpp"
|
||||
#include "except.hpp"
|
||||
#include "crash.hpp"
|
||||
#include "./assert.hpp"
|
||||
#include "./debugger.hpp"
|
||||
#include "./except.hpp"
|
||||
#include "./crash.hpp"
|
||||
|
||||
#include "../backtrace.hpp"
|
||||
#include "../log.hpp"
|
||||
@ -44,14 +45,14 @@ static void abort_with_trace (void)
|
||||
try {
|
||||
std::rethrow_exception (ptr);
|
||||
} catch (std::exception const &x) {
|
||||
LOG_EMERGENCY ("unhandled exception: %!\n%!", x.what (), ::cruft::backtrace {});
|
||||
LOG_EMERGENCY ("unhandled exception: {}\n{}", x.what (), ::cruft::backtrace {});
|
||||
} catch (cruft::error const &x) {
|
||||
LOG_EMERGENCY ("unhandled exception: %!\n%!", x, ::cruft::backtrace {});
|
||||
LOG_EMERGENCY ("unhandled exception: {}\n{}", x, ::cruft::backtrace {});
|
||||
} catch (...) {
|
||||
LOG_EMERGENCY ("unhandled exception\n%!", ::cruft::backtrace {});
|
||||
LOG_EMERGENCY ("unhandled exception\n{}", ::cruft::backtrace {});
|
||||
}
|
||||
} else {
|
||||
LOG_EMERGENCY ("aborting: %!", ::cruft::backtrace {});
|
||||
LOG_EMERGENCY ("aborting: {}", ::cruft::backtrace {});
|
||||
}
|
||||
|
||||
old_handler ();
|
||||
|
@ -31,6 +31,6 @@ warn (const std::string &msg)
|
||||
void
|
||||
warn (const char *msg)
|
||||
{
|
||||
LOG_WARN (msg);
|
||||
LOG_WARN ("{}", msg);
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,7 @@ prepare_debugger (void)
|
||||
// as possible.
|
||||
if (nullptr == LoadLibrary("exchndl.dll")) {
|
||||
auto code = GetLastError ();
|
||||
LOG_WARNING("Emergency debugger not loaded, %s", cruft::win32::error::code_string (code));
|
||||
LOG_WARNING("Emergency debugger not loaded, {:s}", cruft::win32::error::code_string (code));
|
||||
}
|
||||
}
|
||||
|
||||
|
218
format.cpp.rl
218
format.cpp.rl
@ -1,218 +0,0 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* Copyright 2017 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#include "./format.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
// We generate some really old style C code via ragel here, so we have to
|
||||
// disable some noisy warnings (doubly so given -Werror)
|
||||
#pragma GCC diagnostic ignored "-Wold-style-cast"
|
||||
|
||||
namespace cruft::format {
|
||||
std::ostream&
|
||||
operator<< (std::ostream &os, type_t val)
|
||||
{
|
||||
switch (val) {
|
||||
case type_t::LITERAL: return os << "LITERAL";
|
||||
case type_t::USER: return os << "USER";
|
||||
case type_t::ESCAPE: return os << "ESCAPE";
|
||||
case type_t::SIGNED: return os << "SIGNED";
|
||||
case type_t::UNSIGNED: return os << "UNSIGNED";
|
||||
case type_t::REAL: return os << "REAL";
|
||||
case type_t::STRING: return os << "STRING";
|
||||
case type_t::CHAR: return os << "CHAR";
|
||||
case type_t::POINTER: return os << "POINTER";
|
||||
case type_t::COUNT: return os << "COUNT";
|
||||
}
|
||||
|
||||
return os << "UNKNOWN_" << static_cast<std::underlying_type_t<type_t>> (val);
|
||||
}
|
||||
|
||||
|
||||
std::ostream&
|
||||
operator<< (std::ostream &os, const specifier &val)
|
||||
{
|
||||
return os << "{ fmt: " << val.fmt << ", type: " << val.type << " }";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
%%{
|
||||
machine printf;
|
||||
|
||||
parameter = digit+ '$';
|
||||
|
||||
flag = '+' %{ s.flags.plus = true; }
|
||||
| '-' %{ s.flags.minus = true; }
|
||||
| ' ' %{ s.flags.space = true; }
|
||||
| '0' %{ s.flags.zero = true; }
|
||||
| '#' %{ s.flags.hash = true; }
|
||||
;
|
||||
|
||||
width = digit+ >{ s.width = 0; } ${ s.width *= 10; s.width += fc - '0'; };
|
||||
|
||||
# precision may be zero digits which implies zero precision.
|
||||
precision = '.' >{ s.precision = 0; } digit* ${ s.precision *= 10; s.precision += fc - '0'; };
|
||||
|
||||
length =
|
||||
'hh' %{ s.length = sizeof (char); }
|
||||
| 'h' %{ s.length = sizeof (short); }
|
||||
| 'l' %{ s.length = sizeof (long); }
|
||||
| 'll' %{ s.length = sizeof (long long); }
|
||||
| 'L' %{ s.length = sizeof (long double); }
|
||||
| 'z' %{ s.length = sizeof (size_t); }
|
||||
| 'j' %{ s.length = sizeof (intmax_t); }
|
||||
| 't' %{ s.length = sizeof (ptrdiff_t); }
|
||||
;
|
||||
|
||||
type = (
|
||||
'!' >{ s.type = type_t::USER; }
|
||||
| '%' >{ s.type = type_t::ESCAPE; }
|
||||
| (
|
||||
'd'
|
||||
| 'i'
|
||||
) >{ s.type = type_t::SIGNED; }
|
||||
| (
|
||||
'u'
|
||||
| 'x' %{ s.base = 16; }
|
||||
| 'X' %{ s.base = 16; s.upper = true; }
|
||||
| 'o' %{ s.base = 8; }
|
||||
) >{ s.type = type_t::UNSIGNED; }
|
||||
| (
|
||||
('f' | 'F' %{ s.upper = true; }) %{ s.representation = specifier::FIXED; }
|
||||
| ('e' | 'E' %{ s.upper = true; }) %{ s.representation = specifier::SCIENTIFIC; }
|
||||
| ('g' | 'G' %{ s.upper = true; }) %{ s.representation = specifier::DEFAULT; }
|
||||
| ('a' | 'A' %{ s.upper = true; }) %{ s.representation = specifier::HEX; s.base = 16; }
|
||||
) >{ s.type = type_t::REAL; }
|
||||
| 's' >{ s.type = type_t::STRING; }
|
||||
| 'c' >{ s.type = type_t::CHAR; }
|
||||
| 'p' >{ s.type = type_t::POINTER; }
|
||||
| 'n' >{ s.type = type_t::COUNT; }
|
||||
);
|
||||
|
||||
literal = ([^%]+)
|
||||
>{
|
||||
s = specifier {};
|
||||
s.fmt = {fpc,fpc};
|
||||
s.type = type_t::LITERAL;
|
||||
}
|
||||
%{
|
||||
s.fmt = {s.fmt.begin(),fpc};
|
||||
if (!s.fmt.empty ()) {
|
||||
specs.push_back (s);
|
||||
}
|
||||
};
|
||||
|
||||
specifier = (
|
||||
'%'
|
||||
parameter?
|
||||
flag**
|
||||
width?
|
||||
precision?
|
||||
length?
|
||||
type
|
||||
)
|
||||
>{
|
||||
s = specifier {};
|
||||
s.fmt = {fpc,fpc};
|
||||
}
|
||||
%{
|
||||
s.fmt = {s.fmt.begin(),fpc};
|
||||
specs.push_back (s);
|
||||
};
|
||||
|
||||
format := literal? (specifier literal?)**
|
||||
>{ success = false; }
|
||||
%{ success = true; }
|
||||
;
|
||||
|
||||
write data;
|
||||
}%%
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
cruft::format::parsed
|
||||
cruft::format::printf (cruft::view<const char*> fmt)
|
||||
{
|
||||
std::vector<specifier> specs;
|
||||
specifier s;
|
||||
bool success = false;
|
||||
(void)s;
|
||||
|
||||
int cs;
|
||||
|
||||
auto p = std::cbegin (fmt);
|
||||
auto pe = std::cend (fmt);
|
||||
auto eof = pe;
|
||||
|
||||
%%write init;
|
||||
%%write exec;
|
||||
|
||||
if (!success)
|
||||
throw std::runtime_error ("invalid format specification");
|
||||
|
||||
return parsed { std::move (specs) };
|
||||
}
|
||||
|
||||
|
||||
/// parses a format specifier in the style of PEP3101 (with the notable
|
||||
/// exception of named parameters).
|
||||
///
|
||||
/// in the event of a parsing error the function will throw. makes no
|
||||
/// attempt to cater for constexpr validation.
|
||||
cruft::format::parsed
|
||||
cruft::format::python (cruft::view<const char*> fmt)
|
||||
{
|
||||
std::vector<specifier> specs;
|
||||
|
||||
const auto *prev = std::begin (fmt);
|
||||
const auto *cursor = std::begin (fmt);
|
||||
while (*cursor) {
|
||||
switch (*cursor) {
|
||||
case '{':
|
||||
{
|
||||
{
|
||||
specifier s;
|
||||
s.fmt = { prev, cursor };
|
||||
s.type = type_t::LITERAL;
|
||||
specs.push_back (s);
|
||||
}
|
||||
|
||||
auto first = cursor;
|
||||
while (*++cursor != '}')
|
||||
;
|
||||
|
||||
++cursor;
|
||||
{
|
||||
specifier s;
|
||||
s.fmt = {first, cursor};
|
||||
s.type = type_t::USER;
|
||||
specs.push_back (s);
|
||||
}
|
||||
|
||||
prev = cursor;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
++cursor;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
specifier s;
|
||||
s.fmt = {prev,cursor};
|
||||
s.type = type_t::LITERAL;
|
||||
specs.push_back (s);
|
||||
}
|
||||
|
||||
return parsed {std::move (specs)};
|
||||
}
|
599
format.hpp
599
format.hpp
@ -1,599 +0,0 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* Copyright 2017 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#ifndef CRUFT_UTIL_FORMAT_HPP
|
||||
#define CRUFT_UTIL_FORMAT_HPP
|
||||
|
||||
#include "view.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <iomanip>
|
||||
#include <iterator>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
namespace cruft::format {
|
||||
/// denotes the stated data type of one specifier
|
||||
enum class type_t {
|
||||
/// an internal type that indicates a span of literal text to copy
|
||||
/// from the format specifier string
|
||||
LITERAL,
|
||||
/// a type that has implemented an ostream operator
|
||||
USER,
|
||||
|
||||
/// a literal '%' symbol
|
||||
ESCAPE,
|
||||
|
||||
/// numeric types
|
||||
SIGNED,
|
||||
UNSIGNED,
|
||||
REAL,
|
||||
|
||||
/// a C style string, or equivalent C++ type (std::string,
|
||||
/// std::string_view, cruft::view, etc, ...)
|
||||
STRING,
|
||||
/// a single character
|
||||
CHAR,
|
||||
|
||||
/// a raw pointer (rather than value that needs to be dereferenced)
|
||||
POINTER,
|
||||
|
||||
/// number of characters written
|
||||
COUNT
|
||||
};
|
||||
|
||||
/// formatting information for a single specifier.
|
||||
///
|
||||
/// TODO: investigate using a proper tagged union to compress the data size
|
||||
struct specifier {
|
||||
/// the sub-region of the format specifier that we parsed for this
|
||||
/// information. probably only useful for the LITERAL type as we just
|
||||
/// copy the view into the output buffer directly.
|
||||
view<const char*> fmt = view<const char*> {nullptr};
|
||||
|
||||
int parameter = -1;
|
||||
|
||||
struct {
|
||||
bool plus = false;
|
||||
bool minus = false;
|
||||
bool space = false;
|
||||
bool zero = false;
|
||||
bool hash = false;
|
||||
} flags;
|
||||
|
||||
int width = -1;
|
||||
|
||||
int precision = -1;
|
||||
|
||||
int length = -1;
|
||||
|
||||
type_t type = type_t::USER;
|
||||
|
||||
bool upper = false;
|
||||
int base = 10;
|
||||
enum {
|
||||
FIXED,
|
||||
SCIENTIFIC,
|
||||
DEFAULT,
|
||||
HEX,
|
||||
} representation = DEFAULT;
|
||||
};
|
||||
|
||||
|
||||
struct parsed;
|
||||
template <typename ...Args> class bound;
|
||||
template <typename ...Args> class stored;
|
||||
|
||||
/// a sequence of parsed specifiers that can be used to render some
|
||||
/// collection parameters in the future.
|
||||
struct parsed {
|
||||
std::vector<specifier> m_specifiers;
|
||||
|
||||
auto begin (void) const { return std::begin (m_specifiers); }
|
||||
auto end (void) const { return std::end (m_specifiers); }
|
||||
|
||||
/// records a complete collection of parameters for rendering in the
|
||||
/// future. the caller must maintain the 'parsed' object for the
|
||||
/// lifetime of the return value.
|
||||
template <typename ...Args>
|
||||
bound<Args...>
|
||||
operator() (const Args &...args) &;
|
||||
|
||||
/// records a complete collection of parameters for rendering in the
|
||||
/// future. takes ownership of the specifiers so there is no lifetime
|
||||
/// requirement.
|
||||
template <typename ...Args>
|
||||
stored<Args...>
|
||||
operator() (const Args &...args) &&;
|
||||
};
|
||||
|
||||
|
||||
template <typename... ValueT>
|
||||
class bound;
|
||||
|
||||
template <typename ...Args>
|
||||
std::string
|
||||
to_string (const bound<Args...>&);
|
||||
|
||||
template <typename... ValueT>
|
||||
class stored;
|
||||
|
||||
template <typename ...Args>
|
||||
std::string
|
||||
to_string (const bound<Args...>&);
|
||||
|
||||
|
||||
/// parameter collection for a non-owning sequence of specifiers
|
||||
template <typename ...ValueT>
|
||||
class bound {
|
||||
public:
|
||||
bound (const parsed &_parsed, const ValueT &...args):
|
||||
m_parsed {_parsed},
|
||||
m_values {args...}
|
||||
{ ; }
|
||||
|
||||
|
||||
auto specifiers (void) const
|
||||
{ return view (m_parsed.m_specifiers); }
|
||||
|
||||
|
||||
template <size_t Index>
|
||||
auto
|
||||
get (void) const& { return std::get<Index> (m_values); }
|
||||
|
||||
|
||||
operator ::std::string () const
|
||||
{
|
||||
return to_string (*this);
|
||||
}
|
||||
|
||||
private:
|
||||
const parsed &m_parsed;
|
||||
std::tuple<const ValueT&...> m_values;
|
||||
};
|
||||
|
||||
|
||||
/// parameter collection for an owning squence of specifiers.
|
||||
template <typename ...ValueT>
|
||||
class stored {
|
||||
public:
|
||||
stored (std::vector<specifier> &&_specifiers, const ValueT &...args):
|
||||
m_specifiers {std::move (_specifiers)},
|
||||
m_values {args...}
|
||||
{ ; }
|
||||
|
||||
|
||||
auto
|
||||
specifiers (void) const&
|
||||
{
|
||||
return view {m_specifiers};
|
||||
}
|
||||
|
||||
|
||||
template <size_t Index>
|
||||
const auto&
|
||||
get (void) const& { return std::get<Index> (m_values); }
|
||||
|
||||
|
||||
operator ::std::string () const
|
||||
{
|
||||
return to_string (*this);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<specifier> m_specifiers;
|
||||
std::tuple<const ValueT&...> m_values;
|
||||
};
|
||||
|
||||
|
||||
template <typename ...Args>
|
||||
bound<Args...>
|
||||
parsed::operator() (const Args &...args) &
|
||||
{ return bound { *this, args... }; }
|
||||
|
||||
|
||||
template <typename ...Args>
|
||||
stored<Args...>
|
||||
parsed::operator() (const Args &...args) &&
|
||||
{
|
||||
return stored { std::move (m_specifiers), args... };
|
||||
}
|
||||
|
||||
|
||||
/// parses a format string in the style of std::printf
|
||||
///
|
||||
/// if the format specifier is invalid the function will throw an error at
|
||||
/// runtime. specifically does not make allowances for constexpr
|
||||
/// validation.
|
||||
parsed printf (view<const char*>);
|
||||
|
||||
|
||||
/// parses a format specifier in the style of PEP3101 (with the notable
|
||||
/// exception of named parameters).
|
||||
///
|
||||
/// in the event of a parsing error the function will throw. makes no
|
||||
/// attempt to cater for constexpr validation.
|
||||
parsed python (view<const char*>);
|
||||
|
||||
|
||||
/// parses a printf format string and binds parameters for rendering.
|
||||
template <typename ...Args>
|
||||
auto
|
||||
printf (view<const char*> fmt, Args &&...args)
|
||||
{
|
||||
return printf (fmt) (args...);
|
||||
}
|
||||
|
||||
|
||||
/// parses a python format string and binds parameters for rendering.
|
||||
template <typename ...Args>
|
||||
auto
|
||||
python (view<const char*> fmt, Args &&...args)
|
||||
{
|
||||
return python (fmt) (args...);
|
||||
}
|
||||
|
||||
|
||||
template <typename ValueT>
|
||||
struct value {
|
||||
static std::ostream&
|
||||
write (std::ostream &os, specifier spec, const ValueT &val)
|
||||
{
|
||||
os << std::resetiosflags (~std::ios_base::fmtflags{});
|
||||
|
||||
switch (spec.type) {
|
||||
case type_t::REAL:
|
||||
if (!std::is_floating_point_v<ValueT>)
|
||||
throw std::runtime_error ("expected real value");
|
||||
break;
|
||||
|
||||
case type_t::UNSIGNED:
|
||||
if (!std::is_unsigned_v<ValueT>)
|
||||
throw std::runtime_error ("expected unsigned value");
|
||||
break;
|
||||
|
||||
case type_t::SIGNED:
|
||||
if (!std::is_signed_v<ValueT>)
|
||||
throw std::runtime_error ("expected signed value");
|
||||
break;
|
||||
|
||||
case type_t::STRING:
|
||||
if (std::is_same_v<ValueT, view<const char*>>)
|
||||
break;
|
||||
if (std::is_same_v<ValueT, std::string>)
|
||||
break;
|
||||
if (std::is_same_v<ValueT, std::string_view>)
|
||||
break;
|
||||
throw std::runtime_error ("expected string value");
|
||||
|
||||
case type_t::POINTER:
|
||||
if (!std::is_pointer_v<ValueT> && !std::is_integral_v<ValueT>)
|
||||
throw std::runtime_error ("expected pointer value");
|
||||
break;
|
||||
|
||||
case type_t::CHAR:
|
||||
if (!std::is_same_v<ValueT, char> &&
|
||||
!std::is_same_v<ValueT, wchar_t> &&
|
||||
!std::is_same_v<ValueT, char16_t> &&
|
||||
!std::is_same_v<ValueT, char32_t> &&
|
||||
!std::is_same_v<ValueT, signed char> &&
|
||||
!std::is_same_v<ValueT, unsigned char>)
|
||||
throw std::runtime_error ("expected character value");
|
||||
break;
|
||||
|
||||
case type_t::COUNT:
|
||||
if (!std::is_pointer_v<ValueT> && !std::is_reference_v<ValueT>)
|
||||
if (!std::is_integral_v<std::remove_pointer_t<std::remove_reference_t<ValueT>>>)
|
||||
throw std::runtime_error ("expected pointer/reference to integral");
|
||||
break;
|
||||
|
||||
case type_t::USER:
|
||||
break;
|
||||
|
||||
case type_t::ESCAPE:
|
||||
case type_t::LITERAL:
|
||||
break;
|
||||
}
|
||||
|
||||
// easy case where we just throw it to ostream
|
||||
if (spec.type == type_t::USER)
|
||||
return os << val;
|
||||
|
||||
if (spec.length > 0 && sizeof (val) != spec.length)
|
||||
throw std::runtime_error ("mismatched argument size");
|
||||
|
||||
const bool uses_space = std::is_arithmetic_v<ValueT> && spec.flags.space && !spec.flags.plus;
|
||||
if (uses_space)
|
||||
os << ' ';
|
||||
|
||||
if (spec.flags.plus)
|
||||
os << std::showpos;
|
||||
if (spec.flags.minus)
|
||||
os << std::left;
|
||||
if (spec.flags.zero)
|
||||
os << std::setfill ('0');
|
||||
|
||||
if (spec.base >= 0) {
|
||||
switch (spec.base) {
|
||||
case 10: os << std::dec; break;
|
||||
case 16: os << std::hex; break;
|
||||
case 8: os << std::oct; break;
|
||||
default:
|
||||
throw std::runtime_error ("unhandled numeric base");
|
||||
}
|
||||
}
|
||||
|
||||
if (spec.precision >= 0) {
|
||||
os << std::setprecision (spec.precision);
|
||||
}
|
||||
|
||||
if (spec.width >= 0)
|
||||
os << std::setw (spec.width - (uses_space ? 1 : 0));
|
||||
|
||||
if (spec.upper)
|
||||
os << std::uppercase;
|
||||
|
||||
if (spec.type == type_t::UNSIGNED || spec.type == type_t::SIGNED)
|
||||
if (spec.flags.hash)
|
||||
os << std::showbase;
|
||||
if (spec.type == type_t::REAL)
|
||||
if (spec.flags.hash)
|
||||
os << std::showpoint;
|
||||
|
||||
if (spec.type == type_t::REAL) {
|
||||
switch (spec.representation) {
|
||||
case specifier::FIXED: os << std::fixed; break;
|
||||
case specifier::SCIENTIFIC: os << std::scientific; break;
|
||||
case specifier::DEFAULT: os << std::defaultfloat; break;
|
||||
case specifier::HEX: os << std::hexfloat; break;
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (std::is_integral_v<ValueT>) {
|
||||
if (spec.type == type_t::POINTER) {
|
||||
if (!val)
|
||||
return os << "(nil)";
|
||||
return os << reinterpret_cast<const void*> (val);
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (std::is_floating_point_v<ValueT>) {
|
||||
if (spec.type == type_t::REAL) {
|
||||
if (std::isnan (val))
|
||||
return os << (spec.upper ? "NAN" : "nan");
|
||||
if (std::isinf (val))
|
||||
return os << (spec.upper ? "INF" : "inf");
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (std::is_integral_v<ValueT>) {
|
||||
if (spec.type == type_t::SIGNED || spec.type == type_t::UNSIGNED) {
|
||||
// explicitly handle the zero width case as blank because
|
||||
// there's no easy way to do this using iomanip.
|
||||
if (spec.precision == 0 && !val) {
|
||||
return os;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (std::is_same_v<view<const char*>, ValueT>) {
|
||||
if (spec.precision >= 0) {
|
||||
std::copy_n (
|
||||
std::begin (val),
|
||||
min (spec.precision, static_cast<int> (val.size ())),
|
||||
std::ostream_iterator<char> (os)
|
||||
);
|
||||
return os;
|
||||
}
|
||||
}
|
||||
|
||||
// the final output calls. we need to use unary plus so that
|
||||
// chars get promoted to ints for correct stream rendering when
|
||||
// the intention is to output a number.
|
||||
if constexpr (std::is_fundamental_v<ValueT>) {
|
||||
if (spec.type == type_t::CHAR)
|
||||
return os << val;
|
||||
if constexpr (!std::is_null_pointer_v<ValueT>)
|
||||
if (spec.type != type_t::USER)
|
||||
return os << +val;
|
||||
}
|
||||
|
||||
return os << val;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename ValueT>
|
||||
struct value<const ValueT*> {
|
||||
static std::ostream&
|
||||
write (std::ostream &os, specifier spec, const ValueT *val)
|
||||
{
|
||||
if (spec.type != type_t::POINTER && spec.type != type_t::USER)
|
||||
throw std::runtime_error ("expected pointer specification");
|
||||
|
||||
if (!val)
|
||||
return os << "(nil)";
|
||||
return os << reinterpret_cast<const void*> (val);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename ValueT>
|
||||
struct value<ValueT*> {
|
||||
static std::ostream&
|
||||
write (std::ostream &os, specifier spec, ValueT *val) {
|
||||
return value<const ValueT*>::write (os, spec, val);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <>
|
||||
struct value<std::nullptr_t> {
|
||||
static std::ostream&
|
||||
write (std::ostream &os, specifier s, const std::nullptr_t &val)
|
||||
{
|
||||
if (s.type != type_t::POINTER || s.type == type_t::USER)
|
||||
throw std::runtime_error ("expected pointer specifier");
|
||||
return value<const void*>::write (os, s, val);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <size_t N>
|
||||
struct value<const char[N]> {
|
||||
static std::ostream&
|
||||
write (std::ostream &os, specifier spec, const char (&val)[N]) {
|
||||
if (spec.type == type_t::STRING || spec.type == type_t::USER)
|
||||
return value<view<const char*>>::write (os, spec, view<const char*> (val));
|
||||
throw std::runtime_error ("invalid data type");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <size_t N>
|
||||
struct value<char[N]> {
|
||||
static std::ostream&
|
||||
write (std::ostream &os, specifier spec, const char (&val)[N]) {
|
||||
return value<view<const char*>>::write (os, spec, view<const char*> (val));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <>
|
||||
struct value<char*> {
|
||||
static std::ostream&
|
||||
write (std::ostream &os, specifier spec, char *val) {
|
||||
if (!val)
|
||||
return os << "(nil)";
|
||||
if (spec.type == type_t::STRING || spec.type == type_t::USER)
|
||||
return value<view<const char*>>::write (os, spec, view<const char*> { val, val + strlen (val) });
|
||||
if (spec.type == type_t::POINTER)
|
||||
return value<const void*>::write (os, spec, val);
|
||||
throw std::runtime_error ("invalid data type");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <>
|
||||
struct value<const char*> {
|
||||
static std::ostream&
|
||||
write (std::ostream &os, specifier spec, const char *val) {
|
||||
if (!val)
|
||||
return os << "(nil)";
|
||||
if (spec.type == type_t::STRING || spec.type == type_t::USER)
|
||||
return value<view<const char*>>::write (os, spec, view<const char*> { val, val + strlen (val) });
|
||||
if (spec.type == type_t::POINTER)
|
||||
return value<const void*>::write (os, spec, val);
|
||||
throw std::runtime_error ("invalid data type");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <>
|
||||
struct value<const std::string&> {
|
||||
static std::ostream&
|
||||
write (std::ostream &os, specifier spec, const std::string &val) {
|
||||
return value<view<const char*>>::write (
|
||||
os, spec, view<const char*> (val.data (), val.data () + val.size ())
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct value<std::string&> {
|
||||
static std::ostream&
|
||||
write (std::ostream &os, specifier spec, std::string &val) {
|
||||
return value<const std::string&>::write (os, spec, val);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/// renders an LITERAL specifiers followed by one parameter, then
|
||||
/// recurses for any following specifiers.
|
||||
///
|
||||
/// \tparam Index the index of the next parameter to render
|
||||
/// \tparam SpecifierT a forward iterator container
|
||||
/// \tparam DataT a tuple-like object template class
|
||||
/// \tparam Args a paramater pack of all parameter types
|
||||
///
|
||||
/// \param os the ostream that we render to
|
||||
/// \param specifiers the sequence of all specifiers to be rendered
|
||||
/// \param data a tuple-like object containing references to all parameters
|
||||
template <int Index, typename SpecifiersT, template <typename...> class HolderT, typename ...DataT>
|
||||
static std::ostream&
|
||||
write (std::ostream &os, const SpecifiersT &specifiers, const HolderT<DataT...> &data)
|
||||
{
|
||||
for (auto cursor = std::cbegin (specifiers); cursor != std::cend (specifiers); ++cursor) {
|
||||
const auto &s = *cursor;
|
||||
|
||||
if (s.type == type_t::LITERAL) {
|
||||
std::copy (std::begin (s.fmt), std::end (s.fmt), std::ostream_iterator<char> (os));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (s.type == type_t::ESCAPE) {
|
||||
os << '%';
|
||||
continue;
|
||||
}
|
||||
|
||||
if constexpr (Index < sizeof... (DataT)) {
|
||||
using value_t = std::tuple_element_t<Index,std::tuple<DataT...>>;
|
||||
value<value_t>::write (os, s, data.template get<Index> ());
|
||||
return write<Index+1> (os, make_view (cursor+1,specifiers.end ()), data);
|
||||
} else {
|
||||
throw std::runtime_error ("insufficient data parameters");
|
||||
}
|
||||
}
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
/// dispatches rendering of formats with associated parameters
|
||||
template <
|
||||
typename ...Args
|
||||
>
|
||||
std::ostream&
|
||||
operator<< (std::ostream &os, const bound<Args...> &val)
|
||||
{
|
||||
return write<0> (os, val.specifiers (), val);
|
||||
}
|
||||
|
||||
|
||||
/// dispatches rendering of formats with associated parameters
|
||||
template <
|
||||
typename ...Args
|
||||
>
|
||||
std::ostream&
|
||||
operator<< (std::ostream &os, const stored<Args...> &val)
|
||||
{
|
||||
return write<0> (os, val.specifiers (), val);
|
||||
}
|
||||
|
||||
|
||||
template <typename ...Args>
|
||||
std::string
|
||||
to_string (const bound<Args...> &fmt)
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << fmt;
|
||||
return os.str ();
|
||||
}
|
||||
|
||||
template <typename ...Args>
|
||||
std::string
|
||||
to_string (const stored<Args...> &fmt)
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << fmt;
|
||||
return os.str ();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
5
io.cpp
5
io.cpp
@ -10,9 +10,10 @@
|
||||
|
||||
#include "debug/assert.hpp"
|
||||
#include "cast.hpp"
|
||||
#include "format.hpp"
|
||||
#include "posix/except.hpp"
|
||||
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
@ -232,7 +233,7 @@ scoped_cwd::~scoped_cwd ()
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
path_error::path_error (std::filesystem::path const &_path):
|
||||
runtime_error (to_string (format::printf ("Unknown path: %!", _path))),
|
||||
runtime_error (fmt::format ("Unknown path: {}", _path)),
|
||||
m_path (_path)
|
||||
{ ; }
|
||||
|
||||
|
60
log/log.hpp
60
log/log.hpp
@ -11,7 +11,6 @@
|
||||
#include "level.hpp"
|
||||
#include "packet.hpp"
|
||||
#include "sink/base.hpp"
|
||||
#include "../format.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@ -42,26 +41,41 @@ namespace cruft::log {
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Various convenience macros for logging specific strings with a well
|
||||
// known severity.
|
||||
//
|
||||
// LOG_DEBUG is treated similarly to assert; if NDEBUG is defined then we
|
||||
// compile out the statement so as to gain a little runtime efficiency
|
||||
// speed.
|
||||
#define LOG_EMERGENCY(...) do { cruft::log::write (cruft::log::EMERGENCY, ##__VA_ARGS__); } while (0)
|
||||
#define LOG_ALERT(...) do { cruft::log::write (cruft::log::ALERT, ##__VA_ARGS__); } while (0)
|
||||
#define LOG_CRITICAL(...) do { cruft::log::write (cruft::log::CRITICAL, ##__VA_ARGS__); } while (0)
|
||||
#define LOG_ERROR(...) do { cruft::log::write (cruft::log::ERROR, ##__VA_ARGS__); } while (0)
|
||||
#define LOG_WARNING(...) do { cruft::log::write (cruft::log::WARNING, ##__VA_ARGS__); } while (0)
|
||||
#define LOG_WARN(...) do { cruft::log::write (cruft::log::WARN, ##__VA_ARGS__); } while (0)
|
||||
#define LOG_NOTICE(...) do { cruft::log::write (cruft::log::NOTICE, ##__VA_ARGS__); } while (0)
|
||||
#define LOG_INFO(...) do { cruft::log::write (cruft::log::INFO, ##__VA_ARGS__); } while (0)
|
||||
#if !defined(NDEBUG)
|
||||
#define LOG_DEBUG(...) do { cruft::log::write (cruft::log::DEBUG, ##__VA_ARGS__); } while (0)
|
||||
#else
|
||||
#define LOG_DEBUG(...) do { ; } while (0)
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Various convenience macros for logging specific strings with a well
|
||||
// known severity.
|
||||
//
|
||||
// The format string _must_ be a compile time literal so that compile time
|
||||
// checking of strings and arguments is possible.
|
||||
//
|
||||
// LOG_DEBUG is treated similarly to assert; if NDEBUG is defined then we
|
||||
// compile out the statement so as to gain a little runtime efficiency.
|
||||
|
||||
#define LOG(LEVEL, FMT, ...) \
|
||||
do { \
|
||||
::cruft::log::write ( \
|
||||
(LEVEL), \
|
||||
FMT_STRING(FMT) \
|
||||
__VA_OPT__(,) \
|
||||
__VA_ARGS__ \
|
||||
); \
|
||||
} while (0)
|
||||
|
||||
#define LOG_EMERGENCY(FMT, ...) LOG(::cruft::log::EMERGENCY, FMT __VA_OPT__(,) __VA_ARGS__)
|
||||
#define LOG_ALERT(FMT, ...) LOG(::cruft::log::ALERT, FMT __VA_OPT__(,) __VA_ARGS__)
|
||||
#define LOG_CRITICAL(FMT, ...) LOG(::cruft::log::CRITICAL, FMT __VA_OPT__(,) __VA_ARGS__)
|
||||
#define LOG_ERROR(FMT, ...) LOG(::cruft::log::ERROR, FMT __VA_OPT__(,) __VA_ARGS__)
|
||||
#define LOG_WARNING(FMT, ...) LOG(::cruft::log::WARNING, FMT __VA_OPT__(,) __VA_ARGS__)
|
||||
#define LOG_NOTICE(FMT, ...) LOG(::cruft::log::NOTICE, FMT __VA_OPT__(,) __VA_ARGS__)
|
||||
#define LOG_INFO(FMT, ...) LOG(::cruft::log::INFO, FMT __VA_OPT__(,) __VA_ARGS__)
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
#define LOG_DEBUG(FMT, ...) LOG(::cruft::log::DEBUG, FMT __VA_OPT__(,) __VA_ARGS__)
|
||||
#else
|
||||
#define LOG_DEBUG(...) do { ; } while (0)
|
||||
#endif
|
||||
|
||||
#define LOG_WARN(...) LOG_WARNING(__VA_ARGS__)
|
||||
|
@ -1,7 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "level.hpp"
|
||||
#include "../format.hpp"
|
||||
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
@ -26,12 +27,9 @@ namespace cruft::log {
|
||||
ArgsT &&..._args
|
||||
) : packet (
|
||||
_level,
|
||||
cruft::format::to_string (
|
||||
cruft::format::printf (
|
||||
std::forward<FormatT> (_format)
|
||||
) (
|
||||
std::forward<ArgsT> (_args)...
|
||||
)
|
||||
fmt::format (
|
||||
std::forward<FormatT> (_format),
|
||||
std::forward<ArgsT> (_args)...
|
||||
)
|
||||
)
|
||||
{ ; }
|
||||
|
@ -50,7 +50,7 @@ cruft::log::scoped_timer::~scoped_timer ()
|
||||
|
||||
write (
|
||||
m_level,
|
||||
"%fs, %s",
|
||||
"{:f}s, {:s}",
|
||||
float (duration) / 1'000'000'000.f,
|
||||
m_message
|
||||
);
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "../packet.hpp"
|
||||
#include "../../paths.hpp"
|
||||
#include "../../debug/warn.hpp"
|
||||
#include "../../cast.hpp"
|
||||
|
||||
using cruft::log::sink::path;
|
||||
|
||||
|
@ -184,7 +184,7 @@ namespace cruft::parse::enumeration {
|
||||
|
||||
if (!success)
|
||||
LOG_WARN (
|
||||
"duplicate parse setup for %! was ignored",
|
||||
"duplicate parse setup for {:s} was ignored",
|
||||
cruft::introspection::name::bare<EnumT> ()
|
||||
);
|
||||
|
||||
|
@ -59,14 +59,14 @@ namespace cruft {
|
||||
|
||||
if (success) {
|
||||
LOG_INFO (
|
||||
"Registered %! for %!",
|
||||
"Registered {} for {}",
|
||||
key,
|
||||
cruft::introspection::name::full<FactoryT> ()
|
||||
);
|
||||
return cookie { key };
|
||||
} {
|
||||
LOG_ERROR (
|
||||
"Unable to register %! for %!",
|
||||
"Unable to register {} for {}",
|
||||
key,
|
||||
cruft::introspection::name::full<FactoryT> ()
|
||||
);
|
||||
|
7
tap.hpp
7
tap.hpp
@ -52,7 +52,6 @@ namespace cruft::TAP {
|
||||
bool
|
||||
expect (const bool test, const char (&fmt)[N], Args&&... args)
|
||||
{
|
||||
CHECK (!strstr (fmt, "%"));
|
||||
m_output << (test ? "ok " : "not ok ") << ++m_size
|
||||
<< " - ";
|
||||
fmt::print (m_output, fmt, std::forward<Args> (args)...);
|
||||
@ -112,7 +111,7 @@ namespace cruft::TAP {
|
||||
if (almost_equal (a, b))
|
||||
return expect (true, fmt, std::forward<Args> (args)...);
|
||||
else
|
||||
return expect (false, "%! # %! != %!", format::printf (fmt)(std::forward<Args> (args)...), a, b);
|
||||
return expect (false, "{} # {} != {}", format::printf (fmt)(std::forward<Args> (args)...), a, b);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -244,9 +243,9 @@ namespace cruft::TAP {
|
||||
function (tap, args...);
|
||||
return tap.status ();
|
||||
} catch (std::exception const &err) {
|
||||
tap.fail ("no exceptions: %s", err.what ());
|
||||
tap.fail ("no exceptions: {:s}", err.what ());
|
||||
} catch (cruft::error const &err) {
|
||||
tap.fail ("no exceptions: %s", err);
|
||||
tap.fail ("no exceptions: {:s}", err);
|
||||
} catch (...) {
|
||||
tap.fail ("no exceptions");
|
||||
}
|
||||
|
223
test/format.cpp
223
test/format.cpp
@ -1,223 +0,0 @@
|
||||
#include "format.hpp"
|
||||
|
||||
#include "tap.hpp"
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
struct userobj { };
|
||||
|
||||
static std::ostream&
|
||||
operator<< (std::ostream &os, const userobj&)
|
||||
{
|
||||
return os << "userobj";
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
cruft::TAP::logger tap;
|
||||
|
||||
#define CHECK_RENDER(fmt,res,...) do { \
|
||||
auto val = to_string (cruft::format::printf (fmt)(__VA_ARGS__)); \
|
||||
tap.expect_eq (val, res, "render '{}', # {} == {}", fmt, val, res); \
|
||||
} while (0)
|
||||
|
||||
CHECK_RENDER ("foo", "foo");
|
||||
|
||||
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
|
||||
|
||||
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});
|
||||
|
||||
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", "0", size_t{0});
|
||||
CHECK_RENDER ("%zu", "1", size_t{1});
|
||||
CHECK_RENDER ("%!", "1", 1u);
|
||||
|
||||
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);
|
||||
|
||||
#if !defined(PLATFORM_WIN32)
|
||||
// msys2#xxx: hexfloat output is broken under msys2
|
||||
CHECK_RENDER ("%+a", "+0x1.3c0ca2a5b1d5dp+0", +0x1.3c0ca2a5b1d5dp+0);
|
||||
#endif
|
||||
|
||||
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);
|
||||
|
||||
#if !defined(PLATFORM_WIN32)
|
||||
// msys2#xxx: hexfloat output is broken under msys2
|
||||
CHECK_RENDER ("%a", "0x1.3c0ca2a5b1d5dp+0", 0x1.3c0ca2a5b1d5dp+0);
|
||||
CHECK_RENDER ("%A", "0X1.3C0CA2A5B1D5DP+0", 0X1.3C0CA2A5B1D5DP+0);
|
||||
#endif
|
||||
|
||||
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 ("%!", "1", 1.);
|
||||
|
||||
CHECK_RENDER ("%c", "A", 'A');
|
||||
CHECK_RENDER ("%!", "A", 'A');
|
||||
|
||||
CHECK_RENDER ("%s", "foo", "foo");
|
||||
CHECK_RENDER ("%s", "foo", std::string ("foo"));
|
||||
CHECK_RENDER ("%s", "foo", const_cast<char*> ("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 ("%!", "foo", "foo");
|
||||
|
||||
CHECK_RENDER ("%!", "userobj", userobj {});
|
||||
|
||||
CHECK_RENDER ("%p", "0x1234567", reinterpret_cast<void*>(0x01234567));
|
||||
CHECK_RENDER ("%p", "0x1234567", reinterpret_cast<int*> (0x01234567));
|
||||
CHECK_RENDER ("%p", "0x1234567", reinterpret_cast<char*>(0x01234567));
|
||||
CHECK_RENDER ("%p", "(nil)", nullptr);
|
||||
CHECK_RENDER ("%p", "(nil)", NULL);
|
||||
CHECK_RENDER ("%!", "0x1234567", reinterpret_cast<void*>(0x01234567));
|
||||
|
||||
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 ("%%%d%%", "%0%", 0);
|
||||
|
||||
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 (to_string (cruft::format::printf ("%u\n")(1u)), "1\n", "newline");
|
||||
|
||||
#define CHECK_THROW(fmt,except,...) do { \
|
||||
tap.expect_throw<std::exception> ([&] { \
|
||||
to_string (cruft::format::printf (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");
|
||||
|
||||
return tap.status ();
|
||||
}
|
@ -24,7 +24,7 @@ test_good (cruft::TAP::logger &tap)
|
||||
};
|
||||
|
||||
for (const auto &i: TESTS)
|
||||
tap.expect_eq (ipv4::ip::parse (i.str), i.ip, "%s", i.msg);
|
||||
tap.expect_eq (ipv4::ip::parse (i.str), i.ip, "{:s}", i.msg);
|
||||
}
|
||||
|
||||
|
||||
@ -43,7 +43,7 @@ test_bad (cruft::TAP::logger &tap)
|
||||
};
|
||||
|
||||
for (const auto &i: TESTS)
|
||||
tap.expect_throw<ipv4::error> ([&] { ipv4::ip::parse (i.str); }, "%s", i.msg);
|
||||
tap.expect_throw<ipv4::error> ([&] { ipv4::ip::parse (i.str); }, "{:s}", i.msg);
|
||||
}
|
||||
|
||||
|
||||
|
@ -23,7 +23,7 @@ int main ()
|
||||
for (auto const &t: TESTS) {
|
||||
auto res = cruft::parse::si<std::size_t> (t.str);
|
||||
if (!res) {
|
||||
tap.fail ("SI parsing %!", t.str);
|
||||
tap.fail ("SI parsing {}", t.str);
|
||||
} else {
|
||||
tap.expect_eq (t.val, *res, "SI parsing '{}'", t.str);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user