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(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(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)
|
RAGEL_TARGET(parse8601 time/parse8601.cpp.rl ${CMAKE_CURRENT_BINARY_DIR}/time/parse8601.cpp)
|
||||||
|
|
||||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
@ -337,8 +336,6 @@ list (
|
|||||||
fixed_string.hpp
|
fixed_string.hpp
|
||||||
float.cpp
|
float.cpp
|
||||||
float.hpp
|
float.hpp
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/format.cpp
|
|
||||||
format.hpp
|
|
||||||
fourcc.cpp
|
fourcc.cpp
|
||||||
fourcc.hpp
|
fourcc.hpp
|
||||||
functor.hpp
|
functor.hpp
|
||||||
@ -703,7 +700,6 @@ if (TESTS)
|
|||||||
extent
|
extent
|
||||||
fixed
|
fixed
|
||||||
float
|
float
|
||||||
format
|
|
||||||
geom/aabb
|
geom/aabb
|
||||||
geom/ellipse
|
geom/ellipse
|
||||||
geom/frustum
|
geom/frustum
|
||||||
|
@ -10,9 +10,10 @@
|
|||||||
|
|
||||||
#include "endian.hpp"
|
#include "endian.hpp"
|
||||||
#include "view.hpp"
|
#include "view.hpp"
|
||||||
#include "format.hpp"
|
|
||||||
#include "bitwise.hpp"
|
#include "bitwise.hpp"
|
||||||
|
|
||||||
|
#include <fmt/ostream.h>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
|
|
||||||
@ -158,10 +159,11 @@ cruft::cpu::operator<< (std::ostream &os, const x86 &val)
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
return os << cruft::format::printf (
|
fmt::print (
|
||||||
"{ name: { vendor: '%!', product: '%!' }"
|
os,
|
||||||
", cores: { logical: %!, physical: %!, hyper_threading: %! }"
|
"{ name: { vendor: '{}', product: '{}' }"
|
||||||
", simd: { sse: %!, sse2: %!, sse3: %!, ssse3: %!, sse41: %!, sse42: %!, avx: %! }"
|
", cores: { logical: {}, physical: {}, hyper_threading: {} }"
|
||||||
|
", simd: { sse: {}, sse2: {}, sse3: {}, ssse3: {}, sse41: {}, sse42: {}, avx: {} }"
|
||||||
" }",
|
" }",
|
||||||
to_string (val.vendor_name),
|
to_string (val.vendor_name),
|
||||||
to_string (val.product_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.sse42 ? "true" : "false"),
|
||||||
(val.simd.avx ? "true" : "false")
|
(val.simd.avx ? "true" : "false")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return os;
|
||||||
}
|
}
|
||||||
|
@ -8,9 +8,10 @@
|
|||||||
|
|
||||||
#include "./system.hpp"
|
#include "./system.hpp"
|
||||||
|
|
||||||
#include "debugger.hpp"
|
#include "./assert.hpp"
|
||||||
#include "except.hpp"
|
#include "./debugger.hpp"
|
||||||
#include "crash.hpp"
|
#include "./except.hpp"
|
||||||
|
#include "./crash.hpp"
|
||||||
|
|
||||||
#include "../backtrace.hpp"
|
#include "../backtrace.hpp"
|
||||||
#include "../log.hpp"
|
#include "../log.hpp"
|
||||||
@ -44,14 +45,14 @@ static void abort_with_trace (void)
|
|||||||
try {
|
try {
|
||||||
std::rethrow_exception (ptr);
|
std::rethrow_exception (ptr);
|
||||||
} catch (std::exception const &x) {
|
} 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) {
|
} catch (cruft::error const &x) {
|
||||||
LOG_EMERGENCY ("unhandled exception: %!\n%!", x, ::cruft::backtrace {});
|
LOG_EMERGENCY ("unhandled exception: {}\n{}", x, ::cruft::backtrace {});
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
LOG_EMERGENCY ("unhandled exception\n%!", ::cruft::backtrace {});
|
LOG_EMERGENCY ("unhandled exception\n{}", ::cruft::backtrace {});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LOG_EMERGENCY ("aborting: %!", ::cruft::backtrace {});
|
LOG_EMERGENCY ("aborting: {}", ::cruft::backtrace {});
|
||||||
}
|
}
|
||||||
|
|
||||||
old_handler ();
|
old_handler ();
|
||||||
|
@ -31,6 +31,6 @@ warn (const std::string &msg)
|
|||||||
void
|
void
|
||||||
warn (const char *msg)
|
warn (const char *msg)
|
||||||
{
|
{
|
||||||
LOG_WARN (msg);
|
LOG_WARN ("{}", msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ prepare_debugger (void)
|
|||||||
// as possible.
|
// as possible.
|
||||||
if (nullptr == LoadLibrary("exchndl.dll")) {
|
if (nullptr == LoadLibrary("exchndl.dll")) {
|
||||||
auto code = GetLastError ();
|
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 "debug/assert.hpp"
|
||||||
#include "cast.hpp"
|
#include "cast.hpp"
|
||||||
#include "format.hpp"
|
|
||||||
#include "posix/except.hpp"
|
#include "posix/except.hpp"
|
||||||
|
|
||||||
|
#include <fmt/ostream.h>
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
@ -232,7 +233,7 @@ scoped_cwd::~scoped_cwd ()
|
|||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
path_error::path_error (std::filesystem::path const &_path):
|
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)
|
m_path (_path)
|
||||||
{ ; }
|
{ ; }
|
||||||
|
|
||||||
|
60
log/log.hpp
60
log/log.hpp
@ -11,7 +11,6 @@
|
|||||||
#include "level.hpp"
|
#include "level.hpp"
|
||||||
#include "packet.hpp"
|
#include "packet.hpp"
|
||||||
#include "sink/base.hpp"
|
#include "sink/base.hpp"
|
||||||
#include "../format.hpp"
|
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#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
|
#pragma once
|
||||||
|
|
||||||
#include "level.hpp"
|
#include "level.hpp"
|
||||||
#include "../format.hpp"
|
|
||||||
|
#include <fmt/ostream.h>
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
@ -26,14 +27,11 @@ namespace cruft::log {
|
|||||||
ArgsT &&..._args
|
ArgsT &&..._args
|
||||||
) : packet (
|
) : packet (
|
||||||
_level,
|
_level,
|
||||||
cruft::format::to_string (
|
fmt::format (
|
||||||
cruft::format::printf (
|
std::forward<FormatT> (_format),
|
||||||
std::forward<FormatT> (_format)
|
|
||||||
) (
|
|
||||||
std::forward<ArgsT> (_args)...
|
std::forward<ArgsT> (_args)...
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
{ ; }
|
{ ; }
|
||||||
|
|
||||||
level_t level;
|
level_t level;
|
||||||
|
@ -50,7 +50,7 @@ cruft::log::scoped_timer::~scoped_timer ()
|
|||||||
|
|
||||||
write (
|
write (
|
||||||
m_level,
|
m_level,
|
||||||
"%fs, %s",
|
"{:f}s, {:s}",
|
||||||
float (duration) / 1'000'000'000.f,
|
float (duration) / 1'000'000'000.f,
|
||||||
m_message
|
m_message
|
||||||
);
|
);
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include "../packet.hpp"
|
#include "../packet.hpp"
|
||||||
#include "../../paths.hpp"
|
#include "../../paths.hpp"
|
||||||
#include "../../debug/warn.hpp"
|
#include "../../debug/warn.hpp"
|
||||||
|
#include "../../cast.hpp"
|
||||||
|
|
||||||
using cruft::log::sink::path;
|
using cruft::log::sink::path;
|
||||||
|
|
||||||
|
@ -184,7 +184,7 @@ namespace cruft::parse::enumeration {
|
|||||||
|
|
||||||
if (!success)
|
if (!success)
|
||||||
LOG_WARN (
|
LOG_WARN (
|
||||||
"duplicate parse setup for %! was ignored",
|
"duplicate parse setup for {:s} was ignored",
|
||||||
cruft::introspection::name::bare<EnumT> ()
|
cruft::introspection::name::bare<EnumT> ()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -59,14 +59,14 @@ namespace cruft {
|
|||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
LOG_INFO (
|
LOG_INFO (
|
||||||
"Registered %! for %!",
|
"Registered {} for {}",
|
||||||
key,
|
key,
|
||||||
cruft::introspection::name::full<FactoryT> ()
|
cruft::introspection::name::full<FactoryT> ()
|
||||||
);
|
);
|
||||||
return cookie { key };
|
return cookie { key };
|
||||||
} {
|
} {
|
||||||
LOG_ERROR (
|
LOG_ERROR (
|
||||||
"Unable to register %! for %!",
|
"Unable to register {} for {}",
|
||||||
key,
|
key,
|
||||||
cruft::introspection::name::full<FactoryT> ()
|
cruft::introspection::name::full<FactoryT> ()
|
||||||
);
|
);
|
||||||
|
7
tap.hpp
7
tap.hpp
@ -52,7 +52,6 @@ namespace cruft::TAP {
|
|||||||
bool
|
bool
|
||||||
expect (const bool test, const char (&fmt)[N], Args&&... args)
|
expect (const bool test, const char (&fmt)[N], Args&&... args)
|
||||||
{
|
{
|
||||||
CHECK (!strstr (fmt, "%"));
|
|
||||||
m_output << (test ? "ok " : "not ok ") << ++m_size
|
m_output << (test ? "ok " : "not ok ") << ++m_size
|
||||||
<< " - ";
|
<< " - ";
|
||||||
fmt::print (m_output, fmt, std::forward<Args> (args)...);
|
fmt::print (m_output, fmt, std::forward<Args> (args)...);
|
||||||
@ -112,7 +111,7 @@ namespace cruft::TAP {
|
|||||||
if (almost_equal (a, b))
|
if (almost_equal (a, b))
|
||||||
return expect (true, fmt, std::forward<Args> (args)...);
|
return expect (true, fmt, std::forward<Args> (args)...);
|
||||||
else
|
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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,9 +243,9 @@ namespace cruft::TAP {
|
|||||||
function (tap, args...);
|
function (tap, args...);
|
||||||
return tap.status ();
|
return tap.status ();
|
||||||
} catch (std::exception const &err) {
|
} catch (std::exception const &err) {
|
||||||
tap.fail ("no exceptions: %s", err.what ());
|
tap.fail ("no exceptions: {:s}", err.what ());
|
||||||
} catch (cruft::error const &err) {
|
} catch (cruft::error const &err) {
|
||||||
tap.fail ("no exceptions: %s", err);
|
tap.fail ("no exceptions: {:s}", err);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
tap.fail ("no exceptions");
|
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)
|
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)
|
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) {
|
for (auto const &t: TESTS) {
|
||||||
auto res = cruft::parse::si<std::size_t> (t.str);
|
auto res = cruft::parse::si<std::size_t> (t.str);
|
||||||
if (!res) {
|
if (!res) {
|
||||||
tap.fail ("SI parsing %!", t.str);
|
tap.fail ("SI parsing {}", t.str);
|
||||||
} else {
|
} else {
|
||||||
tap.expect_eq (t.val, *res, "SI parsing '{}'", t.str);
|
tap.expect_eq (t.val, *res, "SI parsing '{}'", t.str);
|
||||||
}
|
}
|
||||||
|
2
time.cpp
2
time.cpp
@ -115,7 +115,7 @@ cruft::polled_duration::stop (void) {
|
|||||||
m_series.add (dt / MILLISECOND);
|
m_series.add (dt / MILLISECOND);
|
||||||
|
|
||||||
if (m_next < now) {
|
if (m_next < now) {
|
||||||
LOG_DEBUG ("timing: '%s'. %s", m_name, m_series);
|
LOG_DEBUG ("timing: '{:s}'. {:s}", m_name, m_series);
|
||||||
m_series.reset ();
|
m_series.reset ();
|
||||||
m_next = now + m_interval;
|
m_next = now + m_interval;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user