upd: [imp] fmt_context

This commit is contained in:
mutouyun 2022-12-04 18:07:11 +08:00 committed by 木头云
parent 36b380f80f
commit b687e27a0a
13 changed files with 398 additions and 215 deletions

View File

@ -15,36 +15,73 @@ void imp_fmt_string(benchmark::State &state) {
}
}
void imp_fmt_multi_string(benchmark::State &state) {
for (auto _ : state) {
std::ignore = imp::fmt("hello world.", "hello world.", "hello world.", "hello world.", "hello world.");
}
}
void fmt_format_string(benchmark::State &state) {
for (auto _ : state) {
std::ignore = fmt::format("hello world.hello world.hello world.hello world.hello world.");
}
}
void fmt_format_multi_string(benchmark::State &state) {
for (auto _ : state) {
std::ignore = fmt::format("{}{}{}{}{}",
"hello world.", " hello world.", " hello world.", " hello world.", " hello world.");
}
}
void imp_fmt_int(benchmark::State &state) {
for (auto _ : state) {
std::ignore = imp::fmt(654321);
}
}
void imp_fmt_multi_int(benchmark::State &state) {
for (auto _ : state) {
std::ignore = imp::fmt(654321, 654321, 654321, 654321, 654321);
}
}
void fmt_format_int(benchmark::State &state) {
for (auto _ : state) {
std::ignore = fmt::format("{}", 654321);
}
}
void fmt_format_multi_int(benchmark::State &state) {
for (auto _ : state) {
std::ignore = fmt::format("{}{}{}{}{}", 654321, 654321, 654321, 654321, 654321);
}
}
void imp_fmt_float(benchmark::State &state) {
for (auto _ : state) {
std::ignore = imp::fmt(654.321);
}
}
void imp_fmt_multi_float(benchmark::State &state) {
for (auto _ : state) {
std::ignore = imp::fmt(654.321, 654.321, 654.321, 654.321, 654.321);
}
}
void fmt_format_float(benchmark::State &state) {
for (auto _ : state) {
std::ignore = fmt::format("{}", 654.321);
}
}
void fmt_format_multi_float(benchmark::State &state) {
for (auto _ : state) {
std::ignore = fmt::format("{}{}{}{}{}", 654.321, 654.321, 654.321, 654.321, 654.321);
}
}
void imp_fmt_chrono(benchmark::State &state) {
for (auto _ : state) {
std::ignore = imp::fmt(std::chrono::system_clock::now());
@ -65,5 +102,13 @@ BENCHMARK(imp_fmt_int);
BENCHMARK(fmt_format_int);
BENCHMARK(imp_fmt_float);
BENCHMARK(fmt_format_float);
BENCHMARK(imp_fmt_multi_string);
BENCHMARK(fmt_format_multi_string);
BENCHMARK(imp_fmt_multi_int);
BENCHMARK(fmt_format_multi_int);
BENCHMARK(imp_fmt_multi_float);
BENCHMARK(fmt_format_multi_float);
BENCHMARK(imp_fmt_chrono);
BENCHMARK(fmt_format_chrono);

View File

@ -164,17 +164,17 @@ auto as_bytes(span<T> s) noexcept -> span<Byte> {
return {byte_cast(s.data()), s.size_bytes()};
}
/// @brief Custom defined fmt_to_string method for imp::fmt
/// @brief Custom defined fmt_to method for imp::fmt
namespace detail {
inline std::string tag_invoke(decltype(::LIBIMP::fmt_to_string), ::LIBIMP::byte b) {
return ::LIBIMP::to_string(static_cast<std::uint8_t>(b), "02x");
inline bool tag_invoke(decltype(::LIBIMP::fmt_to), fmt_context &ctx, ::LIBIMP::byte b) {
return ::LIBIMP::to_string(ctx, static_cast<std::uint8_t>(b), "02x");
}
template <typename T,
typename = std::enable_if_t<std::is_same<std::decay_t<T>, ::LIBIMP::byte>::value>>
std::string tag_invoke(fmt_to_string_t, fmt_ref<T> arg) noexcept {
return ::LIBIMP::to_string(static_cast<std::uint8_t>(arg.param), arg.fstr);
bool tag_invoke(decltype(::LIBIMP::fmt_to), fmt_context &ctx, fmt_ref<T> arg) noexcept {
return ::LIBIMP::to_string(ctx, static_cast<std::uint8_t>(arg.param), arg.fstr);
}
} // namespace detail

View File

@ -49,66 +49,62 @@ auto spec(char const (&fstr)[N]) noexcept {
};
}
/// @brief String formatting function.
template <typename A>
LIBIMP_NODISCARD std::string fmt(A &&a) {
return fmt_to_string(std::forward<A>(a));
/**
* @brief String formatting function.
*
* @param args arguments that support the fmt output
* @return an empty string if the fmt output fails
*/
template <typename... A>
LIBIMP_NODISCARD std::string fmt(A &&...args) {
std::string joined;
fmt_context ctx(joined);
if (fmt_to(ctx, std::forward<A>(args)...)) {
return ctx.finish() ? joined : "";
}
return {};
}
template <typename A1, typename... A>
LIBIMP_NODISCARD std::string fmt(A1 &&a1, A &&...args) {
std::string joined(fmt(std::forward<A1>(a1)));
LIBIMP_UNUSED auto unfold = {
joined.append(fmt_to_string(std::forward<A>(args)))...
};
return joined;
}
/// @brief Return the string directly.
inline char const *to_string(char const * a) noexcept { return (a == nullptr) ? "" : a; }
inline std::string to_string(std::string const &a) noexcept { return a; }
inline std::string to_string(std::string && a) noexcept { return std::move(a); }
LIBIMP_EXPORT std::string to_string(char const * a, span<char const> fstr) noexcept;
inline std::string to_string(std::string const &a, span<char const> fstr) noexcept { return to_string(a.c_str(), fstr); }
/// @brief String types.
LIBIMP_EXPORT bool to_string(fmt_context &ctx, char const * a) noexcept;
LIBIMP_EXPORT bool to_string(fmt_context &ctx, std::string const &a) noexcept;
LIBIMP_EXPORT bool to_string(fmt_context &ctx, char const * a, span<char const> fstr) noexcept;
inline bool to_string(fmt_context &ctx, std::string const &a, span<char const> fstr) noexcept { return to_string(ctx, a.c_str(), fstr); }
/// @brief Character to string conversion.
/// @return an empty string if the conversion fails
inline std::string to_string(char a) noexcept { return {a}; }
LIBIMP_EXPORT bool to_string(fmt_context &ctx, char a) noexcept;
#if defined(LIBIMP_CPP_20)
inline std::string to_string(char8_t a) noexcept { return to_string((char)a); }
inline bool to_string(fmt_context &ctx, char8_t a) noexcept { return to_string(ctx, (char)a); }
#endif // defined(LIBIMP_CPP_20)
LIBIMP_EXPORT std::string to_string(wchar_t a) noexcept;
LIBIMP_EXPORT std::string to_string(char16_t a) noexcept;
LIBIMP_EXPORT std::string to_string(char32_t a) noexcept;
LIBIMP_EXPORT bool to_string(fmt_context &ctx, wchar_t a) noexcept;
LIBIMP_EXPORT bool to_string(fmt_context &ctx, char16_t a) noexcept;
LIBIMP_EXPORT bool to_string(fmt_context &ctx, char32_t a) noexcept;
/// @brief Conversion of numeric types to strings.
/// @return an empty string if the conversion fails
LIBIMP_EXPORT std::string to_string( signed short a, span<char const> fstr = {}) noexcept;
LIBIMP_EXPORT std::string to_string(unsigned short a, span<char const> fstr = {}) noexcept;
LIBIMP_EXPORT std::string to_string( signed int a, span<char const> fstr = {}) noexcept;
LIBIMP_EXPORT std::string to_string(unsigned int a, span<char const> fstr = {}) noexcept;
LIBIMP_EXPORT std::string to_string( signed long a, span<char const> fstr = {}) noexcept;
LIBIMP_EXPORT std::string to_string(unsigned long a, span<char const> fstr = {}) noexcept;
LIBIMP_EXPORT std::string to_string( signed long long a, span<char const> fstr = {}) noexcept;
LIBIMP_EXPORT std::string to_string(unsigned long long a, span<char const> fstr = {}) noexcept;
inline std::string to_string( signed char a, span<char const> fstr = {}) noexcept { return to_string( (int)a, fstr); }
inline std::string to_string(unsigned char a, span<char const> fstr = {}) noexcept { return to_string((unsigned)a, fstr); }
LIBIMP_EXPORT bool to_string(fmt_context &ctx, signed short a, span<char const> fstr = {}) noexcept;
LIBIMP_EXPORT bool to_string(fmt_context &ctx, unsigned short a, span<char const> fstr = {}) noexcept;
LIBIMP_EXPORT bool to_string(fmt_context &ctx, signed int a, span<char const> fstr = {}) noexcept;
LIBIMP_EXPORT bool to_string(fmt_context &ctx, unsigned int a, span<char const> fstr = {}) noexcept;
LIBIMP_EXPORT bool to_string(fmt_context &ctx, signed long a, span<char const> fstr = {}) noexcept;
LIBIMP_EXPORT bool to_string(fmt_context &ctx, unsigned long a, span<char const> fstr = {}) noexcept;
LIBIMP_EXPORT bool to_string(fmt_context &ctx, signed long long a, span<char const> fstr = {}) noexcept;
LIBIMP_EXPORT bool to_string(fmt_context &ctx, unsigned long long a, span<char const> fstr = {}) noexcept;
inline bool to_string(fmt_context &ctx, signed char a, span<char const> fstr = {}) noexcept { return to_string(ctx, (int)a, fstr); }
inline bool to_string(fmt_context &ctx, unsigned char a, span<char const> fstr = {}) noexcept { return to_string(ctx, (unsigned)a, fstr); }
/// @brief Conversion of floating point type to strings.
/// @return an empty string if the conversion fails
LIBIMP_EXPORT std::string to_string( double a, span<char const> fstr = {}) noexcept;
LIBIMP_EXPORT std::string to_string(long double a, span<char const> fstr = {}) noexcept;
inline std::string to_string(float a, span<char const> fstr = {}) noexcept { return to_string((double)a, fstr); }
LIBIMP_EXPORT bool to_string(fmt_context &ctx, double a, span<char const> fstr = {}) noexcept;
LIBIMP_EXPORT bool to_string(fmt_context &ctx, long double a, span<char const> fstr = {}) noexcept;
inline bool to_string(fmt_context &ctx, float a, span<char const> fstr = {}) noexcept { return to_string(ctx, (double)a, fstr); }
/// @brief Pointer.
inline std::string to_string(std::nullptr_t) noexcept { return "null"; }
LIBIMP_EXPORT bool to_string(fmt_context &ctx, std::nullptr_t) noexcept;
template <typename T,
typename = std::enable_if_t<std::is_same<T, void>::value>>
LIBIMP_EXPORT std::string to_string(T const volatile *a) noexcept;
LIBIMP_EXPORT bool to_string(fmt_context &ctx, T const volatile *a) noexcept;
/// @brief Date and time.
LIBIMP_EXPORT std::string to_string(std::tm const &a, span<char const> fstr = {}) noexcept;
LIBIMP_EXPORT bool to_string(fmt_context &ctx, std::tm const &a, span<char const> fstr = {}) noexcept;
namespace detail {
@ -116,53 +112,55 @@ namespace detail {
* @brief Convert std::time_t to std::string.
* @return an empty string if the conversion fails
*/
inline std::string time_to_string(std::time_t tt, span<char const> fstr) noexcept {
inline bool time_to_string(fmt_context &ctx, std::time_t tt, span<char const> fstr) noexcept {
#if defined(LIBIMP_CC_MSVC)
/// @see https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/localtime-s-localtime32-s-localtime64-s
std::tm tm {};
if (::localtime_s(&tm, &tt) != 0) {
return {};
}
return to_string(tm, fstr);
return to_string(ctx, tm, fstr);
#else
return to_string(*std::localtime(&tt), fstr);
return to_string(ctx, *std::localtime(&tt), fstr);
#endif
}
} // namespace detail
template <class Clock, class Duration>
std::string to_string(std::chrono::time_point<Clock, Duration> const &a, span<char const> fstr = {}) noexcept {
return detail::time_to_string(std::chrono::system_clock::to_time_t(a), fstr);
bool to_string(fmt_context &ctx, std::chrono::time_point<Clock, Duration> const &a, span<char const> fstr = {}) noexcept {
return detail::time_to_string(ctx, std::chrono::system_clock::to_time_t(a), fstr);
}
/**
* @brief Predefined fmt_to_string method
* @brief Predefined fmt_to method
*/
namespace detail {
template <typename T>
auto tag_invoke(fmt_to_string_t, T &&arg) noexcept
-> decltype(::LIBIMP::to_string(std::forward<T>(arg))) {
return ::LIBIMP::to_string(std::forward<T>(arg));
auto tag_invoke(fmt_to_t, fmt_context &ctx, T &&arg) noexcept
-> decltype(::LIBIMP::to_string(ctx, std::forward<T>(arg))) {
return ::LIBIMP::to_string(ctx, std::forward<T>(arg));
}
template <typename T>
auto tag_invoke(fmt_to_string_t, fmt_ref<T> arg) noexcept
-> decltype(::LIBIMP::to_string(static_cast<T>(arg.param), arg.fstr)) {
return ::LIBIMP::to_string(static_cast<T>(arg.param), arg.fstr);
auto tag_invoke(fmt_to_t, fmt_context &ctx, fmt_ref<T> arg) noexcept
-> decltype(::LIBIMP::to_string(ctx, static_cast<T>(arg.param), arg.fstr)) {
return ::LIBIMP::to_string(ctx, static_cast<T>(arg.param), arg.fstr);
}
template <typename T>
std::string tag_invoke(decltype(::LIBIMP::fmt_to_string), span<T> s) {
bool tag_invoke(decltype(::LIBIMP::fmt_to), fmt_context &ctx, span<T> s) {
if (s.empty()) {
return {};
return false;
}
if (!fmt_to(ctx, s[0])) {
return false;
}
auto appender = fmt(s[0]);
for (std::size_t i = 1; i < s.size(); ++i) {
appender += fmt(' ', s[i]);
if (!fmt_to(ctx, ' ', s[i])) return false;
}
return appender;
return true;
}
} // namespace detail

View File

@ -7,26 +7,66 @@
#pragma once
#include <string>
#include <array>
#include <cstddef>
#include "libimp/def.h"
#include "libimp/generic.h"
#include "libimp/detect_plat.h"
#include "libimp/span.h"
#include "libimp/export.h"
LIBIMP_NAMESPACE_BEG_
constexpr std::size_t fmt_context_aligned_size = 512U;
/**
* @brief Supports custom fmt_to_string methods for imp::fmt.
* @brief The context of fmt.
*/
class LIBIMP_EXPORT fmt_context {
std::array<char, fmt_context_aligned_size> sbuf_;
std::string &joined_;
std::size_t offset_;
public:
fmt_context(std::string &j) noexcept;
std::size_t capacity() noexcept;
void reset() noexcept;
bool finish() noexcept;
bool resize(std::size_t sz) noexcept;
span<char> buffer() noexcept;
bool expend(std::size_t sz) noexcept;
bool append(std::string const &str) noexcept;
};
/**
* @brief Supports custom fmt_to methods for imp::fmt.
*/
namespace detail {
struct fmt_to_string_t {
template <typename T>
std::string operator()(T &&arg) const {
return ::LIBIMP::tag_invoke(fmt_to_string_t{}, std::forward<T>(arg));
class fmt_to_t {
template <typename A1>
bool get_result(fmt_context &ctx, A1 && a1) const {
return ::LIBIMP::tag_invoke(fmt_to_t{}, ctx, std::forward<A1>(a1));
}
template <typename A1, typename... A>
bool get_result(fmt_context &ctx, A1 && a1, A &&...args) const {
return get_result(ctx, std::forward<A1>(a1))
&& get_result(ctx, std::forward<A>(args)...);
}
public:
template <typename... A>
bool operator()(fmt_context &ctx, A &&...args) const {
return get_result(ctx, std::forward<A>(args)...);
}
};
} // namespace detail
constexpr detail::fmt_to_string_t fmt_to_string {};
constexpr detail::fmt_to_t fmt_to {};
LIBIMP_NAMESPACE_END_

View File

@ -37,11 +37,12 @@ struct context {
};
LIBIMP_EXPORT std::string to_string(context &&) noexcept;
LIBIMP_EXPORT bool to_string(fmt_context &ctx, context &&) noexcept;
/// @brief Custom defined fmt_to_string method for imp::fmt
/// @brief Custom defined fmt_to method for imp::fmt
template <typename T>
std::string tag_invoke(decltype(::LIBIMP::fmt_to_string), context &&arg) noexcept {
return ::LIBIMP::log::to_string(std::move(arg));
bool tag_invoke(decltype(::LIBIMP::fmt_to), fmt_context &ctx, context &&arg) noexcept {
return ::LIBIMP::log::to_string(ctx, std::move(arg));
}
} // namespace log

View File

@ -155,16 +155,16 @@ public:
friend bool operator!=(result const &lhs, result const &rhs) noexcept { return lhs.code_ != rhs.code_; }
};
/// @brief Custom defined fmt_to_string method for imp::fmt
/// @brief Custom defined fmt_to method for imp::fmt
namespace detail {
inline std::string tag_invoke(decltype(::LIBIMP::fmt_to_string), result_code r) {
return fmt("[", (r ? "succ" : "fail"), ", value = ", *r, "]");
inline bool tag_invoke(decltype(::LIBIMP::fmt_to), fmt_context &ctx, result_code r) {
return fmt_to(ctx, "[", (r ? "succ" : "fail"), ", value = ", *r, "]");
}
template <typename T, typename D>
std::string tag_invoke(decltype(::LIBIMP::fmt_to_string), result<T, D> r) {
return fmt("[", (r ? "succ" : "fail"), ", value = ", result<T, D>::default_traits_t::format(r), "]");
bool tag_invoke(decltype(::LIBIMP::fmt_to), fmt_context &ctx, result<T, D> r) {
return fmt_to(ctx, "[", (r ? "succ" : "fail"), ", value = ", result<T, D>::default_traits_t::format(r), "]");
}
} // namespace detail

View File

@ -234,7 +234,7 @@ auto make_span(T (&arr)[E]) noexcept -> span<T> {
}
template <typename T, std::size_t E>
auto make_span(std::array<T, E> const &arr) noexcept -> span<T> {
auto make_span(std::array<T, E> &arr) noexcept -> span<T> {
return {arr};
}

View File

@ -50,10 +50,8 @@ public:
std::string str() const noexcept;
friend bool operator==(error const &lhs, error const &rhs) noexcept;
friend bool operator!=(error const &lhs, error const &rhs) noexcept;
friend std::ostream &operator<<(std::ostream &o, error const &e);
friend LIBIMP_EXPORT bool operator==(error const &lhs, error const &rhs) noexcept;
friend LIBIMP_EXPORT bool operator!=(error const &lhs, error const &rhs) noexcept;
};
/**
@ -65,10 +63,10 @@ enum class info : std::int32_t {
LIBIMP_EXPORT result<std::int64_t> conf(info) noexcept;
/**
* @brief @brief Custom defined fmt_to_string method for imp::fmt
* @brief @brief Custom defined fmt_to method for imp::fmt
*/
inline std::string tag_invoke(decltype(::LIBIMP::fmt_to_string), error r) noexcept {
return error_msg(r.code());
inline bool tag_invoke(decltype(::LIBIMP::fmt_to), fmt_context &ctx, error r) noexcept {
return fmt_to(ctx, error_msg(r.code()));
}
} // namespace sys

View File

@ -19,12 +19,16 @@ LIBIMP_NAMESPACE_BEG_
*/
namespace {
span<char const> normalize(span<char const> a) {
constexpr std::size_t roundup(std::size_t sz) noexcept {
return (sz & ~(fmt_context_aligned_size - 1)) + fmt_context_aligned_size;
}
span<char const> normalize(span<char const> const &a) {
if (a.empty()) return {};
return a.first(a.size() - (a.back() == '\0' ? 1 : 0));
}
span<char> smem_cpy(span<char> sbuf, span<char const> a) noexcept {
span<char> smem_cpy(span<char> const &sbuf, span<char const> a) noexcept {
if (sbuf.empty()) return {};
a = normalize(a);
auto sz = (std::min)(sbuf.size() - 1, a.size());
@ -32,13 +36,13 @@ span<char> smem_cpy(span<char> sbuf, span<char const> a) noexcept {
return sbuf.first(sz);
}
span<char> sbuf_cpy(span<char> sbuf, span<char const> a) noexcept {
span<char> sbuf_cpy(span<char> sbuf, span<char const> const &a) noexcept {
sbuf = smem_cpy(sbuf, a);
*sbuf.end() = '\0';
return sbuf;
}
span<char> sbuf_cat(span<char> sbuf, std::initializer_list<span<char const>> args) noexcept {
span<char> sbuf_cat(span<char> const &sbuf, std::initializer_list<span<char const>> args) noexcept {
std::size_t remain = sbuf.size();
for (auto s : args) {
remain -= smem_cpy(sbuf.last(remain), s).size();
@ -49,21 +53,21 @@ span<char> sbuf_cat(span<char> sbuf, std::initializer_list<span<char const>> arg
}
span<char> local_fmt_str() noexcept {
thread_local std::array<char, 512> sbuf;
thread_local std::array<char, fmt_context_aligned_size> sbuf;
return sbuf;
}
char const *as_cstr(span<char const> a) {
char const *as_cstr(span<char const> const &a) {
if (a.empty()) return "";
if (a.back() == '\0') return a.data();
return sbuf_cpy(local_fmt_str(), a).data();
}
span<char> fmt_of(span<char const> fstr, span<char const> s) {
span<char> fmt_of(span<char const> const &fstr, span<char const> const &s) {
return sbuf_cat(local_fmt_str(), {"%", fstr, s});
}
span<char> fmt_of_unsigned(span<char const> fstr, span<char const> l) {
span<char> fmt_of_unsigned(span<char const> fstr, span<char const> const &l) {
if (fstr.empty()) {
return fmt_of(l, "u");
}
@ -77,7 +81,7 @@ span<char> fmt_of_unsigned(span<char const> fstr, span<char const> l) {
}
}
span<char> fmt_of_signed(span<char const> fstr, span<char const> l) {
span<char> fmt_of_signed(span<char const> fstr, span<char const> const &l) {
if (fstr.empty()) {
return fmt_of(l, "d");
}
@ -91,7 +95,7 @@ span<char> fmt_of_signed(span<char const> fstr, span<char const> l) {
}
}
span<char> fmt_of_float(span<char const> fstr, span<char const> l) {
span<char> fmt_of_float(span<char const> fstr, span<char const> const &l) {
if (fstr.empty()) {
return fmt_of(l, "f");
}
@ -106,125 +110,206 @@ span<char> fmt_of_float(span<char const> fstr, span<char const> l) {
}
template <typename A /*a fundamental or pointer type*/>
std::string sprintf(span<char const> sfmt, A a) {
std::array<char, 512> sbuf;
int sprintf(fmt_context &ctx, span<char const> const &sfmt, A a) {
for (;;) {
auto sbuf = ctx.buffer();
auto sz = std::snprintf(sbuf.data(), sbuf.size(), sfmt.data(), a);
if (sz <= 0) return {};
if (sz <= 0) {
return sz;
}
if (sz < sbuf.size()) {
return std::string(sbuf.data(), sz);
return ctx.expend(sz) ? sz : -1;
}
if (!ctx.resize(sz + 1)) {
return -1;
}
std::string des;
des.resize(sz + 1);
if (std::snprintf(&des[0], des.size(), sfmt.data(), a) < 0) {
return {};
}
des.pop_back(); // remove the terminated null character
return des;
}
template <typename F /*a function pointer*/,
typename A /*a fundamental or pointer type*/>
std::string sprintf(F fop, span<char const> fstr, span<char const> s, A a) noexcept {
bool sprintf(fmt_context &ctx, F fop, span<char const> const &fstr, span<char const> const &s, A a) noexcept {
LIBIMP_TRY {
return ::LIBIMP::sprintf(fop(fstr, s), a);
return ::LIBIMP::sprintf(ctx, fop(fstr, s), a) >= 0;
} LIBIMP_CATCH(...) {
return {};
return false;
}
}
} // namespace
std::string to_string(char const *a, span<char const> fstr) noexcept {
if (a == nullptr) return {};
return ::LIBIMP::sprintf(fmt_of, fstr, "s", a);
/// @brief The context of fmt.
fmt_context::fmt_context(std::string &j) noexcept
: joined_(j)
, offset_(0) {}
std::size_t fmt_context::capacity() noexcept {
return (offset_ < sbuf_.size()) ? sbuf_.size() : joined_.size();
}
std::string to_string(wchar_t a) noexcept {
void fmt_context::reset() noexcept {
offset_ = 0;
}
bool fmt_context::finish() noexcept {
LIBIMP_TRY {
if (offset_ < sbuf_.size()) {
joined_.assign(sbuf_.data(), offset_);
} else {
joined_.resize(offset_);
}
return true;
} LIBIMP_CATCH(...) {
return false;
}
}
bool fmt_context::resize(std::size_t sz) noexcept {
LIBIMP_TRY {
if (sz < sbuf_.size()) {
return true;
}
joined_.resize(roundup(sz));
return true;
} LIBIMP_CATCH(...) {
return false;
}
}
span<char> fmt_context::buffer() noexcept {
if (offset_ < sbuf_.size()) {
return make_span(sbuf_).subspan(offset_);
} else {
return {&joined_[offset_], joined_.size() - offset_};
}
}
bool fmt_context::expend(std::size_t sz) noexcept {
if ((offset_ += sz) < sbuf_.size()) {
return true;
}
return (offset_ < joined_.size()) || resize(offset_);
}
bool fmt_context::append(std::string const &str) noexcept {
if ((buffer().size() < str.size()) && !resize(offset_ + str.size())) {
return false;
}
std::memcpy(buffer().data(), str.data(), str.size());
offset_ += str.size();
return true;
}
/// @brief To string conversion.
bool to_string(fmt_context &ctx, char const *a) noexcept {
return to_string(ctx, a, {});
}
bool to_string(fmt_context &ctx, std::string const &a) noexcept {
return ctx.append(a);
}
bool to_string(fmt_context &ctx, char const *a, span<char const> fstr) noexcept {
if (a == nullptr) return false;
return ::LIBIMP::sprintf(ctx, fmt_of, fstr, "s", a);
}
bool to_string(fmt_context &ctx, char a) noexcept {
return ::LIBIMP::sprintf(ctx, fmt_of, {}, "c", a);
}
bool to_string(fmt_context &ctx, wchar_t a) noexcept {
LIBIMP_TRY {
std::string des;
cvt_sstr(std::wstring{a}, des);
return des;
return ctx.append(des);
} LIBIMP_CATCH(...) {
return {};
return false;
}
}
std::string to_string(char16_t a) noexcept {
bool to_string(fmt_context &ctx, char16_t a) noexcept {
LIBIMP_TRY {
std::string des;
cvt_sstr(std::u16string{a}, des);
return des;
return ctx.append(des);
} LIBIMP_CATCH(...) {
return {};
return false;
}
}
std::string to_string(char32_t a) noexcept {
bool to_string(fmt_context &ctx, char32_t a) noexcept {
LIBIMP_TRY {
std::string des;
cvt_sstr(std::u32string{a}, des);
return des;
return ctx.append(des);
} LIBIMP_CATCH(...) {
return {};
return false;
}
}
std::string to_string(signed short a, span<char const> fstr) noexcept {
return ::LIBIMP::sprintf(fmt_of_signed, fstr, "h", a);
bool to_string(fmt_context &ctx, signed short a, span<char const> fstr) noexcept {
return ::LIBIMP::sprintf(ctx, fmt_of_signed, fstr, "h", a);
}
std::string to_string(unsigned short a, span<char const> fstr) noexcept {
return ::LIBIMP::sprintf(fmt_of_unsigned, fstr, "h", a);
bool to_string(fmt_context &ctx, unsigned short a, span<char const> fstr) noexcept {
return ::LIBIMP::sprintf(ctx, fmt_of_unsigned, fstr, "h", a);
}
std::string to_string(signed int a, span<char const> fstr) noexcept {
return ::LIBIMP::sprintf(fmt_of_signed, fstr, "", a);
bool to_string(fmt_context &ctx, signed int a, span<char const> fstr) noexcept {
return ::LIBIMP::sprintf(ctx, fmt_of_signed, fstr, "", a);
}
std::string to_string(unsigned int a, span<char const> fstr) noexcept {
return ::LIBIMP::sprintf(fmt_of_unsigned, fstr, "", a);
bool to_string(fmt_context &ctx, unsigned int a, span<char const> fstr) noexcept {
return ::LIBIMP::sprintf(ctx, fmt_of_unsigned, fstr, "", a);
}
std::string to_string(signed long a, span<char const> fstr) noexcept {
return ::LIBIMP::sprintf(fmt_of_signed, fstr, "l", a);
bool to_string(fmt_context &ctx, signed long a, span<char const> fstr) noexcept {
return ::LIBIMP::sprintf(ctx, fmt_of_signed, fstr, "l", a);
}
std::string to_string(unsigned long a, span<char const> fstr) noexcept {
return ::LIBIMP::sprintf(fmt_of_unsigned, fstr, "l", a);
bool to_string(fmt_context &ctx, unsigned long a, span<char const> fstr) noexcept {
return ::LIBIMP::sprintf(ctx, fmt_of_unsigned, fstr, "l", a);
}
std::string to_string(signed long long a, span<char const> fstr) noexcept {
return ::LIBIMP::sprintf(fmt_of_signed, fstr, "ll", a);
bool to_string(fmt_context &ctx, signed long long a, span<char const> fstr) noexcept {
return ::LIBIMP::sprintf(ctx, fmt_of_signed, fstr, "ll", a);
}
std::string to_string(unsigned long long a, span<char const> fstr) noexcept {
return ::LIBIMP::sprintf(fmt_of_unsigned, fstr, "ll", a);
bool to_string(fmt_context &ctx, unsigned long long a, span<char const> fstr) noexcept {
return ::LIBIMP::sprintf(ctx, fmt_of_unsigned, fstr, "ll", a);
}
std::string to_string(double a, span<char const> fstr) noexcept {
return ::LIBIMP::sprintf(fmt_of_float, fstr, "", a);
bool to_string(fmt_context &ctx, double a, span<char const> fstr) noexcept {
return ::LIBIMP::sprintf(ctx, fmt_of_float, fstr, "", a);
}
std::string to_string(long double a, span<char const> fstr) noexcept {
return ::LIBIMP::sprintf(fmt_of_float, fstr, "L", a);
bool to_string(fmt_context &ctx, long double a, span<char const> fstr) noexcept {
return ::LIBIMP::sprintf(ctx, fmt_of_float, fstr, "L", a);
}
bool to_string(fmt_context &ctx, std::nullptr_t) noexcept {
return ctx.append("null");
}
template <>
std::string to_string<void, void>(void const volatile *a) noexcept {
bool to_string<void, void>(fmt_context &ctx, void const volatile *a) noexcept {
if (a == nullptr) {
return to_string(nullptr);
return to_string(ctx, nullptr);
}
return ::LIBIMP::sprintf(fmt_of, "", "p", a);
return ::LIBIMP::sprintf(ctx, fmt_of, "", "p", a);
}
std::string to_string(std::tm const &a, span<char const> fstr) noexcept {
bool to_string(fmt_context &ctx, std::tm const &a, span<char const> fstr) noexcept {
if (fstr.empty()) {
fstr = "%Y-%m-%d %H:%M:%S";
}
LIBIMP_TRY {
std::ostringstream ss;
ss << std::put_time(&a, as_cstr(fstr));
return ss.str();
return ctx.append(ss.str());
} LIBIMP_CATCH(...) {
return {};
}

View File

@ -6,19 +6,28 @@
LIBIMP_NAMESPACE_BEG_
namespace log {
std::string to_string(context &&ctx) noexcept {
bool to_string(fmt_context &f_ctx, context &&l_ctx) noexcept {
constexpr static char types[] = {
'T', 'D', 'I', 'W', 'E', 'F',
};
LIBIMP_TRY {
auto ms = std::chrono::time_point_cast<std::chrono::milliseconds>(ctx.tp).time_since_epoch().count() % 1000;
return fmt("[", types[enum_cast(ctx.level)], "][", ctx.tp, ".", spec("03")(ms), "][", ctx.func, "] ", ctx.text);
} LIBIMP_CATCH(std::exception const &e) {
/// @remark [TBD] std::string constructor may throw an exception
return e.what();
auto ms = std::chrono::time_point_cast<std::chrono::milliseconds>(l_ctx.tp).time_since_epoch().count() % 1000;
return fmt_to(f_ctx, "[", types[enum_cast(l_ctx.level)], "][", l_ctx.tp, ".", spec("03")(ms), "][", l_ctx.func, "] ", l_ctx.text);
} LIBIMP_CATCH(std::exception const &) {
return false;
}
}
std::string to_string(context &&ctx) noexcept {
std::string log_txt;
fmt_context f_ctx(log_txt);
if (!to_string(f_ctx, std::move(ctx))) {
return {};
}
f_ctx.finish();
return log_txt;
}
printer::operator bool() const noexcept {
return (objp_ != nullptr) && (vtable_ != nullptr);
}

View File

@ -49,10 +49,5 @@ bool operator!=(error const &lhs, error const &rhs) noexcept {
return lhs.code() != rhs.code();
}
std::ostream &operator<<(std::ostream &o, error const &e) {
o << error_msg(e.code());
return o;
}
} // namespace sys
LIBIMP_NAMESPACE_END_

View File

@ -1,5 +1,6 @@
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <tuple>
#include "gtest/gtest.h"
@ -13,68 +14,82 @@ TEST(fmt, spec) {
}
TEST(fmt, to_string) {
std::string joined;
imp::fmt_context ctx(joined);
/// @brief string
EXPECT_STREQ(imp::to_string(""), "");
EXPECT_STREQ(imp::to_string("%what%"), "%what%");
EXPECT_EQ(imp::to_string("%what%", "10") , " %what%");
EXPECT_EQ(imp::to_string("%what%", "-10"), "%what% ");
EXPECT_TRUE(imp::to_string(ctx, "")); ctx.finish();
EXPECT_EQ(joined, "");
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, "%what%")); ctx.finish();
EXPECT_EQ(joined, "%what%");
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, "%what%", "10")); ctx.finish();
EXPECT_EQ(joined, " %what%");
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, "%what%", "-10")); ctx.finish();
EXPECT_EQ(joined, "%what% ");
/// @brief character
EXPECT_EQ(imp::to_string('A'), "A");
EXPECT_EQ(imp::to_string(L'A'), "A");
EXPECT_EQ(imp::to_string(u'A'), "A");
EXPECT_EQ(imp::to_string(U'A'), "A");
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, 'A')); ctx.finish();
EXPECT_EQ(joined, "A");
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, L'A')); ctx.finish();
EXPECT_EQ(joined, "A");
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, u'A')); ctx.finish();
EXPECT_EQ(joined, "A");
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, U'A')); ctx.finish();
EXPECT_EQ(joined, "A");
/// @brief numeric
EXPECT_EQ(imp::to_string((signed char)123), "123");
EXPECT_EQ(imp::to_string((signed char)-321), "-65");
EXPECT_EQ(imp::to_string((unsigned char)123), "123");
EXPECT_EQ(imp::to_string((unsigned char)321), "65");
EXPECT_EQ(imp::to_string((short)123), "123");
EXPECT_EQ(imp::to_string((short)-321), "-321");
EXPECT_EQ(imp::to_string((unsigned short)123), "123");
EXPECT_EQ(imp::to_string((unsigned short)321), "321");
EXPECT_EQ(imp::to_string((short)123123), "-7949");
EXPECT_EQ(imp::to_string((short)-321321), "6359");
EXPECT_EQ(imp::to_string((unsigned short)123123), "57587");
EXPECT_EQ(imp::to_string((unsigned short)321321), "59177");
EXPECT_EQ(imp::to_string(123123), "123123");
EXPECT_EQ(imp::to_string(-321321), "-321321");
EXPECT_EQ(imp::to_string(123123u), "123123");
EXPECT_EQ(imp::to_string(321321u), "321321");
EXPECT_EQ(imp::to_string(123123ll), "123123");
EXPECT_EQ(imp::to_string(-321321ll), "-321321");
EXPECT_EQ(imp::to_string(123123ull), "123123");
EXPECT_EQ(imp::to_string(321321ull), "321321");
EXPECT_EQ(imp::to_string(123123, "x"), "1e0f3");
EXPECT_EQ(imp::to_string(123123u, "x"), "1e0f3");
EXPECT_EQ(imp::to_string(123123123123ll, "X"), "1CAAB5C3B3");
EXPECT_EQ(imp::to_string(123123123123ull, "X"), "1CAAB5C3B3");
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, (signed char)123)); ctx.finish(); EXPECT_EQ(joined, "123");
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, (signed char)-321)); ctx.finish(); EXPECT_EQ(joined, "-65");
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, (unsigned char)123)); ctx.finish(); EXPECT_EQ(joined, "123");
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, (unsigned char)321)); ctx.finish(); EXPECT_EQ(joined, "65");
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, (short)123)); ctx.finish(); EXPECT_EQ(joined, "123");
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, (short)-321)); ctx.finish(); EXPECT_EQ(joined, "-321");
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, (unsigned short)123)); ctx.finish(); EXPECT_EQ(joined, "123");
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, (unsigned short)321)); ctx.finish(); EXPECT_EQ(joined, "321");
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, (short)123123)); ctx.finish(); EXPECT_EQ(joined, "-7949");
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, (short)-321321)); ctx.finish(); EXPECT_EQ(joined, "6359");
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, (unsigned short)123123)); ctx.finish(); EXPECT_EQ(joined, "57587");
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, (unsigned short)321321)); ctx.finish(); EXPECT_EQ(joined, "59177");
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, 123123)); ctx.finish(); EXPECT_EQ(joined, "123123");
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, -321321)); ctx.finish(); EXPECT_EQ(joined, "-321321");
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, 123123u)); ctx.finish(); EXPECT_EQ(joined, "123123");
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, 321321u)); ctx.finish(); EXPECT_EQ(joined, "321321");
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, 123123ll)); ctx.finish(); EXPECT_EQ(joined, "123123");
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, -321321ll)); ctx.finish(); EXPECT_EQ(joined, "-321321");
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, 123123ull)); ctx.finish(); EXPECT_EQ(joined, "123123");
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, 321321ull)); ctx.finish(); EXPECT_EQ(joined, "321321");
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, 123123, "x")); ctx.finish(); EXPECT_EQ(joined, "1e0f3");
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, 123123u, "x")); ctx.finish(); EXPECT_EQ(joined, "1e0f3");
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, 123123123123ll, "X")); ctx.finish(); EXPECT_EQ(joined, "1CAAB5C3B3");
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, 123123123123ull, "X")); ctx.finish(); EXPECT_EQ(joined, "1CAAB5C3B3");
/// @brief floating point
EXPECT_EQ(imp::to_string(123.123f, ".3"), "123.123");
EXPECT_EQ(imp::to_string(123.123, "010.5"), "0123.12300");
EXPECT_EQ(imp::to_string(123.123l, "010.6"), "123.123000");
EXPECT_EQ(imp::to_string(1.5, "e"), "1.500000e+00");
EXPECT_EQ(imp::to_string(1.5, "E"), "1.500000E+00");
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, 123.123f, ".3")); ctx.finish(); EXPECT_EQ(joined, "123.123");
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, 123.123, "010.5")); ctx.finish(); EXPECT_EQ(joined, "0123.12300");
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, 123.123l, "010.6")); ctx.finish(); EXPECT_EQ(joined, "123.123000");
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, 1.5, "e")); ctx.finish(); EXPECT_EQ(joined, "1.500000e+00");
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, 1.5, "E")); ctx.finish(); EXPECT_EQ(joined, "1.500000E+00");
double r = 0.0;
std::cout << imp::to_string(0.0/r) << "\n";
std::cout << imp::to_string(1.0/r) << "\n";
SUCCEED();
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, 0.0/r)); ctx.finish();
std::cout << joined << "\n";
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, 1.0/r)); ctx.finish();
std::cout << joined << "\n";
/// @brief pointer
EXPECT_EQ(imp::to_string(nullptr), "null");
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, nullptr)); ctx.finish(); EXPECT_EQ(joined, "null");
int *p = (int *)0x0f013a04;
std::cout << imp::to_string((void *)p) << "\n";
SUCCEED();
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, (void *)p)); ctx.finish();
std::cout << joined << "\n";
/// @brief date and time
auto tp = std::chrono::system_clock::now();
auto tt = std::chrono::system_clock::to_time_t(tp);
auto tm = *std::localtime(&tt);
std::cout << imp::to_string(tm) << "\n";
EXPECT_EQ(imp::to_string(tm), imp::to_string(tp));
SUCCEED();
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, tm)); ctx.finish();
std::cout << joined << "\n";
std::string tm_str = joined;
ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, tp)); ctx.finish();
EXPECT_EQ(tm_str, joined);
}
TEST(fmt, fmt) {
@ -87,7 +102,7 @@ namespace {
class foo {};
std::string tag_invoke(decltype(imp::fmt_to_string), foo arg) noexcept(false) {
bool tag_invoke(decltype(imp::fmt_to), imp::fmt_context &, foo arg) noexcept(false) {
throw arg;
return {};
}
@ -95,5 +110,5 @@ std::string tag_invoke(decltype(imp::fmt_to_string), foo arg) noexcept(false) {
} // namespace
TEST(fmt, throw) {
EXPECT_THROW(imp::fmt(foo{}), foo);
EXPECT_THROW(std::ignore = imp::fmt(foo{}), foo);
}

View File

@ -26,9 +26,6 @@ TEST(system, error_code) {
imp::sys::error e_obj {err};
EXPECT_EQ(err.value(), e_obj.value());
auto e_msg = imp::fmt(imp::sys::error_msg(imp::sys::error_code()));
std::stringstream ss;
ss << imp::sys::error{};
EXPECT_EQ(e_msg, ss.str());
EXPECT_EQ(e_msg, imp::fmt(imp::sys::error()));
std::cout << e_msg << "\n";
}