diff --git a/benchmark/benchmark_fmt.cpp b/benchmark/benchmark_fmt.cpp new file mode 100644 index 0000000..0693203 --- /dev/null +++ b/benchmark/benchmark_fmt.cpp @@ -0,0 +1,69 @@ +#include + +#include "benchmark/benchmark.h" + +#include "fmt/format.h" +#include "fmt/chrono.h" + +#include "libimp/fmt.h" + +namespace { + +void imp_fmt_string(benchmark::State &state) { + for (auto _ : state) { + 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 imp_fmt_int(benchmark::State &state) { + for (auto _ : state) { + imp::fmt(654321); + } +} + +void fmt_format_int(benchmark::State &state) { + for (auto _ : state) { + std::ignore = fmt::format("{}", 654321); + } +} + +void imp_fmt_float(benchmark::State &state) { + for (auto _ : state) { + imp::fmt(654.321); + } +} + +void fmt_format_float(benchmark::State &state) { + for (auto _ : state) { + std::ignore = fmt::format("{}", 654.321); + } +} + +void imp_fmt_chrono(benchmark::State &state) { + for (auto _ : state) { + imp::fmt(std::chrono::system_clock::now()); + } +} + +void fmt_format_chrono(benchmark::State &state) { + for (auto _ : state) { + std::ignore = fmt::format("{}", std::chrono::system_clock::now()); + } +} + +} // namespace + +BENCHMARK(imp_fmt_string); +BENCHMARK(fmt_format_string); +BENCHMARK(imp_fmt_int); +BENCHMARK(fmt_format_int); +BENCHMARK(imp_fmt_float); +BENCHMARK(fmt_format_float); +BENCHMARK(imp_fmt_chrono); +BENCHMARK(fmt_format_chrono); diff --git a/benchmark/benchmark_log.cpp b/benchmark/benchmark_log.cpp index e7db362..9f5fe09 100644 --- a/benchmark/benchmark_log.cpp +++ b/benchmark/benchmark_log.cpp @@ -5,14 +5,14 @@ namespace { -void BM_imp_log_no_output(benchmark::State& state) { +void imp_log_no_output(benchmark::State &state) { imp::log::grip log {__func__, {}}; for (auto _ : state) { log.debug("hello log."); } } -void BM_imp_log_gripper(benchmark::State& state) { +void imp_log_gripper(benchmark::State &state) { imp::log::grip log {__func__, {}}; for (auto _ : state) { log.info("hello log."); @@ -21,5 +21,5 @@ void BM_imp_log_gripper(benchmark::State& state) { } // namespace -BENCHMARK(BM_imp_log_no_output); -BENCHMARK(BM_imp_log_gripper); \ No newline at end of file +BENCHMARK(imp_log_no_output); +BENCHMARK(imp_log_gripper); \ No newline at end of file diff --git a/include/libimp/fmt.h b/include/libimp/fmt.h index 67f32cc..89dc7b7 100644 --- a/include/libimp/fmt.h +++ b/include/libimp/fmt.h @@ -3,6 +3,9 @@ * @author mutouyun (orz@orzz.org) * @brief String formatting. * @date 2022-11-26 + * + * @remarks The current performance is not high, + * because I use std::sprintf directly for formatting for convenience. */ #pragma once @@ -11,7 +14,7 @@ #include #include // std::chrono::time_point #include -#include // std::tm +#include // std::tm, std::localtime #include "libimp/def.h" #include "libimp/fmt_cpo.h" @@ -21,12 +24,23 @@ LIBIMP_NAMESPACE_BEG_ +/** + * @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 { @@ -35,9 +49,16 @@ auto spec(char const (&fstr)[N]) noexcept { }; } -template -std::string fmt(A &&...args) { - std::string joined; +/// @brief String formatting function. + +template +std::string fmt(A &&a) { + return fmt_to_string(std::forward(a)); +} + +template +std::string fmt(A1 &&a1, A &&...args) { + std::string joined(fmt(std::forward(a1))); LIBIMP_UNUSED auto unfold = { joined.append(fmt_to_string(std::forward(args)))... }; @@ -45,24 +66,26 @@ std::string fmt(A &&...args) { } /// @brief Return the string directly. -LIBIMP_EXPORT std::string to_string(std::string const &a) noexcept; -LIBIMP_EXPORT std::string to_string(std::string &&a) noexcept; -LIBIMP_EXPORT std::string to_string(std::string const &a, span fstr) noexcept; +inline char const *to_string(char const *a) noexcept { return (a == nullptr) ? "" : a; } +template +inline char const *to_string(char const (&a)[N]) noexcept { return 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 fstr) noexcept; +inline std::string to_string(std::string const &a, span fstr) noexcept { return to_string(a.c_str(), fstr); } /// @brief Character to string conversion. /// @return an empty string if the conversion fails -LIBIMP_EXPORT std::string to_string(char a) noexcept; +inline std::string to_string(char a) noexcept { return {a}; } 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; #if defined(LIBIMP_CPP_20) -LIBIMP_EXPORT std::string to_string(char8_t a) noexcept; +LIBIMP_EXPORT std::string to_string(char8_t a) noexcept { return to_string((char)a); } #endif // defined(LIBIMP_CPP_20) /// @brief Conversion of numeric types to strings. /// @return an empty string if the conversion fails -LIBIMP_EXPORT std::string to_string(signed char a, span fstr = {}) noexcept; -LIBIMP_EXPORT std::string to_string(unsigned char a, span fstr = {}) noexcept; LIBIMP_EXPORT std::string to_string(signed short a, span fstr = {}) noexcept; LIBIMP_EXPORT std::string to_string(unsigned short a, span fstr = {}) noexcept; LIBIMP_EXPORT std::string to_string(signed int a, span fstr = {}) noexcept; @@ -71,26 +94,47 @@ LIBIMP_EXPORT std::string to_string(signed long a, span fstr = {}) n LIBIMP_EXPORT std::string to_string(unsigned long a, span fstr = {}) noexcept; LIBIMP_EXPORT std::string to_string(signed long long a, span fstr = {}) noexcept; LIBIMP_EXPORT std::string to_string(unsigned long long a, span fstr = {}) noexcept; +inline std::string to_string( signed char a, span fstr = {}) noexcept { return to_string( (int)a, fstr); } +inline std::string to_string(unsigned char a, span fstr = {}) noexcept { return to_string((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(float a, span fstr = {}) noexcept; LIBIMP_EXPORT std::string to_string(double a, span fstr = {}) noexcept; LIBIMP_EXPORT std::string to_string(long double a, span fstr = {}) noexcept; +inline std::string to_string(float a, span fstr = {}) noexcept { return to_string((double)a, fstr); } /// @brief Pointer. -LIBIMP_EXPORT std::string to_string(std::nullptr_t) noexcept; +inline std::string to_string(std::nullptr_t) noexcept { return "null"; } template ::value>> LIBIMP_EXPORT std::string to_string(T const volatile *a) noexcept; /// @brief Date and time. LIBIMP_EXPORT std::string to_string(std::tm const &a, span fstr = {}) noexcept; + namespace detail { -LIBIMP_EXPORT std::string time_to_string(std::time_t tt, span fstr) noexcept; + +/** + * @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 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); +#else + return to_string(*std::localtime(&tt), fstr); +#endif +} + } // namespace detail + template -LIBIMP_EXPORT std::string to_string(std::chrono::time_point const &a, span fstr = {}) noexcept { +std::string to_string(std::chrono::time_point const &a, span fstr = {}) noexcept { return detail::time_to_string(std::chrono::system_clock::to_time_t(a), fstr); } diff --git a/src/libimp/fmt.cpp b/src/libimp/fmt.cpp index 613f3ad..5a5a8e1 100644 --- a/src/libimp/fmt.cpp +++ b/src/libimp/fmt.cpp @@ -1,4 +1,3 @@ -#define _CRT_SECURE_NO_WARNINGS #include "libimp/fmt.h" #include // std::snprintf @@ -98,21 +97,9 @@ std::string sprintf(F fop, span fstr, span s, A a) noexc } // namespace -std::string to_string(std::string const &a) noexcept { - return a; -} - -std::string to_string(std::string &&a) noexcept { - return std::move(a); -} - -std::string to_string(std::string const &a, span fstr) noexcept { - if (a.empty()) return {}; - return ::LIBIMP::sprintf(fmt_of, fstr, "s", a.c_str()); -} - -std::string to_string(char a) noexcept { - return {a}; +std::string to_string(char const *a, span fstr) noexcept { + if (a == nullptr) return {}; + return ::LIBIMP::sprintf(fmt_of, fstr, "s", a); } std::string to_string(wchar_t a) noexcept { @@ -145,20 +132,6 @@ std::string to_string(char32_t a) noexcept { } } -#if defined(LIBIMP_CPP_20) -std::string to_string(char8_t a) noexcept { - return to_string((char)a); -} -#endif // defined(LIBIMP_CPP_20) - -std::string to_string(signed char a, span fstr) noexcept { - return to_string((int)a, fstr); -} - -std::string to_string(unsigned char a, span fstr) noexcept { - return to_string((unsigned)a, fstr); -} - std::string to_string(signed short a, span fstr) noexcept { return ::LIBIMP::sprintf(fmt_of_signed, fstr, "h", a); } @@ -191,10 +164,6 @@ std::string to_string(unsigned long long a, span fstr) noexcept { return ::LIBIMP::sprintf(fmt_of_unsigned, fstr, "ll", a); } -std::string to_string(float a, span fstr) noexcept { - return to_string((double)a, fstr); -} - std::string to_string(double a, span fstr) noexcept { return ::LIBIMP::sprintf(fmt_of_float, fstr, "", a); } @@ -203,10 +172,6 @@ std::string to_string(long double a, span fstr) noexcept { return ::LIBIMP::sprintf(fmt_of_float, fstr, "L", a); } -std::string to_string(std::nullptr_t) noexcept { - return "null"; -} - template <> std::string to_string(void const volatile *a) noexcept { if (a == nullptr) { @@ -228,12 +193,4 @@ std::string to_string(std::tm const &a, span fstr) noexcept { } } -namespace detail { - -std::string time_to_string(std::time_t tt, span fstr) noexcept { - return to_string(*std::localtime(&tt), fstr); -} - -} // namespace detail - LIBIMP_NAMESPACE_END_