1 /*
2  * mptStringFormat.h
3  * -----------------
4  * Purpose: Convert other types to strings.
5  * Notes  : Currently none.
6  * Authors: OpenMPT Devs
7  * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
8  */
9 
10 
11 #pragma once
12 
13 #include "openmpt/all/BuildSettings.hpp"
14 
15 #include "mpt/base/pointer.hpp"
16 #include "mpt/format/message.hpp"
17 #include "mpt/format/simple_spec.hpp"
18 #include "mpt/string/types.hpp"
19 
20 #include <stdexcept>
21 
22 #include "mptString.h"
23 
24 #include "openmpt/base/FlagSet.hpp"
25 
26 
27 OPENMPT_NAMESPACE_BEGIN
28 
29 
30 
31 // The following section demands a rationale.
32 //  1. mpt::afmt::val(), mpt::wfmt::val() and mpt::ufmt::val() mimic the semantics of c++11 std::to_string() and std::to_wstring().
33 //     There is an important difference though. The c++11 versions are specified in terms of sprintf formatting which in turn
34 //     depends on the current C locale. This renders these functions unusable in a library context because the current
35 //     C locale is set by the library-using application and could be anything. There is no way a library can get reliable semantics
36 //     out of these functions. It is thus better to just avoid them.
37 //     ToAString() and ToWString() are based on iostream internally, but the the locale of the stream is forced to std::locale::classic(),
38 //     which results in "C" ASCII locale behavior.
39 //  2. The full suite of printf-like or iostream like number formatting is generally not required. Instead, a sane subset functionality
40 //     is provided here.
41 //     When formatting integers, it is recommended to use mpt::afmt::dec or mpt::afmt::hex. Appending a template argument '<n>' sets the width,
42 //     the same way as '%nd' would do. Appending a '0' to the function name causes zero-filling as print-like '%0nd' would do. Spelling 'HEX'
43 //     in upper-case generates upper-case hex digits. If these are not known at compile-time, a more verbose FormatValA(int, format) can be
44 //     used.
45 //  3. mpt::format(format)(...) provides simplified and type-safe message and localization string formatting.
46 //     The only specifier allowed is '{}' enclosing a number n. It references to n-th parameter after the format string (1-based).
47 //     This mimics the behaviour of QString::arg() in QT4/5 or MFC AfxFormatString2(). C printf-like functions offer similar functionality
48 //     with a '%n$TYPE' syntax. In .NET, the syntax is '{n}'. This is useful to support localization strings that can change the parameter
49 //     ordering.
50 //  4. Every function is available for std::string, std::wstring and mpt::ustring. std::string makes no assumption about the encoding, which
51 //     basically means, it should work for any 7-bit or 8-bit encoding, including for example ASCII, UTF8 or the current locale encoding.
52 //     std::string         std::wstring          mpt::ustring                    mpt::tsrtring                        CString
53 //     mpt::afmt           mpt::wfmt             mpt::ufmt                       mpt::tfmt                            mpt::cfmt
54 //     MPT_AFORMAT("{}")   MPT_WFORMAT("{}")     MPT_UFORMAT("{}")               MPT_TFORMAT("{}")                    MPT_CFORMAT("{}")
55 //  5. All functionality here delegates real work outside of the header file so that <sstream> and <locale> do not need to be included when
56 //     using this functionality.
57 //     Advantages:
58 //      - Avoids binary code bloat when too much of iostream operator << gets inlined at every usage site.
59 //      - Faster compile times because <sstream> and <locale> (2 very complex headers) are not included everywhere.
60 //     Disadvantages:
61 //      - Slightly more c++ code is required for delegating work.
62 //      - As the header does not use iostreams, custom types need to overload mpt::UString instead of iostream operator << to allow for custom type
63 //        formatting.
64 //      - std::string, std::wstring and mpt::ustring are returned from somewhat deep cascades of helper functions. Where possible, code is
65 //        written in such a way that return-value-optimization (RVO) or named-return-value-optimization (NRVO) should be able to eliminate
66 //        almost all these copies. This should not be a problem for any decent modern compiler (and even less so for a c++11 compiler where
67 //        move-semantics will kick in if RVO/NRVO fails).
68 
69 namespace mpt
70 {
71 
72 // ToUString() converts various built-in types to a well-defined, locale-independent string representation.
73 // This is also used as a type-tunnel pattern for mpt::format.
74 // Custom types that need to be converted to strings are encouraged to overload ToUString().
75 
76 // fallback to member function ToUString()
77 #if MPT_USTRING_MODE_UTF8
78 template <typename T> [[deprecated]] auto ToAString(const T & x) -> decltype(mpt::ToCharset(mpt::Charset::UTF8, x.ToUString())) { return mpt::ToCharset(mpt::Charset::UTF8, x.ToUString()); } // unknown encoding
79 #else
80 #if defined(MPT_ENABLE_CHARSET_LOCALE)
81 template <typename T> [[deprecated]] auto ToAString(const T & x) -> decltype(mpt::ToCharset(mpt::Charset::Locale, x.ToUString())) { return mpt::ToCharset(mpt::Charset::Locale, x.ToUString()); } // unknown encoding
82 #else // !MPT_ENABLE_CHARSET_LOCALE
83 template <typename T> [[deprecated]] auto ToAString(const T & x) -> decltype(mpt::ToCharset(mpt::Charset::UTF8, x.ToUString())) { return mpt::ToCharset(mpt::Charset::UTF8, x.ToUString()); } // unknown encoding
84 #endif // MPT_ENABLE_CHARSET_LOCALE
85 #endif
86 
ToAString(const std::string & x)87 inline std::string ToAString(const std::string & x) { return x; }
ToAString(const char * const & x)88 inline std::string ToAString(const char * const & x) { return x; }
89 std::string ToAString(const char &x) = delete; // deprecated to catch potential API mis-use, use std::string(1, x) instead
90 #if MPT_WSTRING_FORMAT
91 std::string ToAString(const std::wstring & x) = delete; // Unknown encoding.
92 std::string ToAString(const wchar_t * const & x) = delete; // Unknown encoding.
93 std::string ToAString(const wchar_t &x ) = delete; // deprecated to catch potential API mis-use, use std::wstring(1, x) instead
94 #endif
95 #if MPT_USTRING_MODE_UTF8
96 std::string ToAString(const mpt::ustring & x) = delete; // Unknown encoding.
97 #endif
98 #if defined(MPT_WITH_MFC)
99 std::string ToAString(const CString & x) = delete; // unknown encoding
100 #endif // MPT_WITH_MFC
101 std::string ToAString(const bool & x);
102 std::string ToAString(const signed char & x);
103 std::string ToAString(const unsigned char & x);
104 std::string ToAString(const signed short & x);
105 std::string ToAString(const unsigned short & x);
106 std::string ToAString(const signed int & x);
107 std::string ToAString(const unsigned int & x);
108 std::string ToAString(const signed long & x);
109 std::string ToAString(const unsigned long & x);
110 std::string ToAString(const signed long long & x);
111 std::string ToAString(const unsigned long long & x);
112 std::string ToAString(const float & x);
113 std::string ToAString(const double & x);
114 std::string ToAString(const long double & x);
115 
116 // fallback to member function ToUString()
117 template <typename T> auto ToUString(const T & x) -> decltype(x.ToUString()) { return x.ToUString(); }
118 
ToUString(const mpt::ustring & x)119 inline mpt::ustring ToUString(const mpt::ustring & x) { return x; }
120 mpt::ustring ToUString(const std::string & x) = delete; // Unknown encoding.
121 mpt::ustring ToUString(const char * const & x) = delete; // Unknown encoding. Note that this also applies to TCHAR in !UNICODE builds as the type is indistinguishable from char. Wrap with CString or FromTcharStr in this case.
122 mpt::ustring ToUString(const char & x) = delete; // deprecated to catch potential API mis-use, use std::string(1, x) instead
123 #if MPT_WSTRING_FORMAT
124 #if MPT_USTRING_MODE_UTF8
125 mpt::ustring ToUString(const std::wstring & x);
126 #endif
127 mpt::ustring ToUString(const wchar_t * const & x);
128 mpt::ustring ToUString(const wchar_t & x) = delete; // deprecated to catch potential API mis-use, use std::wstring(1, x) instead
129 #endif
130 #if defined(MPT_WITH_MFC)
131 mpt::ustring ToUString(const CString & x);
132 #endif // MPT_WITH_MFC
133 mpt::ustring ToUString(const bool & x);
134 mpt::ustring ToUString(const signed char & x);
135 mpt::ustring ToUString(const unsigned char & x);
136 mpt::ustring ToUString(const signed short & x);
137 mpt::ustring ToUString(const unsigned short & x);
138 mpt::ustring ToUString(const signed int & x);
139 mpt::ustring ToUString(const unsigned int & x);
140 mpt::ustring ToUString(const signed long & x);
141 mpt::ustring ToUString(const unsigned long & x);
142 mpt::ustring ToUString(const signed long long & x);
143 mpt::ustring ToUString(const unsigned long long & x);
144 mpt::ustring ToUString(const float & x);
145 mpt::ustring ToUString(const double & x);
146 mpt::ustring ToUString(const long double & x);
147 
148 #if MPT_WSTRING_FORMAT
149 std::wstring ToWString(const std::string & x) = delete; // Unknown encoding.
150 std::wstring ToWString(const char * const & x) = delete; // Unknown encoding. Note that this also applies to TCHAR in !UNICODE builds as the type is indistinguishable from char. Wrap with CString or FromTcharStr in this case.
151 std::wstring ToWString(const char & x) = delete; // deprecated to catch potential API mis-use, use std::string(1, x) instead
ToWString(const std::wstring & x)152 inline std::wstring ToWString(const std::wstring & x) { return x; }
ToWString(const wchar_t * const & x)153 inline std::wstring ToWString(const wchar_t * const & x) { return x; }
154 std::wstring ToWString(const wchar_t & x) = delete; // deprecated to catch potential API mis-use, use std::wstring(1, x) instead
155 #if MPT_USTRING_MODE_UTF8
156 std::wstring ToWString(const mpt::ustring & x);
157 #endif
158 #if defined(MPT_WITH_MFC)
159 std::wstring ToWString(const CString & x);
160 #endif // MPT_WITH_MFC
161 std::wstring ToWString(const bool & x);
162 std::wstring ToWString(const signed char & x);
163 std::wstring ToWString(const unsigned char & x);
164 std::wstring ToWString(const signed short & x);
165 std::wstring ToWString(const unsigned short & x);
166 std::wstring ToWString(const signed int & x);
167 std::wstring ToWString(const unsigned int & x);
168 std::wstring ToWString(const signed long & x);
169 std::wstring ToWString(const unsigned long & x);
170 std::wstring ToWString(const signed long long & x);
171 std::wstring ToWString(const unsigned long long & x);
172 std::wstring ToWString(const float & x);
173 std::wstring ToWString(const double & x);
174 std::wstring ToWString(const long double & x);
175 // fallback to member function ToUString()
176 template <typename T> auto ToWString(const T & x) -> decltype(mpt::ToWide(x.ToUString())) { return mpt::ToWide(x.ToUString()); }
177 #endif
178 
179 #if defined(MPT_ENABLE_CHARSET_LOCALE)
operatorToLocaleHelper180 template <typename T> struct ToLocaleHelper { mpt::lstring operator () (const T & v) { return mpt::ToLocale(ToUString(v)); } };
181 template <> struct ToLocaleHelper<mpt::lstring> { mpt::lstring operator () (const mpt::lstring & v) { return v; } };
182 #endif // MPT_ENABLE_CHARSET_LOCALE
183 
184 #if defined(MPT_WITH_MFC)
185 template <typename T> struct ToCStringHelper { CString operator () (const T & v) { return mpt::ToCString(ToUString(v)); } };
186 template <> struct ToCStringHelper<CString> { CString operator () (const CString & v) { return v; } };
187 #endif // MPT_WITH_MFC
188 
189 template <typename Tstring> struct ToStringTFunctor {};
190 template <> struct ToStringTFunctor<std::string> { template <typename T> inline std::string operator() (const T & x) { return ToAString(x); } };
191 template <> struct ToStringTFunctor<mpt::ustring> { template <typename T> inline mpt::ustring operator() (const T & x) { return ToUString(x); } };
192 #if MPT_WSTRING_FORMAT && MPT_USTRING_MODE_UTF8
193 template <> struct ToStringTFunctor<std::wstring> { template <typename T> inline std::wstring operator() (const T & x) { return ToWString(x); } };
194 #endif
195 #if defined(MPT_ENABLE_CHARSET_LOCALE)
196 template <> struct ToStringTFunctor<mpt::lstring> { template <typename T> inline mpt::lstring operator() (const T & x) { return mpt::ToLocaleHelper<T>()(x); } };
197 #endif // MPT_ENABLE_CHARSET_LOCALE
198 #if defined(MPT_WITH_MFC)
199 template <> struct ToStringTFunctor<CString> { template <typename T> inline CString operator() (const T & x) { return mpt::ToCStringHelper<T>()(x); } };
200 #endif // MPT_WITH_MFC
201 
202 template<typename Tstring, typename T> inline Tstring ToStringT(const T & x) { return ToStringTFunctor<Tstring>()(x); }
203 
204 
205 struct ToStringFormatter {
206 	template <typename Tstring, typename T>
207 	static inline Tstring format(const T& value) {
208 		return ToStringTFunctor<Tstring>()(value);
209 
210 	}
211 };
212 
213 
214 using FormatSpec = mpt::format_simple_spec;
215 
216 using FormatFlags = mpt::format_simple_flags;
217 
218 using fmt_base = mpt::format_simple_base;
219 
220 
221 std::string FormatValA(const char & x, const FormatSpec & f) = delete; // deprecated to catch potential API mis-use, use std::string(1, x) instead
222 #if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
223 std::string FormatValA(const wchar_t & x, const FormatSpec & f) = delete; // deprecated to catch potential API mis-use, use std::wstring(1, x) instead
224 #endif // !MPT_COMPILER_QUIRK_NO_WCHAR
225 std::string FormatValA(const bool & x, const FormatSpec & f);
226 std::string FormatValA(const signed char & x, const FormatSpec & f);
227 std::string FormatValA(const unsigned char & x, const FormatSpec & f);
228 std::string FormatValA(const signed short & x, const FormatSpec & f);
229 std::string FormatValA(const unsigned short & x, const FormatSpec & f);
230 std::string FormatValA(const signed int & x, const FormatSpec & f);
231 std::string FormatValA(const unsigned int & x, const FormatSpec & f);
232 std::string FormatValA(const signed long & x, const FormatSpec & f);
233 std::string FormatValA(const unsigned long & x, const FormatSpec & f);
234 std::string FormatValA(const signed long long & x, const FormatSpec & f);
235 std::string FormatValA(const unsigned long long & x, const FormatSpec & f);
236 std::string FormatValA(const float & x, const FormatSpec & f);
237 std::string FormatValA(const double & x, const FormatSpec & f);
238 std::string FormatValA(const long double & x, const FormatSpec & f);
239 
240 mpt::ustring FormatValU(const char & x, const FormatSpec & f) = delete; // deprecated to catch potential API mis-use, use std::string(1, x) instead
241 #if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
242 mpt::ustring FormatValU(const wchar_t & x, const FormatSpec & f) = delete; // deprecated to catch potential API mis-use, use std::wstring(1, x) instead
243 #endif // !MPT_COMPILER_QUIRK_NO_WCHAR
244 mpt::ustring FormatValU(const bool & x, const FormatSpec & f);
245 mpt::ustring FormatValU(const signed char & x, const FormatSpec & f);
246 mpt::ustring FormatValU(const unsigned char & x, const FormatSpec & f);
247 mpt::ustring FormatValU(const signed short & x, const FormatSpec & f);
248 mpt::ustring FormatValU(const unsigned short & x, const FormatSpec & f);
249 mpt::ustring FormatValU(const signed int & x, const FormatSpec & f);
250 mpt::ustring FormatValU(const unsigned int & x, const FormatSpec & f);
251 mpt::ustring FormatValU(const signed long & x, const FormatSpec & f);
252 mpt::ustring FormatValU(const unsigned long & x, const FormatSpec & f);
253 mpt::ustring FormatValU(const signed long long & x, const FormatSpec & f);
254 mpt::ustring FormatValU(const unsigned long long & x, const FormatSpec & f);
255 mpt::ustring FormatValU(const float & x, const FormatSpec & f);
256 mpt::ustring FormatValU(const double & x, const FormatSpec & f);
257 mpt::ustring FormatValU(const long double & x, const FormatSpec & f);
258 
259 #if MPT_WSTRING_FORMAT
260 std::wstring FormatValW(const char & x, const FormatSpec & f) = delete; // deprecated to catch potential API mis-use, use std::string(1, x) instead
261 #if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
262 std::wstring FormatValW(const wchar_t & x, const FormatSpec & f) = delete; // deprecated to catch potential API mis-use, use std::wstring(1, x) instead
263 #endif // !MPT_COMPILER_QUIRK_NO_WCHAR
264 std::wstring FormatValW(const bool & x, const FormatSpec & f);
265 std::wstring FormatValW(const signed char & x, const FormatSpec & f);
266 std::wstring FormatValW(const unsigned char & x, const FormatSpec & f);
267 std::wstring FormatValW(const signed short & x, const FormatSpec & f);
268 std::wstring FormatValW(const unsigned short & x, const FormatSpec & f);
269 std::wstring FormatValW(const signed int & x, const FormatSpec & f);
270 std::wstring FormatValW(const unsigned int & x, const FormatSpec & f);
271 std::wstring FormatValW(const signed long & x, const FormatSpec & f);
272 std::wstring FormatValW(const unsigned long & x, const FormatSpec & f);
273 std::wstring FormatValW(const signed long long & x, const FormatSpec & f);
274 std::wstring FormatValW(const unsigned long long & x, const FormatSpec & f);
275 std::wstring FormatValW(const float & x, const FormatSpec & f);
276 std::wstring FormatValW(const double & x, const FormatSpec & f);
277 std::wstring FormatValW(const long double & x, const FormatSpec & f);
278 #endif
279 
280 template <typename Tstring> struct FormatValTFunctor {};
281 template <> struct FormatValTFunctor<std::string> { template <typename T> inline std::string operator() (const T & x, const FormatSpec & f) { return FormatValA(x, f); } };
282 template <> struct FormatValTFunctor<mpt::ustring> { template <typename T> inline mpt::ustring operator() (const T & x, const FormatSpec & f) { return FormatValU(x, f); } };
283 #if MPT_USTRING_MODE_UTF8 && MPT_WSTRING_FORMAT
284 template <> struct FormatValTFunctor<std::wstring> { template <typename T> inline std::wstring operator() (const T & x, const FormatSpec & f) { return FormatValW(x, f); } };
285 #endif
286 #if defined(MPT_ENABLE_CHARSET_LOCALE)
287 template <> struct FormatValTFunctor<mpt::lstring> { template <typename T> inline mpt::lstring operator() (const T & x, const FormatSpec & f) { return mpt::ToLocale(mpt::Charset::Locale, FormatValA(x, f)); } };
288 #endif // MPT_ENABLE_CHARSET_LOCALE
289 #if defined(MPT_WITH_MFC)
290 #ifdef UNICODE
291 template <> struct FormatValTFunctor<CString> { template <typename T> inline CString operator() (const T & x, const FormatSpec & f) { return mpt::ToCString(FormatValW(x, f)); } };
292 #else // !UNICODE
293 template <> struct FormatValTFunctor<CString> { template <typename T> inline CString operator() (const T & x, const FormatSpec & f) { return mpt::ToCString(mpt::Charset::Locale, FormatValA(x, f)); } };
294 #endif // UNICODE
295 #endif // MPT_WITH_MFC
296 
297 
298 template <typename Tstring>
299 struct fmtT : fmt_base
300 {
301 
302 template<typename T>
303 static inline Tstring val(const T& x)
304 {
305 	return ToStringTFunctor<Tstring>()(x);
306 }
307 
308 template<typename T>
309 static inline Tstring fmt(const T& x, const FormatSpec& f)
310 {
311 	return FormatValTFunctor<Tstring>()(x, f);
312 }
313 
314 template<typename T>
315 static inline Tstring dec(const T& x)
316 {
317 	static_assert(std::numeric_limits<T>::is_integer);
318 	return FormatValTFunctor<Tstring>()(x, FormatSpec().BaseDec().FillOff());
319 }
320 template<int width, typename T>
321 static inline Tstring dec0(const T& x)
322 {
323 	static_assert(std::numeric_limits<T>::is_integer);
324 	return FormatValTFunctor<Tstring>()(x, FormatSpec().BaseDec().FillNul().Width(width));
325 }
326 
327 template<typename T>
328 static inline Tstring dec(unsigned int g, char s, const T& x)
329 {
330 	static_assert(std::numeric_limits<T>::is_integer);
331 	return FormatValTFunctor<Tstring>()(x, FormatSpec().BaseDec().FillOff().Group(g).GroupSep(s));
332 }
333 template<int width, typename T>
334 static inline Tstring dec0(unsigned int g, char s, const T& x)
335 {
336 	static_assert(std::numeric_limits<T>::is_integer);
337 	return FormatValTFunctor<Tstring>()(x, FormatSpec().BaseDec().FillNul().Width(width).Group(g).GroupSep(s));
338 }
339 
340 template<typename T>
341 static inline Tstring hex(const T& x)
342 {
343 	static_assert(std::numeric_limits<T>::is_integer);
344 	return FormatValTFunctor<Tstring>()(x, FormatSpec().BaseHex().CaseLow().FillOff());
345 }
346 template<typename T>
347 static inline Tstring HEX(const T& x)
348 {
349 	static_assert(std::numeric_limits<T>::is_integer);
350 	return FormatValTFunctor<Tstring>()(x, FormatSpec().BaseHex().CaseUpp().FillOff());
351 }
352 template<int width, typename T>
353 static inline Tstring hex0(const T& x)
354 {
355 	static_assert(std::numeric_limits<T>::is_integer);
356 	return FormatValTFunctor<Tstring>()(x, FormatSpec().BaseHex().CaseLow().FillNul().Width(width));
357 }
358 template<int width, typename T>
359 static inline Tstring HEX0(const T& x)
360 {
361 	static_assert(std::numeric_limits<T>::is_integer);
362 	return FormatValTFunctor<Tstring>()(x, FormatSpec().BaseHex().CaseUpp().FillNul().Width(width));
363 }
364 
365 template<typename T>
366 static inline Tstring hex(unsigned int g, char s, const T& x)
367 {
368 	static_assert(std::numeric_limits<T>::is_integer);
369 	return FormatValTFunctor<Tstring>()(x, FormatSpec().BaseHex().CaseLow().FillOff().Group(g).GroupSep(s));
370 }
371 template<typename T>
372 static inline Tstring HEX(unsigned int g, char s, const T& x)
373 {
374 	static_assert(std::numeric_limits<T>::is_integer);
375 	return FormatValTFunctor<Tstring>()(x, FormatSpec().BaseHex().CaseUpp().FillOff().Group(g).GroupSep(s));
376 }
377 template<int width, typename T>
378 static inline Tstring hex0(unsigned int g, char s, const T& x)
379 {
380 	static_assert(std::numeric_limits<T>::is_integer);
381 	return FormatValTFunctor<Tstring>()(x, FormatSpec().BaseHex().CaseLow().FillNul().Width(width).Group(g).GroupSep(s));
382 }
383 template<int width, typename T>
384 static inline Tstring HEX0(unsigned int g, char s, const T& x)
385 {
386 	static_assert(std::numeric_limits<T>::is_integer);
387 	return FormatValTFunctor<Tstring>()(x, FormatSpec().BaseHex().CaseUpp().FillNul().Width(width).Group(g).GroupSep(s));
388 }
389 
390 template<typename T>
391 static inline Tstring flt(const T& x, int precision = -1)
392 {
393 	static_assert(std::is_floating_point<T>::value);
394 	return FormatValTFunctor<Tstring>()(x, FormatSpec().NotaNrm().FillOff().Precision(precision));
395 }
396 template<typename T>
397 static inline Tstring fix(const T& x, int precision = -1)
398 {
399 	static_assert(std::is_floating_point<T>::value);
400 	return FormatValTFunctor<Tstring>()(x, FormatSpec().NotaFix().FillOff().Precision(precision));
401 }
402 template<typename T>
403 static inline Tstring sci(const T& x, int precision = -1)
404 {
405 	static_assert(std::is_floating_point<T>::value);
406 	return FormatValTFunctor<Tstring>()(x, FormatSpec().NotaSci().FillOff().Precision(precision));
407 }
408 
409 template<typename T>
410 static inline Tstring ptr(const T& x)
411 {
412 	static_assert(std::is_pointer<T>::value || std::is_same<T, std::uintptr_t>::value || std::is_same<T, std::intptr_t>::value, "");
413 	return hex0<mpt::pointer_size * 2>(mpt::pointer_cast<const std::uintptr_t>(x));
414 }
415 template<typename T>
416 static inline Tstring PTR(const T& x)
417 {
418 	static_assert(std::is_pointer<T>::value || std::is_same<T, std::uintptr_t>::value || std::is_same<T, std::intptr_t>::value, "");
419 	return HEX0<mpt::pointer_size * 2>(mpt::pointer_cast<const std::uintptr_t>(x));
420 }
421 
422 static inline Tstring pad_left(std::size_t width_, const Tstring &str)
423 {
424 	typedef mpt::string_traits<Tstring> traits;
425 	typename traits::size_type width = static_cast<typename traits::size_type>(width_);
426 	return traits::pad(str, width, 0);
427 }
428 static inline Tstring pad_right(std::size_t width_, const Tstring &str)
429 {
430 	typedef mpt::string_traits<Tstring> traits;
431 	typename traits::size_type width = static_cast<typename traits::size_type>(width_);
432 	return traits::pad(str, 0, width);
433 }
434 static inline Tstring left(std::size_t width_, const Tstring &str)
435 {
436 	typedef mpt::string_traits<Tstring> traits;
437 	typename traits::size_type width = static_cast<typename traits::size_type>(width_);
438 	return (traits::length(str) < width) ? traits::pad(str, 0, width - traits::length(str)) : str;
439 }
440 static inline Tstring right(std::size_t width_, const Tstring &str)
441 {
442 	typedef mpt::string_traits<Tstring> traits;
443 	typename traits::size_type width = static_cast<typename traits::size_type>(width_);
444 	return (traits::length(str) < width) ? traits::pad(str, width - traits::length(str), 0) : str;
445 }
446 static inline Tstring center(std::size_t width_, const Tstring &str)
447 {
448 	typedef mpt::string_traits<Tstring> traits;
449 	typename traits::size_type width = static_cast<typename traits::size_type>(width_);
450 	return (traits::length(str) < width) ? traits::pad(str, (width - traits::length(str)) / 2, (width - traits::length(str) + 1) / 2) : str;
451 }
452 
453 }; // struct fmtT
454 
455 
456 typedef fmtT<std::string> afmt;
457 #if MPT_WSTRING_FORMAT
458 typedef fmtT<std::wstring> wfmt;
459 #endif
460 #if MPT_USTRING_MODE_WIDE
461 typedef fmtT<std::wstring> ufmt;
462 #else
463 typedef fmtT<mpt::ustring> ufmt;
464 #endif
465 #if defined(MPT_ENABLE_CHARSET_LOCALE)
466 typedef fmtT<mpt::lstring> lfmt;
467 #endif // MPT_ENABLE_CHARSET_LOCALE
468 #if MPT_OS_WINDOWS
469 typedef fmtT<mpt::tstring> tfmt;
470 #endif
471 #if defined(MPT_WITH_MFC)
472 typedef fmtT<CString> cfmt;
473 #endif // MPT_WITH_MFC
474 
475 
476 #define MPT_AFORMAT(f) mpt::format_message<mpt::ToStringFormatter, mpt::parse_format_string_argument_count(f)>(f)
477 
478 #if MPT_WSTRING_FORMAT
479 #define MPT_WFORMAT(f) mpt::format_message_typed<mpt::ToStringFormatter, mpt::parse_format_string_argument_count( L ## f ), std::wstring>( L ## f )
480 #endif
481 
482 #define MPT_UFORMAT(f) mpt::format_message_typed<mpt::ToStringFormatter, mpt::parse_format_string_argument_count(MPT_ULITERAL(f)), mpt::ustring>(MPT_ULITERAL(f))
483 
484 #if defined(MPT_ENABLE_CHARSET_LOCALE)
485 #define MPT_LFORMAT(f) mpt::format_message_typed<mpt::ToStringFormatter, mpt::parse_format_string_argument_count(f), mpt::lstring>(f)
486 #endif // MPT_ENABLE_CHARSET_LOCALE
487 
488 #if MPT_OS_WINDOWS
489 #define MPT_TFORMAT(f) mpt::format_message_typed<mpt::ToStringFormatter, mpt::parse_format_string_argument_count(TEXT(f)), mpt::tstring>(TEXT(f))
490 #endif
491 
492 #if defined(MPT_WITH_MFC)
493 #define MPT_CFORMAT(f) mpt::format_message_typed<mpt::ToStringFormatter, mpt::parse_format_string_argument_count(TEXT(f)), CString>(TEXT(f))
494 #endif // MPT_WITH_MFC
495 
496 
497 } // namespace mpt
498 
499 
500 
501 namespace mpt { namespace String {
502 
503 // Combine a vector of values into a string, separated with the given separator.
504 // No escaping is performed.
505 template<typename T>
506 mpt::ustring Combine(const std::vector<T> &vals, const mpt::ustring &sep=U_(","))
507 {
508 	mpt::ustring str;
509 	for(std::size_t i = 0; i < vals.size(); ++i)
510 	{
511 		if(i > 0)
512 		{
513 			str += sep;
514 		}
515 		str += mpt::ufmt::val(vals[i]);
516 	}
517 	return str;
518 }
519 template<typename T>
520 std::string Combine(const std::vector<T> &vals, const std::string &sep=std::string(","))
521 {
522 	std::string str;
523 	for(std::size_t i = 0; i < vals.size(); ++i)
524 	{
525 		if(i > 0)
526 		{
527 			str += sep;
528 		}
529 		str += mpt::afmt::val(vals[i]);
530 	}
531 	return str;
532 }
533 
534 } } // namespace mpt::String
535 
536 
537 
538 template <typename enum_t, typename store_t>
539 mpt::ustring ToUString(FlagSet<enum_t, store_t> flagset)
540 {
541 	mpt::ustring str(flagset.size_bits(), UC_('0'));
542 
543 	for(std::size_t x = 0; x < flagset.size_bits(); ++x)
544 	{
545 		str[flagset.size_bits() - x - 1] = (flagset.value().as_bits() & (static_cast<typename FlagSet<enum_t>::store_type>(1) << x) ? UC_('1') : UC_('0'));
546 	}
547 
548 	return str;
549 }
550 
551 
552 
553 
554 OPENMPT_NAMESPACE_END
555