#pragma once #ifndef ASSERT #include #define ASSERT assert #endif //----------------------------------------------------------------------------- // 字符串格式化 // 思路主要来自祈宇的文章: // <实现一个简单的字符串格式化方法> // https://www.cnblogs.com/qicosmos/p/3825612.html //----------------------------------------------------------------------------- #include #include #include #include #include #include #include #include #pragma warning (disable : 4996) //warning C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpy_s instead. // To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details. namespace ECOM::Utility::String { template constexpr bool dependent_false = false; template inline std::string ToString (const T & value) { static_assert (dependent_false , "You must implement ToString for type T with Partial template specialization !"); return std::string (); } } // 针对 bool 的模板偏特化, 编译要求定义这些函数, 但是实际上不会执行 namespace ECOM::Utility::String { template <> inline std::string ToString (const bool & value) { static const auto strTrue = std::string ("true"); static const auto strFalse = std::string ("false"); if (value) return strTrue; else return strFalse; } } namespace ECOM::Utility::String { static const char __MsgOfNull_ [] = "null"; static const int __LenOfNull_ = sizeof (__MsgOfNull_) - 1; template class __declspec(novtable) _string_format_detail { protected: char * m_BufferOrg; char * m_Buffer; int m_NbOfChar; protected: inline int NbOfRemain () const { return m_NbOfChar - int (m_Buffer - m_BufferOrg); } private: template int _my_sprintf (char * buf, int NbOfChar, const char * fmt, arg a) { assert (false); return 22; } public: inline _string_format_detail (char * buf, int NbOfChar) // 缓冲区指针, 缓冲区长度 (字节计数) { assert (buf); m_BufferOrg = m_Buffer = buf; m_NbOfChar = NbOfChar; } protected: inline void CharFormatArg (char ch) { // ch 不能是控制字符, 否则不知道显示成什么 // assert (0 < ch); assert (9 <= ch); assert (ch <= 127); *m_Buffer = ch; m_Buffer ++; } // 有符号 template inline void sDigitFormatArg (T i) { _ltoa (i, m_Buffer, 10); m_Buffer += strlen (m_Buffer); } // 无符号 template inline void uDigitFormatArg (T i) { _ultoa (i, m_Buffer, 10); m_Buffer += strlen (m_Buffer); } //< 64 位偏特化 template <> inline void sDigitFormatArg (__int64 i) { _i64toa (i, m_Buffer, 10); m_Buffer += strlen (m_Buffer); } template <> inline void uDigitFormatArg (unsigned __int64 i) { _ui64toa (i, m_Buffer, 10); m_Buffer += strlen (m_Buffer); } //> template // fmt: D,X, D3,X4,d3,x4, D03,X04 // D - 用 10 进制, X - 用 16 进制 // D3 - 转换成 3 个数字, 如果不足, 填充空格 // D03 - 转换成 3 个数字, 如果不足, 用 0 填充 inline void DigitFormatArg (T i, const char * fmt) { assert (fmt); assert (fmt [0]); auto chfmt = fmt [0]; if (chfmt == 'c' || chfmt == 'C') { CharFormatArg ((char) i); return; } if (chfmt == 'D') chfmt = 'd'; // 把大写的 D 改成小写的 d, 因为 sprintf 不支持大写 D if (chfmt == 'U') chfmt = 'u'; // 把大写的 U 改成小写的 u, 因为 sprintf 不支持大写 U auto rc = (chfmt == 'd' || chfmt == 'u' || chfmt == 'X' || chfmt == 'x'); if (! rc) // 尝试浮点数, 万一使用者搞错了 { rc = (chfmt == 'f' || chfmt == 'F'); if (rc) { FormatArg ((double) i, fmt); return; } } assert (rc); if (! rc) return; // 既不是 d, 也不是 u, 也不是 x, 也不是 c char tofmt [8] = { '%', }; // D023, X093 - 1 个格式字母带 3 个数字, tofmt 将成为 "%023d" 或 "%093x" // 最多支持 3 个数字. 注意, 如果判断到 \0 也不怕 // d1, d3 - 1 个格式字母带 1 个数字, tofmt 将成为 "%1d" 或 "%3d" if (std::isdigit (fmt [1])) { int iFmtChar = 2; for (int Index = 1; Index <= 3; Index ++) { if (std::isdigit (fmt [Index])) { tofmt [Index] = fmt [Index]; iFmtChar = Index + 1; } else break; } tofmt [iFmtChar] = chfmt; } else // 只有 1 个 d, 比如写成 {$:d} { tofmt [1] = chfmt; tofmt [2] = 0; } auto pT = static_cast (this); auto r = pT->_my_sprintf (m_Buffer, NbOfRemain (), tofmt, i); m_Buffer += r; } //< 64 位偏特化 template <> // fmt: D,X, D3,X4,d3,x4, D03,X04 // __int64 只支持 d, x, 不支持前导/后导, 因此下面的代码只用了 D 或 X inline void DigitFormatArg (__int64 i, const char * fmt) { assert (fmt); assert (fmt [0]); auto chfmt = fmt [0]; if (chfmt == 'c' || chfmt == 'C') { CharFormatArg ((char) i); return; } if (chfmt == 'D') chfmt = 'd'; // 把大写的 D 改成小写的 d, 因为 sprintf 不支持大写 D if (chfmt == 'U') chfmt = 'u'; // 把大写的 U 改成小写的 u, 因为 sprintf 不支持大写 U auto rc = (chfmt == 'd' || chfmt == 'u' || chfmt == 'X' || chfmt == 'x'); assert (rc); if (! rc) return; // 既不是 d, 也不是 u, 也不是 x, 也不是 c char tofmt [8] = { '%', 'I', 'I', chfmt }; auto pT = static_cast (this); auto r = pT->_my_sprintf (m_Buffer, NbOfRemain (), tofmt, i); m_Buffer += r; } // 64 位偏特化 template <> // fmt: D,X, D3,X4,d3,x4, D03,X04 // __int64 只支持 d, x, 不支持前导/后导, 因此下面的代码只用了 D 或 X inline void DigitFormatArg (unsigned __int64 i, const char * fmt) { DigitFormatArg <__int64> ((__int64) i, fmt); } //> // 字符,不带格式, 就认为是字符 inline void FormatArg (char ch) { CharFormatArg (ch); } // inline void FormatArg (unsigned char ch) { CharFormatArg (ch); } inline void FormatArg (unsigned char ch) { uDigitFormatArg (ch); } // 字符,带格式, 就认为是整数 inline void FormatArg (char ch, const char * fmt) { DigitFormatArg (ch, fmt); } inline void FormatArg (unsigned char ch, const char * fmt) { DigitFormatArg (ch, fmt); } //< 各种整数 inline void FormatArg (int i) { sDigitFormatArg (i); } inline void FormatArg (long i) { sDigitFormatArg (i); } inline void FormatArg (short i) { sDigitFormatArg (i); } inline void FormatArg (unsigned int i) { uDigitFormatArg (i); } inline void FormatArg (unsigned long i) { uDigitFormatArg (i); } inline void FormatArg (unsigned short i) { uDigitFormatArg (i); } inline void FormatArg (int i , const char * fmt) { DigitFormatArg (i, fmt); } inline void FormatArg (unsigned int i , const char * fmt) { DigitFormatArg (i, fmt); } inline void FormatArg (long i , const char * fmt) { DigitFormatArg (i, fmt); } inline void FormatArg (unsigned long i , const char * fmt) { DigitFormatArg (i, fmt); } inline void FormatArg (short i , const char * fmt) { DigitFormatArg (i, fmt); } inline void FormatArg (unsigned short i, const char * fmt) { DigitFormatArg (i, fmt); } // int64 inline void FormatArg (__int64 i) { sDigitFormatArg <__int64> (i); } inline void FormatArg (unsigned __int64 i) { uDigitFormatArg (i); } // int64, 带参数 inline void FormatArg (__int64 i , const char * fmt) { DigitFormatArg <__int64> (i, fmt); } inline void FormatArg (unsigned __int64 i , const char * fmt) { DigitFormatArg (i, fmt); } //> //< 浮点数, 默认精度由 printf 决定 inline void FormatArg (double i) { auto pT = static_cast (this); auto r = pT->_my_sprintf (m_Buffer, NbOfRemain (), "%f", i); m_Buffer += r; } // fmt: F3, 精确到小数点后面3位 // fmt: F, 默认精度由 printf 决定 // fmt : f} --> tofmt : %f // fmt : f3} --> tofmt : %.3f // fmt : f23} --> tofmt : %.23f inline void FormatArg (double i, const char * fmt) { assert (fmt); assert (fmt [0]); char tofmt [8] = { '%', '.', }; auto chfmt = fmt [0]; // 最多支持 3 个数字. 注意, 如果判断到 \0 也不怕 // f1, f3 - 1 个格式字母带 1 个数字, tofmt 将成为 "%.1f" 或 "%.3f" if (std::isdigit (fmt [1])) { int iFmtChar = 2; for (int Index = 1; Index <= 3; Index ++) { if (std::isdigit (fmt [Index])) { tofmt [Index+1] = fmt [Index]; iFmtChar = Index + 2; } else break; } tofmt [iFmtChar] = chfmt; } else // 只有 1 个 f, 比如写成 {$:f} { tofmt [1] = chfmt; tofmt [2] = 0; } auto pT = static_cast (this); auto r = pT->_my_sprintf (m_Buffer, NbOfRemain (), tofmt, i); m_Buffer += r; } inline void FormatArg (float f) { double d = f; FormatArg (d); } inline void FormatArg (float f, const char * fmt) { double d = f; FormatArg (d, fmt); } //> // 字符串, 无格式 inline void FormatArg (const char * p) { if (p == nullptr) { memcpy (m_Buffer, __MsgOfNull_, __LenOfNull_); m_Buffer += __LenOfNull_; return; } int len = (int) strlen (p); memcpy (m_Buffer, p, len); m_Buffer += len; } //< 字符串, 带格式, 格式: // 6 : 最长 6 个字符, 右对齐, 左边填空 // +6 : 最长 6 个字符, 右对齐, 左边填空 // -6 : 最长 6 个字符, 左对齐, 右边填空 inline void FormatArg (const char * p, const char * fmt) { if (p == nullptr) { memcpy (m_Buffer, __MsgOfNull_, __LenOfNull_); m_Buffer += __LenOfNull_; return; } int len = (int) strlen (p); _StringFormatArg (p, len, fmt); } inline void FormatArg (char * p) { FormatArg ((const char *) p); } inline void FormatArg (char * p, const char * fmt) { FormatArg ((const char *)p, fmt); } inline void _StringFormatArg (const char * p, int len, const char * fmt) { if (p == nullptr) { memcpy (m_Buffer, __MsgOfNull_, __LenOfNull_); m_Buffer += __LenOfNull_; return; } if (fmt [0] == '-') // 左对齐 { fmt ++; auto i = atoi (fmt); if (i > len) { memcpy (m_Buffer, p, len); m_Buffer += len; memset (m_Buffer, ' ', i - len); m_Buffer += i - len; } else { memcpy (m_Buffer, p, len); m_Buffer += len; } } else // 右对齐 { if (fmt [0] == '+') fmt ++; auto i = atoi (fmt); if (i > len) { memset (m_Buffer, ' ', i - len); m_Buffer += i - len; memcpy (m_Buffer, p, len); m_Buffer += len; } else { memcpy (m_Buffer, p, len); m_Buffer += len; } } } //> // bool void FormatArg (bool b) { if (b) { *m_Buffer = 't'; m_Buffer ++; *m_Buffer = 'r'; m_Buffer ++; *m_Buffer = 'u'; m_Buffer ++; *m_Buffer = 'e'; m_Buffer ++; } else { *m_Buffer = 'f'; m_Buffer ++; *m_Buffer = 'a'; m_Buffer ++; *m_Buffer = 'l'; m_Buffer ++; *m_Buffer = 's'; m_Buffer ++; *m_Buffer = 'e'; m_Buffer ++; } } // 任意指针类型 - const template inline void FormatArg (const UNP * ptr) { auto pT = static_cast (this); auto r = pT->_my_sprintf (m_Buffer, NbOfRemain (), "%p", ptr); m_Buffer += r; } // 任意指针类型, 带格式 -- 转化成整数来格式化 template inline void FormatArg (const UNP * ptr, const char * fmt) { DigitFormatArg ((uintptr_t) ptr, fmt); } // 任意指针类型 - 非 const template inline void FormatArg (UNP * ptr) { auto pT = static_cast (this); auto r = pT->_my_sprintf (m_Buffer, NbOfRemain (), "%p", ptr); m_Buffer += r; } // 任意指针类型, 带格式 -- 转化成整数来格式化 template inline void FormatArg (UNP * ptr, const char * fmt) { DigitFormatArg ((uintptr_t)ptr, fmt); } // 偏特化 - 字符串指针 template <> inline void FormatArg (const char * p) { if (p == nullptr) { memcpy (m_Buffer, __MsgOfNull_, __LenOfNull_); m_Buffer += __LenOfNull_; return; } int len = (int) strlen (p); memcpy (m_Buffer, p, len); m_Buffer += len; } // 偏特化 - 字符串指针 template <> inline void FormatArg (const char * p, const char *) { if (p == nullptr) { memcpy (m_Buffer, __MsgOfNull_, __LenOfNull_); m_Buffer += __LenOfNull_; return; } int len = (int) strlen (p); memcpy (m_Buffer, p, len); m_Buffer += len; } protected: //< string_view inline void FormatArg (const std::string_view & s) { memcpy (m_Buffer, s.data (), s.size ()); m_Buffer += s.size (); } inline void FormatArg (const std::string_view & s, const char * fmt) { _StringFormatArg (s.data (), static_cast (s.size ()), fmt); } inline void FormatArg (const std::wstring_view & s) { std::wstring_convert > converter; auto str = converter.to_bytes (s.data (), s.data () + s.size ()); FormatArg (str); } inline void FormatArg (const std::wstring_view & s, const char * fmt) { std::wstring_convert > converter; auto str = converter.to_bytes (s.data (), s.data () + s.size ()); FormatArg (str, fmt); } //> #ifdef __Utility_String_StringView__ inline void FormatArg (const DStringView & s) { memcpy (m_Buffer, s.constBuffer (), s.Length ()); m_Buffer += s.Length (); } inline void FormatArg (const DStringView & s, const char * fmt) { _StringFormatArg (s.constBuffer (), s.Length (), fmt); } #endif #ifdef DSTRING_API inline void FormatArg (const DString & s) { memcpy (m_Buffer, s.constBuffer (), s.GetLength ()); m_Buffer += s.GetLength (); } inline void FormatArg (const DString & s, const char * fmt) { _StringFormatArg (s.constBuffer (), s.GetLength (), fmt); } #endif //< std::string inline void FormatArg (const std::string & s) { memcpy (m_Buffer, s.data (), s.size ()); m_Buffer += s.size (); } inline void FormatArg (const std::string & s, const char * fmt) { _StringFormatArg (s.data (), (int) s.size (), fmt); } //> #ifdef WSTRING_API //#if 1 inline void FormatArg (const wchar_t * p) { if (p == nullptr) { memcpy (m_Buffer, __MsgOfNull_, __LenOfNull_); m_Buffer += __LenOfNull_; return; } auto dp = WString (WStringView (p)).ToDString (); FormatArg (dp); } inline void FormatArg (const wchar_t * p, const char * fmt) { if (p == nullptr) { memcpy (m_Buffer, __MsgOfNull_, __LenOfNull_); m_Buffer += __LenOfNull_; return; } auto dp = WString (WStringView (p)).ToDString (); FormatArg (dp, fmt); } inline void FormatArg (const WString & s) { auto dp = s.ToDString (); FormatArg (dp); } inline void FormatArg (const WString & s, const char * fmt) { auto dp = s.ToDString (); FormatArg (dp, fmt); } inline void FormatArg (const std::wstring & s) { auto dp = WString (s.c_str ()).ToDString (); FormatArg (dp); } inline void FormatArg (const std::wstring & s, const char * fmt) { auto dp = WString (s.c_str ()).ToDString (); FormatArg (dp, fmt); } inline void FormatArg (const WStringView & ws) { auto ds = WString (ws).ToDString (); FormatArg (ds); } inline void FormatArg (const WStringView & ws, const char * fmt) { auto ds = WString (ws).ToDString (); FormatArg (ds, fmt); } #endif #if 1 template inline void FormatArg (const TO & _val) { auto dp = ECOM::Utility::String::ToString (_val); FormatArg (dp); } template inline void FormatArg (const TO & _val, const char * fmt) { auto dp = ECOM::Utility::String::ToString (_val); FormatArg (dp, fmt); } #endif protected: template typename std::enable_if <(k == std::tuple_size ::value)>::type inline GetArgByIndex (size_t, Tuple &) { // 忽略那些误写的位置参数, 比如忽略下面的 {99} // mLog::Info ("{99}, {0}", 123456 #ifdef _DEBUG // throw std::invalid_argument ("arg index out of range"); #endif } template typename std::enable_if <(k < std::tuple_size ::value)>::type inline GetArgByIndex (size_t index, Tuple & tp) { if (k == index) { FormatArg (std::get (tp)); } else { GetArgByIndex (index, tp); } } template typename std::enable_if <(k == std::tuple_size ::value)>::type inline GetArgByIndex (size_t, Tuple &, const char * fmt) { #ifdef _DEBUG // throw std::invalid_argument ("arg index out of range"); #endif } template typename std::enable_if <(k < std::tuple_size ::value)>::type inline GetArgByIndex (size_t index, Tuple & tp, const char * fmt) { if (k == index) { FormatArg (std::get (tp), fmt); } else { GetArgByIndex (index, tp, fmt); } } static inline const char * FirstDollar (const char *& p) { while (*p != '}' && *p != '\0') { if ('$' == (*p)) return p; p ++; } return nullptr; } static inline const char * FirstDigit (const char *& p) { while (*p != '}' && *p != '\0') { if (std::isdigit (*p)) return p; p ++; } return nullptr; } static inline const char * LastDigit (const char *& p) { while (*p != '}' && *p != '\0') { if (! std::isdigit (*p)) return p - 1; p ++; } return p - 1; } static inline const char * FindColon (const char *& p) { while (*p != '}' && *p != '\0') { if (*p == ':') return p; p++; } return nullptr; } static inline const char * FindRightBrace (const char *& p) { while (*p != '\0') { if (*p == '}') return p; p++; } return nullptr; } static inline int GetIndex (const char *& p, const char *& fmt, int prevIndex) { auto bp = p; // 备份一下, 因为后面跟的可能是 $, 也可能是数字 auto pDollar = FirstDollar (p); if (pDollar) { auto pc = FindColon (p); auto _ = FindRightBrace (p); if (pc) fmt = pc + 1; return prevIndex + 1; } p = bp; auto pd = FirstDigit (p); auto pl = LastDigit (p); auto pc = FindColon (p); auto _ = FindRightBrace (p); if (pd == nullptr) return -1; auto ii = pl + 1 - pd; assert (ii <= 2); if (ii <= 0) return -1; char temp [3] = {}; temp [0] = *pd; pd ++; temp [1] = *pd; auto index = std::atoi (temp); if (pc) fmt = pc + 1; return index; } public: // 返回: 格式化后的字符串长度 template inline int Format (const char * fmt, Args && ... args) { int fmtlen = (int) strlen (fmt); return DoFormat (fmtlen, fmt, std::forward (args)...); } #ifdef __Utility_String_StringView__ template inline int Format (const eSTR::StringView & fmt, Args && ... args) { int fmtlen = fmt.GetLength (); return DoFormat (fmtlen, fmt, std::forward (args)...); } #endif template inline int Format (const std::string_view & fmt, Args && ... args) { int fmtlen = static_cast (fmt.size ()); return DoFormat (fmtlen, fmt, std::forward (args)...); } protected: template inline int DoFormat (int fmtlen, const char * fmt, Args && ... args) { assert (fmt); if (fmt == nullptr) { m_Buffer [0] = 0; return 0; } // 最好在上层判断, 以避免在这里重复计算 strlen if (sizeof...(args) == 0) { strcpy (m_Buffer, fmt); return fmtlen; } auto tp = std::tuple (std::forward (args)...); return DoFormat (fmtlen, fmt, std::move (tp)); } template inline int DoFormat (int fmtlen, const char * fmt, std::tuple && tp) { assert (fmt); if (fmt == nullptr) { m_Buffer [0] = 0; return 0; } m_Buffer = m_BufferOrg; const char * p = fmt; const char * original = p; int len = fmtlen + 1; int last = 0; int Index = -1; while (true) { if (*p == '{') { //copy content befor { last = int (p - original); if (last) { memcpy (m_Buffer, original, last); m_Buffer += last; } //format args const char * tmp = nullptr; Index = GetIndex (p, tmp, Index); if (Index >= 0) { if (tmp) GetArgByIndex <0> (Index, tp, tmp); else GetArgByIndex <0> (Index, tp); } //skip } original = p + 1; } else if (*p == '\0') { last = int (p - original); if (last == 0) *m_Buffer = 0; else { memcpy (m_Buffer, original, last + 1); // +1 的目的是, 包括尾部的 \0 m_Buffer += last; } break; } p++; } auto delta = m_Buffer - m_BufferOrg; #ifdef _DEBUG len = (int) strlen (m_BufferOrg); assert ((delta - len) == 0); // ! 此断言对以下语句不成立: // char ch = 0; mLog::Info ("{$}", ch); // 应改成以下语句, 即强制作为数字 // char ch = 0; mLog::Info ("{$:d2}", ch); #endif return (int) delta; // return buf_org; } }; class StringFormat : public _string_format_detail { public: inline StringFormat (char * buf, int NbOfChar) : _string_format_detail (buf, NbOfChar) { } public: template inline int _my_sprintf (char * buf, int NbOfChar, const char * fmt, arg a) { return sprintf_s (buf, NbOfChar, fmt, a); } }; } //----------------------------------------------------------------------------- // 设计说明 (2021-06-18): // 1. 为什么只能支持 3 个数字 (即 最大只能是 {$:d999} 或 {$:f999}) // 1) 预留的 Buffer 一般大小是 8192, 见 DString::Format 及 TLSLog // 2) 最终会调用 _vsprintf_s_l (buffer, len, ...) // 3) 当 buffer 不足时, 上述函数会调用 无效参数错误处理函数, // 4) 无效参数错误处理函数 : 在 Debug 模式时, 弹出断言错误. 在 Release 模式时, 直接终止程序. // //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // 自定义类型的格式化 (2023-05-15) /* class TestMyClass { public: TestMyClass () = default; }; namespace ECOM::Utility::String { template <> inline std::string ToString (const TestMyClass & value) { return std::string ("it is TestMyClass !"); } } */ // assert (eSTR::DString::FromFormat ("{$}{$}-{$}"_sv, 11, 22, TestMyClass ()) == eSTR::DString ("1122-it is TestMyClass !")); //-----------------------------------------------------------------------------