/** * \file libipc/fmt.h * \author mutouyun (orz@orzz.org) * \brief String formatting. * * \remarks The current performance is not high, * because I use std::sprintf directly for formatting for convenience. */ #pragma once #include #include #include #include // std::chrono::time_point #include #include #include // std::tm, std::localtime #include "libipc/imp/fmt_cpo.h" #include "libipc/imp/span.h" #include "libipc/imp/detect_plat.h" #include "libipc/imp/export.h" namespace ipc { /** * \brief The format string reference wrapper. */ template struct fmt_ref { span fstr; T param; }; /** * \brief Conversion specifiers. * * \remarks Just like printf, the format string is of the form * [flags][field_width][.precision][conversion_character] * * \see http://personal.ee.surrey.ac.uk/Personal/R.Bowden/C/printf.html */ template auto spec(char const (&fstr)[N]) noexcept { return [&fstr](auto &&arg) noexcept { using arg_t = decltype(arg); return fmt_ref {{fstr}, static_cast(arg)}; }; } /** * \brief String formatting function. * * \param args arguments that support the fmt output * \return an empty string if the fmt output fails */ template LIBIPC_NODISCARD std::string fmt(A &&...args) { std::string joined; fmt_context ctx(joined); if (fmt_to(ctx, std::forward(args)...)) { return ctx.finish() ? joined : ""; } return {}; } /// \brief String types. LIBIPC_EXPORT bool to_string(fmt_context &ctx, char const * a) noexcept; LIBIPC_EXPORT bool to_string(fmt_context &ctx, std::string const &a) noexcept; LIBIPC_EXPORT bool to_string(fmt_context &ctx, char const * a, span fstr) noexcept; inline bool to_string(fmt_context &ctx, std::string const &a, span fstr) noexcept { return to_string(ctx, a.c_str(), fstr); } /// \brief Character to string conversion. LIBIPC_EXPORT bool to_string(fmt_context &ctx, char a) noexcept; #if defined(LIBIPC_CPP_20) inline bool to_string(fmt_context &ctx, char8_t a) noexcept { return to_string(ctx, (char)a); } #endif // defined(LIBIPC_CPP_20) LIBIPC_EXPORT bool to_string(fmt_context &ctx, wchar_t a) noexcept; LIBIPC_EXPORT bool to_string(fmt_context &ctx, char16_t a) noexcept; LIBIPC_EXPORT bool to_string(fmt_context &ctx, char32_t a) noexcept; /// \brief Conversion of numeric types to strings. LIBIPC_EXPORT bool to_string(fmt_context &ctx, signed short a, span fstr = {}) noexcept; LIBIPC_EXPORT bool to_string(fmt_context &ctx, unsigned short a, span fstr = {}) noexcept; LIBIPC_EXPORT bool to_string(fmt_context &ctx, signed int a, span fstr = {}) noexcept; LIBIPC_EXPORT bool to_string(fmt_context &ctx, unsigned int a, span fstr = {}) noexcept; LIBIPC_EXPORT bool to_string(fmt_context &ctx, signed long a, span fstr = {}) noexcept; LIBIPC_EXPORT bool to_string(fmt_context &ctx, unsigned long a, span fstr = {}) noexcept; LIBIPC_EXPORT bool to_string(fmt_context &ctx, signed long long a, span fstr = {}) noexcept; LIBIPC_EXPORT bool to_string(fmt_context &ctx, unsigned long long a, span fstr = {}) noexcept; inline bool to_string(fmt_context &ctx, signed char a, span fstr = {}) noexcept { return to_string(ctx, (int)a, fstr); } inline bool to_string(fmt_context &ctx, unsigned char a, span fstr = {}) noexcept { return to_string(ctx, (unsigned)a, fstr); } /// \brief Conversion of floating point type to strings. LIBIPC_EXPORT bool to_string(fmt_context &ctx, double a, span fstr = {}) noexcept; LIBIPC_EXPORT bool to_string(fmt_context &ctx, long double a, span fstr = {}) noexcept; inline bool to_string(fmt_context &ctx, float a, span fstr = {}) noexcept { return to_string(ctx, (double)a, fstr); } /// \brief Pointer. LIBIPC_EXPORT bool to_string(fmt_context &ctx, std::nullptr_t) noexcept; template ::value>> LIBIPC_EXPORT bool to_string(fmt_context &ctx, T const volatile *a) noexcept; /// \brief Date and time. LIBIPC_EXPORT bool to_string(fmt_context &ctx, std::tm const &a, span fstr = {}) noexcept; namespace detail_fmt { /** * \brief Convert std::time_t to std::string. * \return an empty string if the conversion fails */ inline bool time_to_string(fmt_context &ctx, std::time_t tt, span fstr) noexcept { #if defined(LIBIPC_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(ctx, tm, fstr); #else return to_string(ctx, *std::localtime(&tt), fstr); #endif } } // namespace detail_fmt template bool to_string(fmt_context &ctx, std::chrono::time_point const &a, span fstr = {}) noexcept { return detail_fmt::time_to_string(ctx, std::chrono::system_clock::to_time_t(a), fstr); } /** * \brief Predefined fmt_to method */ namespace detail_tag_invoke { template auto tag_invoke(decltype(ipc::fmt_to), fmt_context &ctx, T &&arg) noexcept -> decltype(ipc::to_string(ctx, std::forward(arg))) { return ipc::to_string(ctx, std::forward(arg)); } template auto tag_invoke(decltype(ipc::fmt_to), fmt_context &ctx, fmt_ref arg) noexcept -> decltype(ipc::to_string(ctx, static_cast(arg.param), arg.fstr)) { return ipc::to_string(ctx, static_cast(arg.param), arg.fstr); } template bool tag_invoke(decltype(ipc::fmt_to), fmt_context &ctx, span s) { if (s.empty()) { return false; } if (!fmt_to(ctx, s[0])) { return false; } for (std::size_t i = 1; i < s.size(); ++i) { if (!fmt_to(ctx, ' ', s[i])) return false; } return true; } template bool unfold_tuple_fmt_to(fmt_context &ctx, Tp const &tp, std::index_sequence) { return fmt_to(ctx, std::get(tp)...); } template bool tag_invoke(decltype(ipc::fmt_to), fmt_context &ctx, std::tuple const &tp) { return unfold_tuple_fmt_to(ctx, tp, std::index_sequence_for{}); } } // namespace detail_tag_invoke } // namespace ipc