mirror of
https://github.com/mutouyun/cpp-ipc.git
synced 2025-12-06 16:56:45 +08:00
add: [imp] fmt
This commit is contained in:
parent
44fe8ce8da
commit
bbbb03408d
95
include/libimp/fmt.h
Normal file
95
include/libimp/fmt.h
Normal file
@ -0,0 +1,95 @@
|
||||
/**
|
||||
* @file libimp/fmt.h
|
||||
* @author mutouyun (orz@orzz.org)
|
||||
* @brief String formatting.
|
||||
* @date 2022-11-26
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
#include <chrono> // std::chrono::time_point
|
||||
#include <cstddef>
|
||||
#include <ctime> // std::tm
|
||||
|
||||
#include "libimp/def.h"
|
||||
#include "libimp/span.h"
|
||||
#include "libimp/detect_plat.h"
|
||||
#include "libimp/export.h"
|
||||
|
||||
LIBIMP_NAMESPACE_BEG_
|
||||
|
||||
template <typename T>
|
||||
struct fmt_ref {
|
||||
span<char const> fstr;
|
||||
T param;
|
||||
};
|
||||
|
||||
template <std::size_t N>
|
||||
auto spec(char const (&fstr)[N]) noexcept {
|
||||
return [&fstr](auto &&arg) noexcept {
|
||||
using arg_t = decltype(arg);
|
||||
return fmt_ref<arg_t> {{fstr}, static_cast<arg_t>(arg)};
|
||||
};
|
||||
}
|
||||
|
||||
template <typename... A>
|
||||
std::string fmt(A &&...args) {
|
||||
std::string joined;
|
||||
LIBIMP_UNUSED auto unfold = {
|
||||
joined.append(to_string(std::forward<A>(args)))...
|
||||
};
|
||||
return joined;
|
||||
}
|
||||
|
||||
/// @brief Return the string directly.
|
||||
LIBIMP_EXPORT std::string const &to_string(std::string const &a) noexcept;
|
||||
LIBIMP_EXPORT std::string to_string(std::string const &a, span<char const> fstr) noexcept;
|
||||
|
||||
/// @brief Character to string conversion.
|
||||
/// @return an empty string if the conversion fails
|
||||
LIBIMP_EXPORT std::string to_string(char a) noexcept;
|
||||
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;
|
||||
#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<char const> fstr = {}) noexcept;
|
||||
LIBIMP_EXPORT std::string to_string(unsigned char a, span<char const> fstr = {}) noexcept;
|
||||
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;
|
||||
|
||||
/// @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<char const> fstr = {}) noexcept;
|
||||
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;
|
||||
|
||||
/// @brief Pointer.
|
||||
LIBIMP_EXPORT std::string to_string(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 *a) noexcept;
|
||||
|
||||
/// @brief Date and time.
|
||||
LIBIMP_EXPORT std::string to_string(std::tm const &a, span<char const> fstr = {}) noexcept;
|
||||
namespace detail {
|
||||
LIBIMP_EXPORT std::string time_to_string(std::time_t tt, span<char const> fstr) noexcept;
|
||||
} // namespace detail
|
||||
template <class Clock, class Duration>
|
||||
LIBIMP_EXPORT 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);
|
||||
}
|
||||
|
||||
LIBIMP_NAMESPACE_END_
|
||||
235
src/libimp/fmt.cpp
Normal file
235
src/libimp/fmt.cpp
Normal file
@ -0,0 +1,235 @@
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#include "libimp/fmt.h"
|
||||
|
||||
#include <cstdio> // std::snprintf
|
||||
#include <iomanip> // std::put_time
|
||||
#include <sstream> // std::ostringstream
|
||||
|
||||
#include "libimp/codecvt.h"
|
||||
|
||||
LIBIMP_NAMESPACE_BEG_
|
||||
|
||||
/**
|
||||
* @brief Format conversions helpers.
|
||||
* @see http://personal.ee.surrey.ac.uk/Personal/R.Bowden/C/printf.html
|
||||
* https://en.cppreference.com/w/cpp/io/c/fprintf
|
||||
*/
|
||||
namespace {
|
||||
|
||||
span<char const> normalize(span<char const> a) {
|
||||
if (a.empty()) return {};
|
||||
return a.first(a.size() - (a.back() == '\0' ? 1 : 0));
|
||||
}
|
||||
|
||||
std::string as_string(span<char const> a) {
|
||||
if (a.empty()) return {};
|
||||
a = normalize(a);
|
||||
return std::string(a.data(), a.size());
|
||||
}
|
||||
|
||||
std::string fmt_of(span<char const> fstr, span<char const> s) {
|
||||
return '%' + as_string(fstr) + as_string(s);
|
||||
}
|
||||
|
||||
std::string fmt_of_unsigned(span<char const> fstr, span<char const> l) {
|
||||
if (fstr.empty()) {
|
||||
return fmt_of(l, "u");
|
||||
}
|
||||
fstr = normalize(fstr);
|
||||
switch (fstr.back()) {
|
||||
case 'o':
|
||||
case 'x':
|
||||
case 'X':
|
||||
case 'u': return fmt_of(fstr.first(fstr.size() - 1), l) + fstr.back();
|
||||
default : return fmt_of(fstr, l) + 'u';
|
||||
}
|
||||
}
|
||||
|
||||
std::string fmt_of_signed(span<char const> fstr, span<char const> l) {
|
||||
if (fstr.empty()) {
|
||||
return fmt_of(l, "d");
|
||||
}
|
||||
fstr = normalize(fstr);
|
||||
switch (fstr.back()) {
|
||||
case 'o':
|
||||
case 'x':
|
||||
case 'X':
|
||||
case 'u': return fmt_of_unsigned(fstr, l);
|
||||
default : return fmt_of(fstr, l) + 'd';
|
||||
}
|
||||
}
|
||||
|
||||
std::string fmt_of_float(span<char const> fstr, span<char const> l) {
|
||||
if (fstr.empty()) {
|
||||
return fmt_of(l, "f");
|
||||
}
|
||||
fstr = normalize(fstr);
|
||||
switch (fstr.back()) {
|
||||
case 'e':
|
||||
case 'E':
|
||||
case 'g':
|
||||
case 'G': return fmt_of(fstr.first(fstr.size() - 1), l) + fstr.back();
|
||||
default : return fmt_of(fstr, l) + 'f';
|
||||
}
|
||||
}
|
||||
|
||||
template <typename A /*a fundamental or pointer type*/>
|
||||
std::string sprintf(std::string const &sfmt, A a) {
|
||||
auto sz = std::snprintf(nullptr, 0, sfmt.c_str(), a);
|
||||
if (sz <= 0) return {};
|
||||
std::string des;
|
||||
des.resize(sz + 1);
|
||||
if (std::snprintf(&des[0], des.size(), sfmt.c_str(), 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 {
|
||||
LIBIMP_TRY {
|
||||
return ::LIBIMP::sprintf(fop(fstr, s), a);
|
||||
} LIBIMP_CATCH(...) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::string const &to_string(std::string const &a) noexcept {
|
||||
return a;
|
||||
}
|
||||
|
||||
std::string to_string(std::string const &a, span<char const> 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(wchar_t a) noexcept {
|
||||
LIBIMP_TRY {
|
||||
std::string des;
|
||||
cvt_sstr(std::wstring{a}, des);
|
||||
return des;
|
||||
} LIBIMP_CATCH(...) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
std::string to_string(char16_t a) noexcept {
|
||||
LIBIMP_TRY {
|
||||
std::string des;
|
||||
cvt_sstr(std::u16string{a}, des);
|
||||
return des;
|
||||
} LIBIMP_CATCH(...) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
std::string to_string(char32_t a) noexcept {
|
||||
LIBIMP_TRY {
|
||||
std::string des;
|
||||
cvt_sstr(std::u32string{a}, des);
|
||||
return des;
|
||||
} LIBIMP_CATCH(...) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
#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<char const> fstr) noexcept {
|
||||
return to_string((int)a, fstr);
|
||||
}
|
||||
|
||||
std::string to_string(unsigned char a, span<char const> fstr) noexcept {
|
||||
return to_string((unsigned)a, fstr);
|
||||
}
|
||||
|
||||
std::string to_string(signed short a, span<char const> fstr) noexcept {
|
||||
return ::LIBIMP::sprintf(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);
|
||||
}
|
||||
|
||||
std::string to_string(signed int a, span<char const> fstr) noexcept {
|
||||
return ::LIBIMP::sprintf(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);
|
||||
}
|
||||
|
||||
std::string to_string(signed long a, span<char const> fstr) noexcept {
|
||||
return ::LIBIMP::sprintf(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);
|
||||
}
|
||||
|
||||
std::string to_string(signed long long a, span<char const> fstr) noexcept {
|
||||
return ::LIBIMP::sprintf(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);
|
||||
}
|
||||
|
||||
std::string to_string(float a, span<char const> fstr) noexcept {
|
||||
return to_string((double)a, fstr);
|
||||
}
|
||||
|
||||
std::string to_string(double a, span<char const> fstr) noexcept {
|
||||
return ::LIBIMP::sprintf(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);
|
||||
}
|
||||
|
||||
std::string to_string(std::nullptr_t) noexcept {
|
||||
return "null";
|
||||
}
|
||||
|
||||
template <>
|
||||
std::string to_string<void, void>(void *a) noexcept {
|
||||
if (a == nullptr) {
|
||||
return to_string(nullptr);
|
||||
}
|
||||
return ::LIBIMP::sprintf(fmt_of, "", "p", a);
|
||||
}
|
||||
|
||||
std::string to_string(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_string(fstr).c_str());
|
||||
return ss.str();
|
||||
} LIBIMP_CATCH(...) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
std::string time_to_string(std::time_t tt, span<char const> fstr) noexcept {
|
||||
return to_string(*std::localtime(&tt), fstr);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
LIBIMP_NAMESPACE_END_
|
||||
87
test/imp/test_imp_fmt.cpp
Normal file
87
test/imp/test_imp_fmt.cpp
Normal file
@ -0,0 +1,87 @@
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#include <iostream>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "libimp/fmt.h"
|
||||
|
||||
TEST(fmt, operator) {
|
||||
auto a = imp::spec("hello")(123);
|
||||
EXPECT_STREQ(a.fstr.data(), "hello");
|
||||
EXPECT_EQ(a.param , 123);
|
||||
|
||||
auto b = imp::spec("hello")("world");
|
||||
EXPECT_STREQ(b.fstr.data(), "hello");
|
||||
EXPECT_STREQ(b.param , "world");
|
||||
}
|
||||
|
||||
TEST(fmt, to_string) {
|
||||
/// @brief string
|
||||
EXPECT_EQ(imp::to_string(""), "");
|
||||
EXPECT_EQ(imp::to_string("%what%"), "%what%");
|
||||
EXPECT_EQ(imp::to_string("%what%", "10") , " %what%");
|
||||
EXPECT_EQ(imp::to_string("%what%", "-10"), "%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");
|
||||
|
||||
/// @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");
|
||||
|
||||
/// @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");
|
||||
double r = 0.0;
|
||||
std::cout << imp::to_string(0.0/r) << "\n";
|
||||
std::cout << imp::to_string(1.0/r) << "\n";
|
||||
SUCCEED();
|
||||
|
||||
/// @brief pointer
|
||||
EXPECT_EQ(imp::to_string(nullptr), "null");
|
||||
int *p = (int *)0x0f013a04;
|
||||
std::cout << imp::to_string((void *)p) << "\n";
|
||||
SUCCEED();
|
||||
|
||||
/// @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();
|
||||
}
|
||||
|
||||
TEST(fmt, fmt) {
|
||||
auto s = imp::fmt("hello", " ", "world", ".");
|
||||
EXPECT_EQ(s, "hello world.");
|
||||
std::cout << imp::fmt('[', std::chrono::system_clock::now(), "] ", s) << "\n";
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user