From: <man...@us...> - 2013-11-12 23:02:13
|
Revision: 3203 http://sourceforge.net/p/modplug/code/3203 Author: manxorist Date: 2013-11-12 23:02:01 +0000 (Tue, 12 Nov 2013) Log Message: ----------- [Fix] For ConvertStrTo, force the istringstream to classic() locale to make it independent of any global c or c++ locale set by the host program (in case of libopenmpt). [Fix] Replace all scanf-like calls as well as ato[i|f] and strto[l|f] with either a classic()-locale based istringstream solution or just simply locale-independent ConvertStrTo<T>() to make libopenmpt behaviour independent of the global c/c++ locale. [Fix] Replace almost all printf-like calls in soundlib/ with our own type-safe and locale-independent formatting. Debug logging via the global Log() function is still based on sprintf though. [Ref] Formatting and number parsing are both based on c++ iostreams. However, the interface header is independent of <sstream> and <locale> to improve compile times and cleanly separate the implementation detail (i.e. do not catch up custom type formatting via ostream::operator<<). This also avoids binary code bloat which could be caused by ostream::operator<< inlining. [Ref] Add some small documentation snippets to mptString.h. [Var] Documentation for the custom formatting library (also included in common/mptString.h): 1. ToString() and ToWString() mimic the semantics of c++11 std::to_string() and std::to_wstring(). There is an important difference though. The c++11 versions are specified in terms of sprintf formatting which in turn depend on the current C locale. This renders these functions unusable in a library context because the current C locale is set by the library-using application and could be anything. There is no way a library can get reliable semantics out of these functions. It is thus better to just avoid them. ToString() and ToWString() are based on iostream internally, but the the locale of the stream is forced to std::locale::classic(), which results in "C" ASCII locale behavior. 2. The full suite of printf-like or iostream like number formatting is generally not required. Instead, a sane subset functionality is provided here. For convenience, mpt::Format().ParsePrintf(const char *).ToString(float) allows formatting a single floating point value with a standard printf-like format string. This itself relies on iostream with classic() locale internally and is thus current locale agnostic. When formatting integers, it is recommended to use mpt::fmt::dec or mpt::fmt::hex. Appending a template argument '<n>' sets the width, 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' in upper-case generates upper-case hex digits. If these are not known at compile-time, a more verbose FormatVal(int, format) can be used. 3. mpt::String::Print(format, ...) provides simplified and type-safe message and localization string formatting. The only specifier allowed is '%' followed by a single digit n. It references to n-th parameter after the format string (1-based). This mimics the behaviour of QString::arg() in QT4/5 or MFC AfxFormatString2(). C printf-like functions offer similar functionality with a '%n$TYPE' syntax. In .NET, the syntax is '{n}'. This is useful to support localization strings that can change the parameter ordering. 4. Every function is available for std::string and std::wstring. std::string makes no assumption about the encoding, which basically means, it should work for any 7-bit or 8-bit encoding, including for example ASCII, UTF8 or the current locale encoding. std::string std::wstring mpt::ToString mpt::ToWString mpt::FormatVal mpt::FormatValW mpt::fmt mpt::wfmt mpt::String::Print mpt::String::PrintW 5. All functionality here delegates real work outside of the header file so that <sstream> and <locale> do not need to be included when using this functionality. Advantages: - Avoids binary code bloat when too much of iostream operator << gets inlined at every usage site. - Faster compile times because <sstream> and <locale> (2 very complex headers) are not included everywhere. Disadvantages: - Slightly more c++ code is required for delegating work. - As the header does not use iostreams, custom types need to overload mpt::ToString and mpt::ToWstring instead of iostream operator << to allow for custom type formatting. - std::string and std::wstring are returned from somewhat deep cascades of helper functions. Where possible, code is written in such a way that return-value-optimization (RVO) or named-return-value-optimization (NRVO) should be able to eliminate 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 move-semantics will kick in if RVO/NRVO fails). Modified Paths: -------------- trunk/OpenMPT/common/Profiler.cpp trunk/OpenMPT/common/misc_util.cpp trunk/OpenMPT/common/misc_util.h trunk/OpenMPT/common/mptString.cpp trunk/OpenMPT/common/mptString.h trunk/OpenMPT/common/stdafx.h trunk/OpenMPT/common/typedefs.cpp trunk/OpenMPT/common/typedefs.h trunk/OpenMPT/common/version.cpp trunk/OpenMPT/libopenmpt/libopenmpt_impl.cpp trunk/OpenMPT/mptrack/Settings.cpp trunk/OpenMPT/soundlib/Load_ams.cpp trunk/OpenMPT/soundlib/Load_dbm.cpp trunk/OpenMPT/soundlib/Load_digi.cpp trunk/OpenMPT/soundlib/Load_gdm.cpp trunk/OpenMPT/soundlib/Load_it.cpp trunk/OpenMPT/soundlib/Load_med.cpp trunk/OpenMPT/soundlib/Load_mtm.cpp trunk/OpenMPT/soundlib/Load_psm.cpp trunk/OpenMPT/soundlib/Load_ptm.cpp trunk/OpenMPT/soundlib/Load_s3m.cpp trunk/OpenMPT/soundlib/Load_stm.cpp trunk/OpenMPT/soundlib/Load_umx.cpp trunk/OpenMPT/soundlib/Load_xm.cpp trunk/OpenMPT/soundlib/ModSequence.cpp trunk/OpenMPT/test/test.cpp Modified: trunk/OpenMPT/common/Profiler.cpp =================================================================== --- trunk/OpenMPT/common/Profiler.cpp 2013-11-12 21:56:31 UTC (rev 3202) +++ trunk/OpenMPT/common/Profiler.cpp 2013-11-12 23:02:01 UTC (rev 3203) @@ -110,7 +110,7 @@ case Profiler::Audio: cat = "Audio"; break; case Profiler::Notify: cat = "Notify"; break; } - ret += cat + " " + std::string(stats.profile.Name) + ": " + mpt::String::Format("%6.3f", (stats.usage * 100.0)) + "%\r\n"; + ret += cat + " " + std::string(stats.profile.Name) + ": " + mpt::Format("%6.3f").ToString(stats.usage * 100.0) + "%\r\n"; } } ret += "\r\n"; Modified: trunk/OpenMPT/common/misc_util.cpp =================================================================== --- trunk/OpenMPT/common/misc_util.cpp 2013-11-12 21:56:31 UTC (rev 3202) +++ trunk/OpenMPT/common/misc_util.cpp 2013-11-12 23:02:01 UTC (rev 3203) @@ -11,7 +11,75 @@ #include "stdafx.h" #include "misc_util.h" +#include <locale> +#include <sstream> +#include <string> + +template<typename T> +inline T ConvertStrToHelper(const std::string &str) +{ + std::istringstream i(str); + i.imbue(std::locale::classic()); + T x; + if(!(i >> x)) + { + return T(); + } + return x; +} +template<> inline bool ConvertStrToHelper(const std::string &str) { return ConvertStrToHelper<int>(str)?true:false; } +template<> inline signed char ConvertStrToHelper(const std::string &str) { return static_cast<signed char>(ConvertStrToHelper<signed int>(str)); } +template<> inline unsigned char ConvertStrToHelper(const std::string &str) { return static_cast<unsigned char>(ConvertStrToHelper<unsigned int>(str)); } + +template<typename T> +inline T ConvertStrToHelper(const std::wstring &str) +{ + std::wistringstream i(str); + i.imbue(std::locale::classic()); + T x; + if(!(i >> x)) + { + return T(); + } + return x; +} +template<> inline bool ConvertStrToHelper(const std::wstring &str) { return ConvertStrToHelper<int>(str)?true:false; } +template<> inline signed char ConvertStrToHelper(const std::wstring &str) { return static_cast<signed char>(ConvertStrToHelper<signed int>(str)); } +template<> inline unsigned char ConvertStrToHelper(const std::wstring &str) { return static_cast<unsigned char>(ConvertStrToHelper<unsigned int>(str)); } + +bool ConvertStrToBool(const std::string &str) { return ConvertStrToHelper<bool>(str); } +signed char ConvertStrToSignedChar(const std::string &str) { return ConvertStrToHelper<signed char>(str); } +unsigned char ConvertStrToUnsignedChar(const std::string &str) { return ConvertStrToHelper<unsigned char>(str); } +signed short ConvertStrToSignedShort(const std::string &str) { return ConvertStrToHelper<signed short>(str); } +unsigned short ConvertStrToUnsignedShort(const std::string &str) { return ConvertStrToHelper<unsigned short>(str); } +signed int ConvertStrToSignedInt(const std::string &str) { return ConvertStrToHelper<signed int>(str); } +unsigned int ConvertStrToUnsignedInt(const std::string &str) { return ConvertStrToHelper<unsigned int>(str); } +signed long ConvertStrToSignedLong(const std::string &str) { return ConvertStrToHelper<signed long>(str); } +unsigned long ConvertStrToUnsignedLong(const std::string &str) { return ConvertStrToHelper<unsigned long>(str); } +signed long long ConvertStrToSignedLongLong(const std::string &str) { return ConvertStrToHelper<signed long long>(str); } +unsigned long long ConvertStrToUnsignedLongLong(const std::string &str) { return ConvertStrToHelper<unsigned long long>(str); } +float ConvertStrToFloat(const std::string &str) { return ConvertStrToHelper<float>(str); } +double ConvertStrToDouble(const std::string &str) { return ConvertStrToHelper<double>(str); } +long double ConvertStrToLongDouble(const std::string &str) { return ConvertStrToHelper<long double>(str); } + +bool ConvertStrToBool(const std::wstring &str) { return ConvertStrToHelper<bool>(str); } +signed char ConvertStrToSignedChar(const std::wstring &str) { return ConvertStrToHelper<signed char>(str); } +unsigned char ConvertStrToUnsignedChar(const std::wstring &str) { return ConvertStrToHelper<unsigned char>(str); } +signed short ConvertStrToSignedShort(const std::wstring &str) { return ConvertStrToHelper<signed short>(str); } +unsigned short ConvertStrToUnsignedShort(const std::wstring &str) { return ConvertStrToHelper<unsigned short>(str); } +signed int ConvertStrToSignedInt(const std::wstring &str) { return ConvertStrToHelper<signed int>(str); } +unsigned int ConvertStrToUnsignedInt(const std::wstring &str) { return ConvertStrToHelper<unsigned int>(str); } +signed long ConvertStrToSignedLong(const std::wstring &str) { return ConvertStrToHelper<signed long>(str); } +unsigned long ConvertStrToUnsignedLong(const std::wstring &str) { return ConvertStrToHelper<unsigned long>(str); } +signed long long ConvertStrToSignedLongLong(const std::wstring &str) { return ConvertStrToHelper<signed long long>(str); } +unsigned long long ConvertStrToUnsignedLongLong(const std::wstring &str) { return ConvertStrToHelper<unsigned long long>(str); } +float ConvertStrToFloat(const std::wstring &str) { return ConvertStrToHelper<float>(str); } +double ConvertStrToDouble(const std::wstring &str) { return ConvertStrToHelper<double>(str); } +long double ConvertStrToLongDouble(const std::wstring &str) { return ConvertStrToHelper<long double>(str); } + + + #if defined(ENABLE_ASM) Modified: trunk/OpenMPT/common/misc_util.h =================================================================== --- trunk/OpenMPT/common/misc_util.h 2013-11-12 21:56:31 UTC (rev 3202) +++ trunk/OpenMPT/common/misc_util.h 2013-11-12 23:02:01 UTC (rev 3203) @@ -11,7 +11,6 @@ #pragma once #include <limits> -#include <sstream> #include <string> #include <cmath> @@ -25,79 +24,89 @@ #include <type_traits> #endif -//Convert object(typically number) to string -template<class T> -inline std::string Stringify(const T& x) -//-------------------------------------- -{ - std::ostringstream o; - if(!(o << x)) return "FAILURE"; - else return o.str(); -} +bool ConvertStrToBool(const std::string &str); +signed char ConvertStrToSignedChar(const std::string &str); +unsigned char ConvertStrToUnsignedChar(const std::string &str); +signed short ConvertStrToSignedShort(const std::string &str); +unsigned short ConvertStrToUnsignedShort(const std::string &str); +signed int ConvertStrToSignedInt(const std::string &str); +unsigned int ConvertStrToUnsignedInt(const std::string &str); +signed long ConvertStrToSignedLong(const std::string &str); +unsigned long ConvertStrToUnsignedLong(const std::string &str); +signed long long ConvertStrToSignedLongLong(const std::string &str); +unsigned long long ConvertStrToUnsignedLongLong(const std::string &str); +float ConvertStrToFloat(const std::string &str); +double ConvertStrToDouble(const std::string &str); +long double ConvertStrToLongDouble(const std::string &str); -template<> inline std::string Stringify(const signed char& x) { return Stringify((signed int)x); } -template<> inline std::string Stringify(const unsigned char& x) { return Stringify((unsigned int)x); } +template<typename T> inline T ConvertStrTo(const std::string &str); // not defined, generates compiler error for non-specialized types +template<> inline bool ConvertStrTo(const std::string &str) { return ConvertStrToBool(str); } +template<> inline signed char ConvertStrTo(const std::string &str) { return ConvertStrToSignedChar(str); } +template<> inline unsigned char ConvertStrTo(const std::string &str) { return ConvertStrToUnsignedChar(str); } +template<> inline signed short ConvertStrTo(const std::string &str) { return ConvertStrToSignedShort(str); } +template<> inline unsigned short ConvertStrTo(const std::string &str) { return ConvertStrToUnsignedShort(str); } +template<> inline signed int ConvertStrTo(const std::string &str) { return ConvertStrToSignedInt(str); } +template<> inline unsigned int ConvertStrTo(const std::string &str) { return ConvertStrToUnsignedInt(str); } +template<> inline signed long ConvertStrTo(const std::string &str) { return ConvertStrToSignedLong(str); } +template<> inline unsigned long ConvertStrTo(const std::string &str) { return ConvertStrToUnsignedLong(str); } +template<> inline signed long long ConvertStrTo(const std::string &str) { return ConvertStrToSignedLongLong(str); } +template<> inline unsigned long long ConvertStrTo(const std::string &str) { return ConvertStrToUnsignedLongLong(str); } +template<> inline float ConvertStrTo(const std::string &str) { return ConvertStrToFloat(str); } +template<> inline double ConvertStrTo(const std::string &str) { return ConvertStrToDouble(str); } +template<> inline long double ConvertStrTo(const std::string &str) { return ConvertStrToLongDouble(str); } -template<class T> -inline std::wstring StringifyW(const T& x) -//---------------------------------------- -{ - std::wostringstream o; - if(!(o << x)) return L"FAILURE"; - else return o.str(); -} +bool ConvertStrToBool(const std::wstring &str); +signed char ConvertStrToSignedChar(const std::wstring &str); +unsigned char ConvertStrToUnsignedChar(const std::wstring &str); +signed short ConvertStrToSignedShort(const std::wstring &str); +unsigned short ConvertStrToUnsignedShort(const std::wstring &str); +signed int ConvertStrToSignedInt(const std::wstring &str); +unsigned int ConvertStrToUnsignedInt(const std::wstring &str); +signed long ConvertStrToSignedLong(const std::wstring &str); +unsigned long ConvertStrToUnsignedLong(const std::wstring &str); +signed long long ConvertStrToSignedLongLong(const std::wstring &str); +unsigned long long ConvertStrToUnsignedLongLong(const std::wstring &str); +float ConvertStrToFloat(const std::wstring &str); +double ConvertStrToDouble(const std::wstring &str); +long double ConvertStrToLongDouble(const std::wstring &str); -template<> inline std::wstring StringifyW(const signed char& x) { return StringifyW((signed int)x); } -template<> inline std::wstring StringifyW(const unsigned char& x) { return StringifyW((unsigned int)x); } +template<typename T> inline T ConvertStrTo(const std::wstring &str); // not defined, generates compiler error for non-specialized types +template<> inline bool ConvertStrTo(const std::wstring &str) { return ConvertStrToBool(str); } +template<> inline signed char ConvertStrTo(const std::wstring &str) { return ConvertStrToSignedChar(str); } +template<> inline unsigned char ConvertStrTo(const std::wstring &str) { return ConvertStrToUnsignedChar(str); } +template<> inline signed short ConvertStrTo(const std::wstring &str) { return ConvertStrToSignedShort(str); } +template<> inline unsigned short ConvertStrTo(const std::wstring &str) { return ConvertStrToUnsignedShort(str); } +template<> inline signed int ConvertStrTo(const std::wstring &str) { return ConvertStrToSignedInt(str); } +template<> inline unsigned int ConvertStrTo(const std::wstring &str) { return ConvertStrToUnsignedInt(str); } +template<> inline signed long ConvertStrTo(const std::wstring &str) { return ConvertStrToSignedLong(str); } +template<> inline unsigned long ConvertStrTo(const std::wstring &str) { return ConvertStrToUnsignedLong(str); } +template<> inline signed long long ConvertStrTo(const std::wstring &str) { return ConvertStrToSignedLongLong(str); } +template<> inline unsigned long long ConvertStrTo(const std::wstring &str) { return ConvertStrToUnsignedLongLong(str); } +template<> inline float ConvertStrTo(const std::wstring &str) { return ConvertStrToFloat(str); } +template<> inline double ConvertStrTo(const std::wstring &str) { return ConvertStrToDouble(str); } +template<> inline long double ConvertStrTo(const std::wstring &str) { return ConvertStrToLongDouble(str); } -//Convert string to number. -template<class T> +template<typename T> inline T ConvertStrTo(const char *str) -//------------------------------------ { - #ifdef HAS_TYPE_TRAITS - static_assert(std::is_const<T>::value == false && std::is_volatile<T>::value == false, "Const and volatile types are not handled correctly."); - #endif - if(std::numeric_limits<T>::is_integer) - return static_cast<T>(atoi(str)); - else - return static_cast<T>(atof(str)); + if(!str) + { + return T(); + } + return ConvertStrTo<T>(std::string(str)); } -template<class T> -inline T ConvertStrTo(const std::string &str) -//------------------------------------------- +template<typename T> +inline T ConvertStrTo(const wchar_t *str) { - return ConvertStrTo<T>(str.c_str()); + if(!str) + { + return T(); + } + return ConvertStrTo<T>(std::wstring(str)); } -#if MPT_COMPILER_MSVC -#define cxx11_strtoll _strtoi64 -#define cxx11_strtoull _strtoui64 -#else -#define cxx11_strtoll std::strtoll -#define cxx11_strtoull std::strtoull -#endif -template<> inline bool ConvertStrTo(const char *str) {return std::strtol(str, nullptr, 10) ? true : false;} -template<> inline signed char ConvertStrTo(const char *str) {return (signed char)std::strtol(str, nullptr, 10);} -template<> inline signed short ConvertStrTo(const char *str) {return (signed short)std::strtol(str, nullptr, 10);} -template<> inline signed int ConvertStrTo(const char *str) {return (signed int)std::strtol(str, nullptr, 10);} -template<> inline signed long ConvertStrTo(const char *str) {return std::strtol(str, nullptr, 10);} -template<> inline signed long long ConvertStrTo(const char *str) {return cxx11_strtoll(str, nullptr, 10);} -template<> inline unsigned char ConvertStrTo(const char *str) {return (unsigned char)std::strtoul(str, nullptr, 10);} -template<> inline unsigned short ConvertStrTo(const char *str) {return (unsigned short)std::strtoul(str, nullptr, 10);} -template<> inline unsigned int ConvertStrTo(const char *str) {return (unsigned int)std::strtoul(str, nullptr, 10);} -template<> inline unsigned long ConvertStrTo(const char *str) {return std::strtoul(str, nullptr, 10);} -template<> inline unsigned long long ConvertStrTo(const char *str) {return cxx11_strtoull(str, nullptr, 10);} -template<class T> -inline T ConvertStrTo(const std::wstring &str) -//-------------------------------------------- -{ - return ConvertStrTo<T>(mpt::String::Encode(str, mpt::CharsetLocale)); -} - - // Memset given object to zero. template <class T> inline void MemsetZero(T &a) Modified: trunk/OpenMPT/common/mptString.cpp =================================================================== --- trunk/OpenMPT/common/mptString.cpp 2013-11-12 21:56:31 UTC (rev 3202) +++ trunk/OpenMPT/common/mptString.cpp 2013-11-12 23:02:01 UTC (rev 3203) @@ -1,8 +1,8 @@ /* * mptString.cpp * ------------- - * Purpose: A wrapper around std::string implemeting the CString. - * Notes : Should be removed somewhen in the future when all uses of CString have been converted to std::string. + * Purpose: MFC compatibility classes, small string-related utilities, number and message formatting. + * Notes : Currently none. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ @@ -10,8 +10,13 @@ #include "stdafx.h" #include "mptString.h" +#include <iomanip> +#include <locale> +#include <sstream> +#include <string> #include <stdexcept> #include <vector> + #include <cstdarg> #if !defined(WIN32) @@ -22,6 +27,8 @@ namespace mpt { namespace String { +#ifdef MODPLUG_TRACKER + std::string Format(const char *format, ...) { #if MPT_COMPILER_MSVC @@ -48,7 +55,9 @@ #endif } +#endif + #if defined(WIN32) static UINT CharsetToCodepage(Charset charset) { @@ -294,3 +303,332 @@ } } // namespace mpt::String + + +namespace mpt +{ + + +template<typename Tstream, typename T> inline void SaneInsert(Tstream & s, const T & x) { s << x; } +// do the right thing for signed/unsigned char and bool +template<typename Tstream> void SaneInsert(Tstream & s, const bool & x) { s << static_cast<int>(x); } +template<typename Tstream> void SaneInsert(Tstream & s, const signed char & x) { s << static_cast<signed int>(x); } +template<typename Tstream> void SaneInsert(Tstream & s, const unsigned char & x) { s << static_cast<unsigned int>(x); } + +template<typename T> +inline std::string ToStringHelper(const T & x) +{ + std::ostringstream o; + o.imbue(std::locale::classic()); + SaneInsert(o, x); + return o.str(); +} + +template<typename T> +inline std::wstring ToWStringHelper(const T & x) +{ + std::wostringstream o; + o.imbue(std::locale::classic()); + SaneInsert(o, x); + return o.str(); +} + +std::string ToString(const char & x) { return std::string(1, x); } +std::string ToString(const wchar_t & x) { return mpt::String::Encode(std::wstring(1, x), mpt::CharsetLocale); } +std::string ToString(const bool & x) { return ToStringHelper(x); } +std::string ToString(const signed char & x) { return ToStringHelper(x); } +std::string ToString(const unsigned char & x) { return ToStringHelper(x); } +std::string ToString(const signed short & x) { return ToStringHelper(x); } +std::string ToString(const unsigned short & x) { return ToStringHelper(x); } +std::string ToString(const signed int & x) { return ToStringHelper(x); } +std::string ToString(const unsigned int & x) { return ToStringHelper(x); } +std::string ToString(const signed long & x) { return ToStringHelper(x); } +std::string ToString(const unsigned long & x) { return ToStringHelper(x); } +std::string ToString(const signed long long & x) { return ToStringHelper(x); } +std::string ToString(const unsigned long long & x) { return ToStringHelper(x); } +std::string ToString(const float & x) { return ToStringHelper(x); } +std::string ToString(const double & x) { return ToStringHelper(x); } +std::string ToString(const long double & x) { return ToStringHelper(x); } + +std::wstring ToWString(const char & x) { return mpt::String::Decode(std::string(1, x), mpt::CharsetLocale); } +std::wstring ToWString(const wchar_t & x) { return std::wstring(1, x); } +std::wstring ToWString(const bool & x) { return ToWStringHelper(x); } +std::wstring ToWString(const signed char & x) { return ToWStringHelper(x); } +std::wstring ToWString(const unsigned char & x) { return ToWStringHelper(x); } +std::wstring ToWString(const signed short & x) { return ToWStringHelper(x); } +std::wstring ToWString(const unsigned short & x) { return ToWStringHelper(x); } +std::wstring ToWString(const signed int & x) { return ToWStringHelper(x); } +std::wstring ToWString(const unsigned int & x) { return ToWStringHelper(x); } +std::wstring ToWString(const signed long & x) { return ToWStringHelper(x); } +std::wstring ToWString(const unsigned long & x) { return ToWStringHelper(x); } +std::wstring ToWString(const signed long long & x) { return ToWStringHelper(x); } +std::wstring ToWString(const unsigned long long & x) { return ToWStringHelper(x); } +std::wstring ToWString(const float & x) { return ToWStringHelper(x); } +std::wstring ToWString(const double & x) { return ToWStringHelper(x); } +std::wstring ToWString(const long double & x) { return ToWStringHelper(x); } + + +#if defined(MPT_FMT) + + +template<typename Tostream> +inline void ApplyFormat(Tostream & o, const Format & format) +{ + FormatFlags f = format.GetFlags(); + std::size_t width = format.GetWidth(); + int precision = format.GetPrecision(); + if(precision != -1 && width != 0 && !(f & fmt::NotaFix) && !(f & fmt::NotaSci)) + { + // fixup: + // precision behaves differently from .# + // avoid default format when precision and width are set + f &= ~fmt::NotaNrm; + f |= fmt::NotaFix; + } + if(f & fmt::BaseDec) { o << std::dec; } + else if(f & fmt::BaseHex) { o << std::hex; } + if(f & fmt::NotaNrm ) { /*nothing*/ } + else if(f & fmt::NotaFix ) { o << std::setiosflags(std::ios::fixed); } + else if(f & fmt::NotaSci ) { o << std::setiosflags(std::ios::scientific); } + if(f & fmt::CaseLow) { o << std::nouppercase; } + else if(f & fmt::CaseUpp) { o << std::uppercase; } + if(f & fmt::FillOff) { /* nothing */ } + else if(f & fmt::FillNul) { o << std::setw(width) << std::setfill(typename Tostream::char_type('0')); } + else if(f & fmt::FillSpc) { o << std::setw(width) << std::setfill(typename Tostream::char_type(' ')); } + if(precision != -1) { o << std::setprecision(precision); } +} + + +template<typename T> +inline std::string FormatValHelper(const T & x, const Format & f) +{ + std::ostringstream o; + o.imbue(std::locale::classic()); + ApplyFormat(o, f); + SaneInsert(o, x); + return o.str(); +} + +template<typename T> +inline std::wstring FormatValWHelper(const T & x, const Format & f) +{ + std::wostringstream o; + o.imbue(std::locale::classic()); + ApplyFormat(o, f); + SaneInsert(o, x); + return o.str(); +} + +// Parses a useful subset of standard sprintf syntax for specifying floating point formatting. +template<typename Tchar> +inline Format ParseFormatStringFloat(const Tchar * str) +{ + ASSERT(str); + FormatFlags f = FormatFlags(); + std::size_t width = 0; + int precision = -1; + if(!str) + { + return Format(); + } + const Tchar * p = str; + while(*p && *p != Tchar('%')) + { + ++p; + } + ++p; + while(*p && (*p == Tchar(' ') || *p == Tchar('0'))) + { + if(*p == Tchar(' ')) f |= mpt::fmt::FillSpc; + if(*p == Tchar('0')) f |= mpt::fmt::FillNul; + ++p; + } + if(!(f & mpt::fmt::FillSpc) && !(f & mpt::fmt::FillNul)) + { + f |= mpt::fmt::FillOff; + } + while(*p && (Tchar('0') <= *p && *p <= Tchar('9'))) + { + if(f & mpt::fmt::FillOff) + { + f &= ~mpt::fmt::FillOff; + f |= mpt::fmt::FillSpc; + } + width *= 10; + width += *p - Tchar('0'); + ++p; + } + if(*p && *p == Tchar('.')) + { + ++p; + precision = 0; + while(*p && (Tchar('0') <= *p && *p <= Tchar('9'))) + { + precision *= 10; + precision += *p - Tchar('0'); + ++p; + } + } + if(*p && (*p == Tchar('g') || *p == Tchar('G') || *p == Tchar('f') || *p == Tchar('F') || *p == Tchar('e') || *p == Tchar('E'))) + { + if(*p == Tchar('g')) f |= mpt::fmt::NotaNrm | mpt::fmt::CaseLow; + if(*p == Tchar('G')) f |= mpt::fmt::NotaNrm | mpt::fmt::CaseUpp; + if(*p == Tchar('f')) f |= mpt::fmt::NotaFix | mpt::fmt::CaseLow; + if(*p == Tchar('F')) f |= mpt::fmt::NotaFix | mpt::fmt::CaseUpp; + if(*p == Tchar('e')) f |= mpt::fmt::NotaSci | mpt::fmt::CaseLow; + if(*p == Tchar('E')) f |= mpt::fmt::NotaSci | mpt::fmt::CaseUpp; + ++p; + } + return Format().SetFlags(f).SetWidth(width).SetPrecision(precision); +} + +Format & Format::ParsePrintf(const char * format) +{ + *this = ParseFormatStringFloat(format); + return *this; +} +Format & Format::ParsePrintf(const wchar_t * format) +{ + *this = ParseFormatStringFloat(format); + return *this; +} +Format & Format::ParsePrintf(const std::string & format) +{ + *this = ParseFormatStringFloat(format.c_str()); + return *this; +} +Format & Format::ParsePrintf(const std::wstring & format) +{ + *this = ParseFormatStringFloat(format.c_str()); + return *this; +} + + +std::string FormatVal(const char & x, const Format & f) { return FormatValHelper(x, f); } +std::string FormatVal(const wchar_t & x, const Format & f) { return FormatValHelper(x, f); } +std::string FormatVal(const bool & x, const Format & f) { return FormatValHelper(x, f); } +std::string FormatVal(const signed char & x, const Format & f) { return FormatValHelper(x, f); } +std::string FormatVal(const unsigned char & x, const Format & f) { return FormatValHelper(x, f); } +std::string FormatVal(const signed short & x, const Format & f) { return FormatValHelper(x, f); } +std::string FormatVal(const unsigned short & x, const Format & f) { return FormatValHelper(x, f); } +std::string FormatVal(const signed int & x, const Format & f) { return FormatValHelper(x, f); } +std::string FormatVal(const unsigned int & x, const Format & f) { return FormatValHelper(x, f); } +std::string FormatVal(const signed long & x, const Format & f) { return FormatValHelper(x, f); } +std::string FormatVal(const unsigned long & x, const Format & f) { return FormatValHelper(x, f); } +std::string FormatVal(const signed long long & x, const Format & f) { return FormatValHelper(x, f); } +std::string FormatVal(const unsigned long long & x, const Format & f) { return FormatValHelper(x, f); } + +std::string FormatVal(const float & x, const Format & f) { return FormatValHelper(x, f); } +std::string FormatVal(const double & x, const Format & f) { return FormatValHelper(x, f); } +std::string FormatVal(const long double & x, const Format & f) { return FormatValHelper(x, f); } + +std::wstring FormatValW(const char & x, const Format & f) { return FormatValWHelper(x, f); } +std::wstring FormatValW(const wchar_t & x, const Format & f) { return FormatValWHelper(x, f); } +std::wstring FormatValW(const bool & x, const Format & f) { return FormatValWHelper(x, f); } +std::wstring FormatValW(const signed char & x, const Format & f) { return FormatValWHelper(x, f); } +std::wstring FormatValW(const unsigned char & x, const Format & f) { return FormatValWHelper(x, f); } +std::wstring FormatValW(const signed short & x, const Format & f) { return FormatValWHelper(x, f); } +std::wstring FormatValW(const unsigned short & x, const Format & f) { return FormatValWHelper(x, f); } +std::wstring FormatValW(const signed int & x, const Format & f) { return FormatValWHelper(x, f); } +std::wstring FormatValW(const unsigned int & x, const Format & f) { return FormatValWHelper(x, f); } +std::wstring FormatValW(const signed long & x, const Format & f) { return FormatValWHelper(x, f); } +std::wstring FormatValW(const unsigned long & x, const Format & f) { return FormatValWHelper(x, f); } +std::wstring FormatValW(const signed long long & x, const Format & f) { return FormatValWHelper(x, f); } +std::wstring FormatValW(const unsigned long long & x, const Format & f) { return FormatValWHelper(x, f); } + +std::wstring FormatValW(const float & x, const Format & f) { return FormatValWHelper(x, f); } +std::wstring FormatValW(const double & x, const Format & f) { return FormatValWHelper(x, f); } +std::wstring FormatValW(const long double & x, const Format & f) { return FormatValWHelper(x, f); } + + +#endif // MPT_FMT + + +namespace String +{ + + +namespace detail +{ + +template<typename Tstring> +Tstring PrintImplTemplate(const Tstring & format + , const Tstring & x1 + , const Tstring & x2 + , const Tstring & x3 + , const Tstring & x4 + , const Tstring & x5 + , const Tstring & x6 + , const Tstring & x7 + , const Tstring & x8 + ) +{ + Tstring result; + const std::size_t len = format.length(); + for(std::size_t pos = 0; pos != len; ++pos) + { + typename Tstring::value_type c = format[pos]; + if(pos + 1 != len && c == '%') + { + pos++; + c = format[pos]; + if('1' <= c && c <= '9') + { + const std::size_t n = c - '0'; + switch(n) + { + case 1: result.append(x1); break; + case 2: result.append(x2); break; + case 3: result.append(x3); break; + case 4: result.append(x4); break; + case 5: result.append(x5); break; + case 6: result.append(x6); break; + case 7: result.append(x7); break; + case 8: result.append(x8); break; + } + continue; + } else if(c != '%') + { + result.append(1, '%'); + } + } + result.append(1, c); + } + return result; +} + +std::string PrintImpl(const std::string & format + , const std::string & x1 + , const std::string & x2 + , const std::string & x3 + , const std::string & x4 + , const std::string & x5 + , const std::string & x6 + , const std::string & x7 + , const std::string & x8 + ) +{ + return PrintImplTemplate<std::string>(format, x1,x2,x3,x4,x5,x6,x7,x8); +} + +std::wstring PrintImplW(const std::wstring & format + , const std::wstring & x1 + , const std::wstring & x2 + , const std::wstring & x3 + , const std::wstring & x4 + , const std::wstring & x5 + , const std::wstring & x6 + , const std::wstring & x7 + , const std::wstring & x8 + ) +{ + return PrintImplTemplate<std::wstring>(format, x1,x2,x3,x4,x5,x6,x7,x8); +} + +} // namespace detail + + +} // namespace String + + +} // namespace mpt Modified: trunk/OpenMPT/common/mptString.h =================================================================== --- trunk/OpenMPT/common/mptString.h 2013-11-12 21:56:31 UTC (rev 3202) +++ trunk/OpenMPT/common/mptString.h 2013-11-12 23:02:01 UTC (rev 3203) @@ -1,8 +1,8 @@ /* * mptString.h * ---------- - * Purpose: A wrapper around std::string implemeting the CString interface. - * Notes : Should be removed somewhen in the future when all uses of CString have been converted to std::string. + * Purpose: MFC compatibility classes, small string-related utilities, number and message formatting. + * Notes : Currently none. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ @@ -10,7 +10,12 @@ #pragma once +#include <limits> #include <string> +#if defined(HAS_TYPE_TRAITS) +#include <type_traits> +#endif + #include <cstdio> #include <cstring> #include <stdio.h> @@ -18,13 +23,7 @@ #include <strings.h> // for strcasecmp #endif -#if MPT_COMPILER_GCC || MPT_COMPILER_CLANG -#define MPT_PRINTF_FUNC(formatstringindex,varargsindex) __attribute__((format(printf, formatstringindex, varargsindex))) -#else -#define MPT_PRINTF_FUNC(formatstringindex,varargsindex) -#endif - namespace mpt { @@ -51,7 +50,15 @@ { +#ifdef MODPLUG_TRACKER +// There are 4 reasons why this is not available for library code: +// 1. printf-like functionality is not type-safe. +// 2. There are portability problems with char/wchar_t and the semantics of %s/%ls/%S . +// 3. There are portability problems with specifying format for 64bit integers. +// 4. Formatting of floating point values depends on the currently set C locale. +// A library is not allowed to mock with that and thus cannot influence the behavior in this case. std::string MPT_PRINTF_FUNC(1,2) Format(const char * format, ...); +#endif // Remove whitespace at start of string @@ -147,11 +154,11 @@ enum Charset { - CharsetLocale, + CharsetLocale, // CP_ACP on windows, current C locale otherwise CharsetUTF8, - CharsetUS_ASCII, + CharsetUS_ASCII, // strictly 7-bit ASCII CharsetISO8859_1, CharsetISO8859_15, @@ -164,14 +171,30 @@ namespace String { +// Encode a wide unicode string into the specified encoding. +// Invalid unicode code points, or code points not representable are silently substituted. +// The wide encoding is UTF-16 or UTF-32, based on sizeof(wchar_t). std::string Encode(const std::wstring &src, Charset charset); + +// Decode a 8-bit or multi-byte encoded string from the specified charset into a wide unicode string. +// Invalid char sequences are silently substituted. +// The wide encoding is UTF-16 or UTF-32, based on sizeof(wchar_t). std::wstring Decode(const std::string &src, Charset charset); +// Convert from one 8-bit charset to another. +// This is semantically equivalent to Encode(Decode(src, from), to). std::string Convert(const std::string &src, Charset from, Charset to); #if defined(_MFC_VER) +// Convert to a MFC CString. The CString encoding depends on UNICODE. +// This should also be used when converting to TCHAR strings. +// If UNICODE is defined, this is a completely lossless operation. CString ToCString(const std::string &src, Charset charset); CString ToCString(const std::wstring &src); + +// Convert from a MFC CString. The CString encoding depends on UNICODE. +// This should also be used when converting from TCHAR strings. +// If UNICODE is defined, this is a completely lossless operation. std::string FromCString(const CString &src, Charset charset); std::wstring FromCString(const CString &src); #endif @@ -179,3 +202,841 @@ } // namespace String } // namespace mpt + + + + + +// The following section demands a rationale. +// 1. ToString() and ToWString() mimic the semantics of c++11 std::to_string() and std::to_wstring(). +// There is an important difference though. The c++11 versions are specified in terms of sprintf formatting which in turn +// depend on the current C locale. This renders these functions unusable in a library context because the current +// C locale is set by the library-using application and could be anything. There is no way a library can get reliable semantics +// out of these functions. It is thus better to just avoid them. +// ToString() and ToWString() are based on iostream internally, but the the locale of the stream is forced to std::locale::classic(), +// which results in "C" ASCII locale behavior. +// 2. The full suite of printf-like or iostream like number formatting is generally not required. Instead, a sane subset functionality +// is provided here. +// For convenience, mpt::Format().ParsePrintf(const char *).ToString(float) allows formatting a single floating point value with a +// standard printf-like format string. This itself relies on iostream with classic() locale internally and is thus current locale +// agnostic. +// When formatting integers, it is recommended to use mpt::fmt::dec or mpt::fmt::hex. Appending a template argument '<n>' sets the width, +// 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' +// in upper-case generates upper-case hex digits. If these are not known at compile-time, a more verbose FormatVal(int, format) can be +// used. +// 3. mpt::String::Print(format, ...) provides simplified and type-safe message and localization string formatting. +// The only specifier allowed is '%' followed by a single digit n. It references to n-th parameter after the format string (1-based). +// This mimics the behaviour of QString::arg() in QT4/5 or MFC AfxFormatString2(). C printf-like functions offer similar functionality +// with a '%n$TYPE' syntax. In .NET, the syntax is '{n}'. This is useful to support localization strings that can change the parameter +// ordering. +// 4. Every function is available for std::string and std::wstring. std::string makes no assumption about the encoding, which basically means, +// it should work for any 7-bit or 8-bit encoding, including for example ASCII, UTF8 or the current locale encoding. +// std::string std::wstring +// mpt::ToString mpt::ToWString +// mpt::FormatVal mpt::FormatValW +// mpt::fmt mpt::wfmt +// mpt::String::Print mpt::String::PrintW +// 5. All functionality here delegates real work outside of the header file so that <sstream> and <locale> do not need to be included when +// using this functionality. +// Advantages: +// - Avoids binary code bloat when too much of iostream operator << gets inlined at every usage site. +// - Faster compile times because <sstream> and <locale> (2 very complex headers) are not included everywhere. +// Disadvantages: +// - Slightly more c++ code is required for delegating work. +// - As the header does not use iostreams, custom types need to overload mpt::ToString and mpt::ToWstring instead of iostream +// operator << to allow for custom type formatting. +// - std::string and std::wstring are returned from somewhat deep cascades of helper functions. Where possible, code is written in such +// a way that return-value-optimization (RVO) or named-return-value-optimization (NRVO) should be able to eliminate 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 move-semantics +// will kick in if RVO/NRVO fails). + +namespace mpt +{ + +// ToString() converts various built-in types to a well-defined, locale-independent string representation. +// This is also used as a type-tunnel pattern for mpt::String::Print. +// Custom types that need to be converted to strings are encouraged to overload ToString() and ToWString(). + +static inline std::string ToString(const std::string & x) { return x; } +static inline std::string ToString(const char * const & x) { return x; } +MPT_DEPRECATED std::string ToString(const char & x); // deprecated to catch potential API mis-use, use std::string(1, x) instead +MPT_DEPRECATED std::string ToString(const wchar_t & x); // deprecated to catch potential API mis-use, use std::wstring(1, x) instead +std::string ToString(const bool & x); +std::string ToString(const signed char & x); +std::string ToString(const unsigned char & x); +std::string ToString(const signed short & x); +std::string ToString(const unsigned short & x); +std::string ToString(const signed int & x); +std::string ToString(const unsigned int & x); +std::string ToString(const signed long & x); +std::string ToString(const unsigned long & x); +std::string ToString(const signed long long & x); +std::string ToString(const unsigned long long & x); +std::string ToString(const float & x); +std::string ToString(const double & x); +std::string ToString(const long double & x); + +static inline std::wstring ToWString(const std::wstring & x) { return x; } +static inline std::wstring ToWString(const wchar_t * const & x) { return x; } +MPT_DEPRECATED std::wstring ToWString(const char & x); // deprecated to catch potential API mis-use, use std::string(1, x) instead +MPT_DEPRECATED std::wstring ToWString(const wchar_t & x); // deprecated to catch potential API mis-use, use std::wstring(1, x) instead +std::wstring ToWString(const bool & x); +std::wstring ToWString(const signed char & x); +std::wstring ToWString(const unsigned char & x); +std::wstring ToWString(const signed short & x); +std::wstring ToWString(const unsigned short & x); +std::wstring ToWString(const signed int & x); +std::wstring ToWString(const unsigned int & x); +std::wstring ToWString(const signed long & x); +std::wstring ToWString(const unsigned long & x); +std::wstring ToWString(const signed long long & x); +std::wstring ToWString(const unsigned long long & x); +std::wstring ToWString(const float & x); +std::wstring ToWString(const double & x); +std::wstring ToWString(const long double & x); + +#define MPT_FMT + +#if defined(MPT_FMT) + +namespace fmt +{ + +enum FormatFlagsEnum +{ + BaseDec = 0x0001, // base 10 (integers only) + BaseHex = 0x0002, // base 16 (integers only) + CaseLow = 0x0010, // lower case hex digits + CaseUpp = 0x0020, // upper case hex digits + FillOff = 0x0100, // do not fill up width + FillSpc = 0x0200, // fill up width with spaces + FillNul = 0x0400, // fill up width with zeros + NotaNrm = 0x1000, // float: normal/default notation + NotaFix = 0x2000, // float: fixed point notation + NotaSci = 0x4000, // float: scientific notation +}; + +} // namespace fmt + +typedef unsigned int FormatFlags; + +STATIC_ASSERT(sizeof(FormatFlags) >= sizeof(fmt::FormatFlagsEnum)); + +class Format; + +MPT_DEPRECATED std::string FormatVal(const char & x, const Format & f); // deprecated to catch potential API mis-use, use std::string(1, x) instead +MPT_DEPRECATED std::string FormatVal(const wchar_t & x, const Format & f); // deprecated to catch potential API mis-use, use std::wstring(1, x) instead +std::string FormatVal(const bool & x, const Format & f); +std::string FormatVal(const signed char & x, const Format & f); +std::string FormatVal(const unsigned char & x, const Format & f); +std::string FormatVal(const signed short & x, const Format & f); +std::string FormatVal(const unsigned short & x, const Format & f); +std::string FormatVal(const signed int & x, const Format & f); +std::string FormatVal(const unsigned int & x, const Format & f); +std::string FormatVal(const signed long & x, const Format & f); +std::string FormatVal(const unsigned long & x, const Format & f); +std::string FormatVal(const signed long long & x, const Format & f); +std::string FormatVal(const unsigned long long & x, const Format & f); + +std::string FormatVal(const float & x, const Format & f); +std::string FormatVal(const double & x, const Format & f); +std::string FormatVal(const long double & x, const Format & f); + +MPT_DEPRECATED std::wstring FormatValW(const char & x, const Format & f); // deprecated to catch potential API mis-use, use std::string(1, x) instead +MPT_DEPRECATED std::wstring FormatValW(const wchar_t & x, const Format & f); // deprecated to catch potential API mis-use, use std::wstring(1, x) instead +std::wstring FormatValW(const bool & x, const Format & f); +std::wstring FormatValW(const signed char & x, const Format & f); +std::wstring FormatValW(const unsigned char & x, const Format & f); +std::wstring FormatValW(const signed short & x, const Format & f); +std::wstring FormatValW(const unsigned short & x, const Format & f); +std::wstring FormatValW(const signed int & x, const Format & f); +std::wstring FormatValW(const unsigned int & x, const Format & f); +std::wstring FormatValW(const signed long & x, const Format & f); +std::wstring FormatValW(const unsigned long & x, const Format & f); +std::wstring FormatValW(const signed long long & x, const Format & f); +std::wstring FormatValW(const unsigned long long & x, const Format & f); + +std::wstring FormatValW(const float & x, const Format & f); +std::wstring FormatValW(const double & x, const Format & f); +std::wstring FormatValW(const long double & x, const Format & f); + +class Format +{ +private: + FormatFlags flags; + std::size_t width; + int precision; +public: + Format() : flags(0), width(0), precision(-1) {} + FormatFlags GetFlags() const { return flags; } + std::size_t GetWidth() const { return width; } + int GetPrecision() const { return precision; } + Format & SetFlags(FormatFlags f) { flags = f; return *this; } + Format & SetWidth(std::size_t w) { width = w; return *this; } + Format & SetPrecision(int p) { precision = p; return *this; } +public: + // short-hand construction + explicit Format(FormatFlags f, std::size_t w = 0, int p = -1) : flags(f), width(w), precision(p) {} + explicit Format(const char * format) : flags(0), width(0), precision(-1) { ParsePrintf(format); } + explicit Format(const wchar_t * format) : flags(0), width(0), precision(-1) { ParsePrintf(format); } + explicit Format(const std::string & format) : flags(0), width(0), precision(-1) { ParsePrintf(format); } + explicit Format(const std::wstring & format) : flags(0), width(0), precision(-1) { ParsePrintf(format); } +public: + // only for floating point formats + Format & ParsePrintf(const char * format); + Format & ParsePrintf(const wchar_t * format); + Format & ParsePrintf(const std::string & format); + Format & ParsePrintf(const std::wstring & format); +public: + Format & BaseDec() { flags &= ~(fmt::BaseDec|fmt::BaseHex); flags |= fmt::BaseDec; return *this; } + Format & BaseHex() { flags &= ~(fmt::BaseDec|fmt::BaseHex); flags |= fmt::BaseHex; return *this; } + Format & CaseLow() { flags &= ~(fmt::CaseLow|fmt::CaseUpp); flags |= fmt::CaseLow; return *this; } + Format & CaseUpp() { flags &= ~(fmt::CaseLow|fmt::CaseUpp); flags |= fmt::CaseUpp; return *this; } + Format & FillOff() { flags &= ~(fmt::FillOff|fmt::FillSpc|fmt::FillNul); flags |= fmt::FillOff; return *this; } + Format & FillSpc() { flags &= ~(fmt::FillOff|fmt::FillSpc|fmt::FillNul); flags |= fmt::FillSpc; return *this; } + Format & FillNul() { flags &= ~(fmt::FillOff|fmt::FillSpc|fmt::FillNul); flags |= fmt::FillNul; return *this; } + Format & NotaNrm() { flags &= ~(fmt::NotaNrm|fmt::NotaFix|fmt::NotaSci); flags |= fmt::NotaNrm; return *this; } + Format & NotaFix() { flags &= ~(fmt::NotaNrm|fmt::NotaFix|fmt::NotaSci); flags |= fmt::NotaFix; return *this; } + Format & NotaSci() { flags &= ~(fmt::NotaNrm|fmt::NotaFix|fmt::NotaSci); flags |= fmt::NotaSci; return *this; } + Format & Width(std::size_t w) { width = w; return *this; } + Format & Prec(int p) { precision = p; return *this; } +public: + Format & Dec() { return BaseDec(); } + Format & Hex() { return BaseHex(); } + Format & Low() { return CaseLow(); } + Format & Upp() { return CaseUpp(); } + Format & Off() { return FillOff(); } + Format & Spc() { return FillSpc(); } + Format & Nul() { return FillNul(); } + Format & Nrm() { return NotaNrm(); } + Format & Fix() { return NotaFix(); } + Format & Sci() { return NotaSci(); } +public: + Format & Decimal() { return BaseDec(); } + Format & Hexadecimal() { return BaseHex(); } + Format & Lower() { return CaseLow(); } + Format & Upper() { return CaseUpp(); } + Format & FillNone() { return FillOff(); } + Format & FillSpace() { return FillSpc(); } + Format & FillZero() { return FillNul(); } + Format & FloatNormal() { return NotaNrm(); } + Format & FloatFixed() { return NotaFix(); } + Format & FloatScientific() { return NotaSci(); } + Format & Precision(int p) { return Prec(p); } + template<typename T> + inline std::string ToString(const T & x) const + { + return FormatVal(x, *this); + } + template<typename T> + inline std::wstring ToWString(const T & x) const + { + return FormatValW(x, *this); + } +}; + +namespace fmt +{ + +template<typename T> +inline std::string dec(const T& x) +{ + STATIC_ASSERT(std::numeric_limits<T>::is_integer); + return FormatVal(x, Format().BaseDec().FillOff()); +} +template<int width, typename T> +inline std::string dec(const T& x) +{ + STATIC_ASSERT(std::numeric_limits<T>::is_integer); + return FormatVal(x, Format().BaseDec().FillSpc().Width(width)); +} +template<int width, typename T> +inline std::string dec0(const T& x) +{ + STATIC_ASSERT(std::numeric_limits<T>::is_integer); + return FormatVal(x, Format().BaseDec().FillNul().Width(width)); +} + +template<typename T> +inline std::string hex(const T& x) +{ + STATIC_ASSERT(std::numeric_limits<T>::is_integer); + return FormatVal(x, Format().BaseHex().CaseLow().FillOff()); +} +template<typename T> +inline std::string HEX(const T& x) +{ + STATIC_ASSERT(std::numeric_limits<T>::is_integer); + return FormatVal(x, Format().BaseHex().CaseUpp().FillOff()); +} +template<int width, typename T> +inline std::string hex(const T& x) +{ + STATIC_ASSERT(std::numeric_limits<T>::is_integer); + return FormatVal(x, Format().BaseHex().CaseLow().FillSpc().Width(width)); +} +template<int width, typename T> +inline std::string HEX(const T& x) +{ + STATIC_ASSERT(std::numeric_limits<T>::is_integer); + return FormatVal(x, Format().BaseHex().CaseUpp().FillSpc().Width(width)); +} +template<int width, typename T> +inline std::string hex0(const T& x) +{ + STATIC_ASSERT(std::numeric_limits<T>::is_integer); + return FormatVal(x, Format().BaseHex().CaseLow().FillNul().Width(width)); +} +template<int width, typename T> +inline std::string HEX0(const T& x) +{ + STATIC_ASSERT(std::numeric_limits<T>::is_integer); + return FormatVal(x, Format().BaseHex().CaseUpp().FillNul().Width(width)); +} + +template<typename T> +inline std::string flt(const T& x, std::size_t width = 0, int precision = -1) +{ + #if defined(HAS_TYPE_TRAITS) + STATIC_ASSERT(std::is_floating_point<T>::value); + #endif + if(width == 0) + { + return FormatVal(x, Format().NotaNrm().FillOff().Precision(precision)); + } else + { + return FormatVal(x, Format().NotaNrm().FillSpc().Width(width).Precision(precision)); + } +} +template<typename T> +inline std::string fix(const T& x, std::size_t width = 0, int precision = -1) +{ + #if defined(HAS_TYPE_TRAITS) + STATIC_ASSERT(std::is_floating_point<T>::value); + #endif + if(width == 0) + { + return FormatVal(x, Format().NotaFix().FillOff().Precision(precision)); + } else + { + return FormatVal(x, Format().NotaFix().FillSpc().Width(width).Precision(precision)); + } +} +template<typename T> +inline std::string sci(const T& x, std::size_t width = 0, int precision = -1) +{ + #if defined(HAS_TYPE_TRAITS) + STATIC_ASSERT(std::is_floating_point<T>::value); + #endif + if(width == 0) + { + return FormatVal(x, Format().NotaSci().FillOff().Precision(precision)); + } else + { + return FormatVal(x, Format().NotaSci().FillSpc().Width(width).Precision(precision)); + } +} + +} // namespace fmt + +namespace wfmt +{ + +template<typename T> +inline std::wstring dec(const T& x) +{ + STATIC_ASSERT(std::numeric_limits<T>::is_integer); + return FormatValW(x, Format().BaseDec().FillOff()); +} +template<int width, typename T> +inline std::wstring dec(const T& x) +{ + STATIC_ASSERT(std::numeric_limits<T>::is_integer); + return FormatValW(x, Format().BaseDec().FillSpc().Width(width)); +} +template<int width, typename T> +inline std::wstring dec0(const T& x) +{ + STATIC_ASSERT(std::numeric_limits<T>::is_integer); + return FormatValW(x, Format().BaseDec().FillNul().Width(width)); +} + +template<typename T> +inline std::wstring hex(const T& x) +{ + STATIC_ASSERT(std::numeric_limits<T>::is_integer); + return FormatValW(x, Format().BaseHex().CaseLow().FillOff()); +} +template<typename T> +inline std::wstring HEX(const T& x) +{ + STATIC_ASSERT(std::numeric_limits<T>::is_integer); + return FormatValW(x, Format().BaseHex().CaseUpp().FillOff()); +} +template<int width, typename T> +inline std::wstring hex(const T& x) +{ + STATIC_ASSERT(std::numeric_limits<T>::is_integer); + return FormatValW(x, Format().BaseHex().CaseLow().FillSpc().Width(width)); +} +template<int width, typename T> +inline std::wstring HEX(const T& x) +{ + STATIC_ASSERT(std::numeric_limits<T>::is_integer); + return FormatValW(x, Format().BaseHex().CaseUpp().FillSpc().Width(width)); +} +template<int width, typename T> +inline std::wstring hex0(const T& x) +{ + STATIC_ASSERT(std::numeric_limits<T>::is_integer); + return FormatValW(x, Format().BaseHex().CaseLow().FillNul().Width(width)); +} +template<int width, typename T> +inline std::wstring HEX0(const T& x) +{ + STATIC_ASSERT(std::numeric_limits<T>::is_integer); + return FormatValW(x, Format().BaseHex().CaseUpp().FillNul().Width(width)); +} + +template<typename T> +inline std::wstring flt(const T& x, std::size_t width = 0, int precision = -1) +{ + #if defined(HAS_TYPE_TRAITS) + STATIC_ASSERT(std::is_floating_point<T>::value); + #endif + if(width == 0) + { + return FormatValW(x, Format().NotaNrm().FillOff().Precision(precision)); + } else + { + return FormatValW(x, Format().NotaNrm().FillSpc().Width(width).Precision(precision)); + } +} +template<typename T> +inline std::wstring fix(const T& x, std::size_t width = 0, int precision = -1) +{ + #if defined(HAS_TYPE_TRAITS) + STATIC_ASSERT(std::is_floating_point<T>::value); + #endif + if(width == 0) + { + return FormatValW(x, Format().NotaFix().FillOff().Precision(precision)); + } else + { + return FormatValW(x, Format().NotaFix().FillSpc().Width(width).Precision(precision)); + } +} +template<typename T> +inline std::wstring sci(const T& x, std::size_t width = 0, int precision = -1) +{ + #if defined(HAS_TYPE_TRAITS) + STATIC_ASSERT(std::is_floating_point<T>::value); + #endif + if(width == 0) + { + return FormatValW(x, Format().NotaSci().FillOff().Precision(precision)); + } else + { + return FormatValW(x, Format().NotaSci().FillSpc().Width(width).Precision(precision)); + } +} + +} // namespace wfmt + +#endif // MPT_FMT + +} // namespace mpt + +#define Stringify(x) mpt::ToString(x) +#define StringifyW(x) mpt::ToWString(x) + +namespace mpt { namespace String { + +namespace detail +{ + +std::string PrintImpl(const std::string & format + , const std::string & x1 = std::string() + , const std::string & x2 = std::string() + , const std::string & x3 = std::string() + , const std::string & x4 = std::string() + , const std::string & x5 = std::string() + , const std::string & x6 = std::string() + , const std::string & x7 = std::string() + , const std::string & x8 = std::string() + ); + +std::wstring PrintImplW(const std::wstring & format + , const std::wstring & x1 = std::wstring() + , const std::wstring & x2 = std::wstring() + , const std::wstring & x3 = std::wstring() + , const std::wstring & x4 = std::wstring() + , const std::wstring & x5 = std::wstring() + , const std::wstring & x6 = std::wstring() + , const std::wstring & x7 = std::wstring() + , const std::wstring & x8 = std::wstring() + ); + +} // namespace detail + +template< + typename T1 +> +std::string Print(const std::string & format + , const T1& x1 +) +{ + return detail::PrintImpl(format + , ToString(x1) + ); +} + +template< + typename T1, + typename T2 +> +std::string Print(const std::string & format + , const T1& x1 + , const T2& x2 +) +{ + return detail::PrintImpl(format + , ToString(x1) + , ToString(x2) + ); +} + +template< + typename T1, + typename T2, + typename T3 +> +std::string Print(const std::string & format + , const T1& x1 + , const T2& x2 + , const T3& x3 +) +{ + return detail::PrintImpl(format + , ToString(x1) + , ToString(x2) + , ToString(x3) + ); +} + +template< + typename T1, + typename T2, + typename T3, + typename T4 +> +std::string Print(const std::string & format + , const T1& x1 + , const T2& x2 + , const T3& x3 + , const T4& x4 +) +{ + return detail::PrintImpl(format + , ToString(x1) + , ToString(x2) + , ToString(x3) + , ToString(x4) + ); +} + +template< + typename T1, + typename T2, + typename T3, + typename T4, + typename T5 +> +std::string Print(const std::string & format + , const T1& x1 + , const T2& x2 + , const T3& x3 + , const T4& x4 + , const T5& x5 +) +{ + return detail::PrintImpl(format + , ToString(x1) + , ToString(x2) + , ToString(x3) + , ToString(x4) + , ToString(x5) + ); +} + +template< + typename T1, + typename T2, + typename T3, + typename T4, + typename T5, + typename T6 +> +std::string Print(const std::string & format + , const T1& x1 + , const T2& x2 + , const T3& x3 + , const T4& x4 + , const T5& x5 + , const T6& x6 +) +{ + return detail::PrintImpl(format + , ToString(x1) + , ToString(x2) + , ToString(x3) + , ToString(x4) + , ToString(x5) + , ToString(x6) + ); +} + +template< + typename T1, + typename T2, + typename T3, + typename T4, + typename T5, + typename T6, + typename T7 +> +std::string Print(const std::string & format + , const T1& x1 + , const T2& x2 + , const T3& x3 + , const T4& x4 + , const T5& x5 + , const T6& x6 + , const T7& x7 +) +{ + return detail::PrintImpl(format + , ToString(x1) + , ToString(x2) + , ToString(x3) + , ToString(x4) + , ToString(x5) + , ToString(x6) + , ToString(x7) + ); +} + +template< + typename T1, + typename T2, + typename T3, + typename T4, + typename T5, + typename T6, + typename T7, + typename T8 +> +std::string Print(const std::string & format + , const T1& x1 + , const T2& x2 + , const T3& x3 + , const T4& x4 + , const T5& x5 + , const T6& x6 + , const T7& x7 + , const T8& x8 +) +{ + return detail::PrintImpl(format + , ToString(x1) + , ToString(x2) + , ToString(x3) + , ToString(x4) + , ToString(x5) + , ToString(x6) + , ToString(x7) + , ToString(x8) + ); +} + +template< + typename T1 +> +std::wstring PrintW(const std::wstring & fo... [truncated message content] |