diff --git a/.github/workflows/code_checks.yml b/.github/workflows/code_checks.yml index a6ceed0eac..c92ec51414 100644 --- a/.github/workflows/code_checks.yml +++ b/.github/workflows/code_checks.yml @@ -90,7 +90,7 @@ jobs: if git diff origin/master \ ':!.github/workflows/code_checks.yml' \ ':!src/stc/scintilla/' \ - | grep -E '^\+.*(wxOVERRIDE|wxNOEXCEPT|\WNULL)'; then + | grep -E '^\+.*(wxOVERRIDE|wxNOEXCEPT|[^_@]NULL)'; then echo "::error ::Please use C++11 equivalents of the deprecated macros in the new code." exit 1 fi diff --git a/include/wx/log.h b/include/wx/log.h index 57f1ed89ce..dde1dceba4 100644 --- a/include/wx/log.h +++ b/include/wx/log.h @@ -26,7 +26,6 @@ typedef unsigned long wxLogLevel; // ---------------------------------------------------------------------------- #include "wx/string.h" -#include "wx/strvararg.h" // ---------------------------------------------------------------------------- // forward declarations @@ -882,7 +881,7 @@ public: // remember that fatal errors can't be disabled if ( m_level == wxLOG_FatalError || wxLog::IsLevelEnabled(m_level, wxASCII_STR(m_info.component)) ) - DoCallOnLog(format, argptr); + DoCallOnLog(wxString::FormatV(format, argptr)); } // overloads used by functions with optional leading arguments (whose @@ -912,173 +911,87 @@ public: } - // vararg functions used by wxLogXXX(): + // variadic functions used by wxLogXXX(): + + // we need to use an extra class with a more restricted set of ctors than + // wxString itself, as otherwise we'd get ambiguities between constructing + // wxString from long + struct FormatString : wxString + { + FormatString(const wxString& str) : wxString(str) {} + FormatString(const wxCStrData& str) : wxString(str) {} + FormatString(const wchar_t *str) : wxString(str) {} + FormatString(const wxScopedWCharBuffer& str) : wxString(str) {} +#ifndef wxNO_IMPLICIT_WXSTRING_ENCODING + FormatString(const char *str) : wxString(str) {} + FormatString(const wxScopedCharBuffer& str) : wxString(str) {} +#endif // !wxNO_IMPLICIT_WXSTRING_ENCODING + }; // will log the message at the level specified in the ctor // // notice that this function supposes that the caller already checked that // the level was enabled and does no checks itself - WX_DEFINE_VARARG_FUNC_VOID - ( - Log, - 1, (const wxFormatString&), - DoLog, DoLogUtf8 - ) + template + void Log(const FormatString& format, Targs... args) + { + DoCallOnLog(wxString::Format(format, args...)); + } // same as Log() but with an extra numeric or pointer parameters: this is // used to pass an optional value by storing it in m_info under the name // passed to MaybeStore() and is required to support "overloaded" versions // of wxLogStatus() and wxLogSysError() - WX_DEFINE_VARARG_FUNC_VOID - ( - Log, - 2, (long, const wxFormatString&), - DoLogWithNum, DoLogWithNumUtf8 - ) + template + void Log(long num, const FormatString& format, Targs... args) + { + Store(m_optKey, num); + + DoCallOnLog(wxString::Format(format, args...)); + } // unfortunately we can't use "void *" here as we get overload ambiguities - // with Log(wxFormatString, ...) when the first argument is a "char *" or + // with Log(FormatString, ...) when the first argument is a "char *" or // "wchar_t *" then -- so we only allow passing wxObject here, which is // ugly but fine in practice as this overload is only used by wxLogStatus() // whose first argument is a wxFrame - WX_DEFINE_VARARG_FUNC_VOID - ( - Log, - 2, (wxObject *, const wxFormatString&), - DoLogWithPtr, DoLogWithPtrUtf8 - ) + template + void Log(wxObject* ptr, const FormatString& format, Targs... args) + { + Store(m_optKey, wxPtrToUInt(ptr)); + + DoCallOnLog(wxString::Format(format, args...)); + } // log the message at the level specified as its first argument // // as the macros don't have access to the level argument in this case, this // function does check that the level is enabled itself - WX_DEFINE_VARARG_FUNC_VOID - ( - LogAtLevel, - 2, (wxLogLevel, const wxFormatString&), - DoLogAtLevel, DoLogAtLevelUtf8 - ) + template + void LogAtLevel(wxLogLevel level, const wxString& format, Targs... args) + { + if ( !wxLog::IsLevelEnabled(level, wxASCII_STR(m_info.component)) ) + return; + + DoCallOnLog(level, wxString::Format(format, args...)); + } // special versions for wxLogTrace() which is passed either string or // integer mask as first argument determining whether the message should be // logged or not - WX_DEFINE_VARARG_FUNC_VOID - ( - LogTrace, - 2, (const wxString&, const wxFormatString&), - DoLogTrace, DoLogTraceUtf8 - ) + template + void LogTrace(const wxString& mask, const wxString& format, Targs... args) + { + if ( !wxLog::IsAllowedTraceMask(mask) ) + return; + + Store(wxLOG_KEY_TRACE_MASK, mask); + + DoCallOnLog(wxString::Format(format, args...)); + } private: -#if !wxUSE_UTF8_LOCALE_ONLY - void DoLog(const wxChar *format, ...) - { - va_list argptr; - va_start(argptr, format); - DoCallOnLog(format, argptr); - va_end(argptr); - } - - void DoLogWithNum(long num, const wxChar *format, ...) - { - Store(m_optKey, num); - - va_list argptr; - va_start(argptr, format); - DoCallOnLog(format, argptr); - va_end(argptr); - } - - void DoLogWithPtr(void *ptr, const wxChar *format, ...) - { - Store(m_optKey, wxPtrToUInt(ptr)); - - va_list argptr; - va_start(argptr, format); - DoCallOnLog(format, argptr); - va_end(argptr); - } - - void DoLogAtLevel(wxLogLevel level, const wxChar *format, ...) - { - if ( !wxLog::IsLevelEnabled(level, wxASCII_STR(m_info.component)) ) - return; - - va_list argptr; - va_start(argptr, format); - DoCallOnLog(level, format, argptr); - va_end(argptr); - } - - void DoLogTrace(const wxString& mask, const wxChar *format, ...) - { - if ( !wxLog::IsAllowedTraceMask(mask) ) - return; - - Store(wxLOG_KEY_TRACE_MASK, mask); - - va_list argptr; - va_start(argptr, format); - DoCallOnLog(format, argptr); - va_end(argptr); - } -#endif // !wxUSE_UTF8_LOCALE_ONLY - -#if wxUSE_UNICODE_UTF8 - void DoLogUtf8(const char *format, ...) - { - va_list argptr; - va_start(argptr, format); - DoCallOnLog(format, argptr); - va_end(argptr); - } - - void DoLogWithNumUtf8(long num, const char *format, ...) - { - Store(m_optKey, num); - - va_list argptr; - va_start(argptr, format); - DoCallOnLog(format, argptr); - va_end(argptr); - } - - void DoLogWithPtrUtf8(void *ptr, const char *format, ...) - { - Store(m_optKey, wxPtrToUInt(ptr)); - - va_list argptr; - va_start(argptr, format); - DoCallOnLog(format, argptr); - va_end(argptr); - } - - void DoLogAtLevelUtf8(wxLogLevel level, const char *format, ...) - { - if ( !wxLog::IsLevelEnabled(level, wxASCII_STR(m_info.component)) ) - return; - - va_list argptr; - va_start(argptr, format); - DoCallOnLog(level, format, argptr); - va_end(argptr); - } - - void DoLogTraceUtf8(const wxString& mask, const char *format, ...) - { - if ( !wxLog::IsAllowedTraceMask(mask) ) - return; - - Store(wxLOG_KEY_TRACE_MASK, mask); - - va_list argptr; - va_start(argptr, format); - DoCallOnLog(format, argptr); - va_end(argptr); - } -#endif // wxUSE_UNICODE_UTF8 - - void DoCallOnLog(wxLogLevel level, const wxString& format, va_list argptr) + void DoCallOnLog(wxLogLevel level, const wxString& msg) { // As explained in wxLogRecordInfo ctor, we don't initialize its // timestamp to avoid calling time() unnecessary, but now that we are @@ -1089,12 +1002,12 @@ private: m_info.timestamp = m_info.timestampMS / 1000; #endif // WXWIN_COMPATIBILITY_3_0 - wxLog::OnLog(level, wxString::FormatV(format, argptr), m_info); + wxLog::OnLog(level, msg, m_info); } - void DoCallOnLog(const wxString& format, va_list argptr) + void DoCallOnLog(const wxString& msg) { - DoCallOnLog(m_level, format, argptr); + DoCallOnLog(m_level, msg); } @@ -1272,14 +1185,17 @@ WXDLLIMPEXP_BASE wxString wxSysErrorMsgStr(unsigned long nErrCode = 0); // define macros for defining log functions which do nothing at all #define wxDEFINE_EMPTY_LOG_FUNCTION(level) \ - WX_DEFINE_VARARG_FUNC_NOP(wxLog##level, 1, (const wxFormatString&)) \ - inline void wxVLog##level(const wxFormatString& WXUNUSED(format), \ + template \ + void wxLog##level(const wxString& WXUNUSED(format), Targs...) { } \ + inline void wxVLog##level(const wxString& WXUNUSED(format), \ va_list WXUNUSED(argptr)) { } \ #define wxDEFINE_EMPTY_LOG_FUNCTION2(level, argclass) \ - WX_DEFINE_VARARG_FUNC_NOP(wxLog##level, 2, (argclass, const wxFormatString&)) \ + template \ + void wxLog##level(argclass WXUNUSED(arg), \ + const wxString& WXUNUSED(format), Targs...) { } \ inline void wxVLog##level(argclass WXUNUSED(arg), \ - const wxFormatString& WXUNUSED(format), \ + const wxString& WXUNUSED(format), \ va_list WXUNUSED(argptr)) {} wxDEFINE_EMPTY_LOG_FUNCTION(FatalError); diff --git a/include/wx/longlong.h b/include/wx/longlong.h index fdc73878f4..dff7186710 100644 --- a/include/wx/longlong.h +++ b/include/wx/longlong.h @@ -1111,6 +1111,12 @@ namespace std // it doesn't seem necessary). #include "wx/strvararg.h" +template<> +struct wxFormatStringSpecifier +{ + enum { value = wxFormatString::Arg_LongLongInt }; +}; + template<> struct WXDLLIMPEXP_BASE wxArgNormalizer { diff --git a/include/wx/mimetype.h b/include/wx/mimetype.h index 3584e70876..236fafea31 100644 --- a/include/wx/mimetype.h +++ b/include/wx/mimetype.h @@ -118,42 +118,18 @@ private: class WXDLLIMPEXP_BASE wxFileTypeInfo { private: - void DoVarArgInit(const wxString& mimeType, - const wxString& openCmd, - const wxString& printCmd, - const wxString& desc, - va_list argptr); + // Helpers of variadic ctor. + void DoAddExts() { } + void DoAddExts(std::nullptr_t) { } - void VarArgInit(const wxString *mimeType, - const wxString *openCmd, - const wxString *printCmd, - const wxString *desc, - // the other parameters form a nullptr terminated list of - // extensions - ...); + template + void DoAddExts(const wxString& ext, Targs... extensions) + { + AddExtension(ext); + DoAddExts(extensions...); + } public: - // NB: This is a helper to get implicit conversion of variadic ctor's - // fixed arguments into something that can be passed to VarArgInit(). - // Do not use, it's used by the ctor only. - struct CtorString - { -#ifndef wxNO_IMPLICIT_WXSTRING_ENCODING - CtorString(const char *str) : m_str(str) {} -#endif - CtorString(const wchar_t *str) : m_str(str) {} - CtorString(const wxString& str) : m_str(str) {} - CtorString(const wxCStrData& str) : m_str(str) {} -#ifndef wxNO_IMPLICIT_WXSTRING_ENCODING - CtorString(const wxScopedCharBuffer& str) : m_str(str) {} -#endif - CtorString(const wxScopedWCharBuffer& str) : m_str(str) {} - - operator const wxString*() const { return &m_str; } - - wxString m_str; - }; - // ctors // Ctor specifying just the MIME type (which is mandatory), the other @@ -163,21 +139,21 @@ public: { } - // Ctor allowing to specify the values of all fields at once: - // - // wxFileTypeInfo(const wxString& mimeType, - // const wxString& openCmd, - // const wxString& printCmd, - // const wxString& desc, - // // the other parameters form a list of extensions for this - // // file type and should be terminated with nullptr - // ...); - WX_DEFINE_VARARG_FUNC_CTOR(wxFileTypeInfo, - 4, (const CtorString&, - const CtorString&, - const CtorString&, - const CtorString&), - VarArgInit, VarArgInit) + // Ctor allowing to specify the values of all fields at once and also + // allowing to specify the list of additional extensions for this file type + template + wxFileTypeInfo(const wxString& mimeType, + const wxString& openCmd, + const wxString& printCmd, + const wxString& description, + Targs... extensions) + : m_mimeType{mimeType}, + m_openCmd{openCmd}, + m_printCmd{printCmd}, + m_desc{description} + { + DoAddExts(extensions...); + } // the array elements correspond to the parameters of the ctor above in // the same order diff --git a/include/wx/msgout.h b/include/wx/msgout.h index 107c4ac486..b04fc2cf71 100644 --- a/include/wx/msgout.h +++ b/include/wx/msgout.h @@ -16,8 +16,7 @@ // ---------------------------------------------------------------------------- #include "wx/defs.h" -#include "wx/chartype.h" -#include "wx/strvararg.h" +#include "wx/string.h" // ---------------------------------------------------------------------------- // wxMessageOutput is a class abstracting formatted output target, i.e. @@ -37,22 +36,16 @@ public: static wxMessageOutput* Set(wxMessageOutput* msgout); // show a message to the user - // void Printf(const wxString& format, ...) = 0; - WX_DEFINE_VARARG_FUNC_VOID(Printf, 1, (const wxFormatString&), - DoPrintfWchar, DoPrintfUtf8) + template + void Printf(FormatString format, Targs... args) + { + Output(wxString::Format(format, args...)); + } // called by DoPrintf() to output formatted string but can also be called // directly if no formatting is needed virtual void Output(const wxString& str) = 0; -protected: -#if !wxUSE_UTF8_LOCALE_ONLY - void DoPrintfWchar(const wxChar *format, ...); -#endif -#if wxUSE_UNICODE_UTF8 - void DoPrintfUtf8(const char *format, ...); -#endif - private: static wxMessageOutput* ms_msgOut; }; diff --git a/include/wx/string.h b/include/wx/string.h index cc8f0abc51..dd77f7c326 100644 --- a/include/wx/string.h +++ b/include/wx/string.h @@ -43,6 +43,7 @@ #include "wx/beforestd.h" #include +#include #include "wx/afterstd.h" // by default we cache the mapping of the positions in UTF-8 string to the byte @@ -2195,17 +2196,34 @@ public: // formatted input/output // as sprintf(), returns the number of characters written or < 0 on error - // (take 'this' into account in attribute parameter count) - // int Printf(const wxString& format, ...); - WX_DEFINE_VARARG_FUNC(int, Printf, 1, (const wxFormatString&), - DoPrintfWchar, DoPrintfUtf8) + template + int Printf(const wxFormatString& format, Targs... args) + { + format.Validate({wxFormatStringSpecifier::value...}); + +#if wxUSE_UNICODE_UTF8 + #if !wxUSE_UTF8_LOCALE_ONLY + if ( wxLocaleIsUtf8 ) + #endif + return DoPrintfUtf8(format, wxArgNormalizerUtf8{args, nullptr, 0}.get()...); +#endif // wxUSE_UNICODE_UTF8 + +#if !wxUSE_UTF8_LOCALE_ONLY + return DoPrintfWchar(format, wxArgNormalizerWchar{args, nullptr, 0}.get()...); +#endif // !wxUSE_UTF8_LOCALE_ONLY + } + // as vprintf(), returns the number of characters written or < 0 on error int PrintfV(const wxString& format, va_list argptr); // returns the string containing the result of Printf() to it - // static wxString Format(const wxString& format, ...) WX_ATTRIBUTE_PRINTF_1; - WX_DEFINE_VARARG_FUNC(static wxString, Format, 1, (const wxFormatString&), - DoFormatWchar, DoFormatUtf8) + template + static wxString Format(const wxFormatString& format, Targs... args) + { + wxString s; + s.Printf(format, args...); + return s; + } // the same as above, but takes a va_list static wxString FormatV(const wxString& format, va_list argptr); @@ -2228,10 +2246,11 @@ public: enum stripType {leading = 0x1, trailing = 0x2, both = 0x3}; // use Printf() - // (take 'this' into account in attribute parameter count) - // int sprintf(const wxString& format, ...) WX_ATTRIBUTE_PRINTF_2; - WX_DEFINE_VARARG_FUNC(int, sprintf, 1, (const wxFormatString&), - DoPrintfWchar, DoPrintfUtf8) + template + int sprintf(const wxFormatString& format, Targs... args) + { + return Printf(format, args...); + } // use Cmp() int CompareTo(const wxChar* psz, caseCompare cmp = exact) const @@ -3375,11 +3394,9 @@ public: private: #if !wxUSE_UTF8_LOCALE_ONLY int DoPrintfWchar(const wxChar *format, ...); - static wxString DoFormatWchar(const wxChar *format, ...); #endif #if wxUSE_UNICODE_UTF8 int DoPrintfUtf8(const char *format, ...); - static wxString DoFormatUtf8(const char *format, ...); #endif private: diff --git a/include/wx/strvararg.h b/include/wx/strvararg.h index 8e319d18e0..21477cfd29 100644 --- a/include/wx/strvararg.h +++ b/include/wx/strvararg.h @@ -7,6 +7,17 @@ // Licence: wxWindows licence /////////////////////////////////////////////////////////////////////////////// +/* + This header contains helpers for implementing printf-like functions in + wxWidgets as well as legacy WX_DEFINE_VARARG_FUNC macros that are not used + by wxWidgets itself any more, but are preserved here as they can be used in + the application code (even if they had never been part of documented API). + + The only non-deprecated parts of this header are wxFormatString class and + wxFormatStringSpecifier and wxArgNormalizer templates, which are used by + wxWidgets itself. + */ + #ifndef _WX_STRVARARG_H_ #define _WX_STRVARARG_H_ @@ -20,6 +31,7 @@ #include #include +#include #ifdef __cpp_lib_string_view #include @@ -195,6 +207,10 @@ public: Arg_Unknown = 0x8000 // unrecognized specifier (likely error) }; + // Validate all format string parameters at once: the vector contains the + // format specifiers corresponding to the actually given arguments. + void Validate(const std::vector& argTypes) const; + // returns the type of format specifier for n-th variadic argument (this is // not necessarily n-th format specifier if positional specifiers are used); // called by wxArgNormalizer<> specializations to get information about @@ -335,6 +351,13 @@ struct wxFormatStringArgumentFinder #define wxASSERT_ARG_TYPE(fmt, index, expected_mask) \ wxUnusedVar(fmt); \ wxUnusedVar(index) + + // Also provide a trivial implementation of Validate() doing nothing in + // this case. + inline void + wxFormatString::Validate(const std::vector& WXUNUSED(argTypes)) const + { + } #endif // wxDEBUG_LEVEL/!wxDEBUG_LEVEL @@ -384,6 +407,9 @@ struct wxFormatStringSpecifier }; wxFORMAT_STRING_SPECIFIER(bool, wxFormatString::Arg_Int) +wxFORMAT_STRING_SPECIFIER(char, wxFormatString::Arg_Char | wxFormatString::Arg_Int) +wxFORMAT_STRING_SPECIFIER(signed char, wxFormatString::Arg_Char | wxFormatString::Arg_Int) +wxFORMAT_STRING_SPECIFIER(unsigned char, wxFormatString::Arg_Char | wxFormatString::Arg_Int) wxFORMAT_STRING_SPECIFIER(int, wxFormatString::Arg_Int) wxFORMAT_STRING_SPECIFIER(unsigned int, wxFormatString::Arg_Int) wxFORMAT_STRING_SPECIFIER(short int, wxFormatString::Arg_Int) @@ -401,6 +427,8 @@ wxFORMAT_STRING_SPECIFIER(long double, wxFormatString::Arg_LongDouble) #if wxWCHAR_T_IS_REAL_TYPE wxFORMAT_STRING_SPECIFIER(wchar_t, wxFormatString::Arg_Char | wxFormatString::Arg_Int) #endif +wxFORMAT_STRING_SPECIFIER(wxUniChar, wxFormatString::Arg_Char | wxFormatString::Arg_Int) +wxFORMAT_STRING_SPECIFIER(wxUniCharRef, wxFormatString::Arg_Char | wxFormatString::Arg_Int) #ifndef wxNO_IMPLICIT_WXSTRING_ENCODING wxFORMAT_STRING_SPECIFIER(char*, wxFormatString::Arg_String) @@ -409,6 +437,12 @@ wxFORMAT_STRING_SPECIFIER(signed char*, wxFormatString::Arg_String) wxFORMAT_STRING_SPECIFIER(const char*, wxFormatString::Arg_String) wxFORMAT_STRING_SPECIFIER(const unsigned char*, wxFormatString::Arg_String) wxFORMAT_STRING_SPECIFIER(const signed char*, wxFormatString::Arg_String) +wxFORMAT_STRING_SPECIFIER(wxCharBuffer, wxFormatString::Arg_String) +wxFORMAT_STRING_SPECIFIER(wxScopedCharBuffer, wxFormatString::Arg_String) +wxFORMAT_STRING_SPECIFIER(std::string, wxFormatString::Arg_String) +#ifdef __cpp_lib_string_view +wxFORMAT_STRING_SPECIFIER(std::string_view, wxFormatString::Arg_String) +#endif #else // wxNO_IMPLICIT_WXSTRING_ENCODING wxDISABLED_FORMAT_STRING_SPECIFIER(char*) wxDISABLED_FORMAT_STRING_SPECIFIER(unsigned char*) @@ -420,6 +454,12 @@ wxDISABLED_FORMAT_STRING_SPECIFIER(const signed char*) wxFORMAT_STRING_SPECIFIER(wchar_t*, wxFormatString::Arg_String) wxFORMAT_STRING_SPECIFIER(const wchar_t*, wxFormatString::Arg_String) +wxFORMAT_STRING_SPECIFIER(wxWCharBuffer, wxFormatString::Arg_String) +wxFORMAT_STRING_SPECIFIER(wxScopedWCharBuffer, wxFormatString::Arg_String) +wxFORMAT_STRING_SPECIFIER(wxString, wxFormatString::Arg_String) +wxFORMAT_STRING_SPECIFIER(wxCStrData, wxFormatString::Arg_String) +wxFORMAT_STRING_SPECIFIER(std::wstring, wxFormatString::Arg_String) + wxFORMAT_STRING_SPECIFIER(int*, wxFormatString::Arg_IntPtr | wxFormatString::Arg_Pointer) wxFORMAT_STRING_SPECIFIER(short int*, wxFormatString::Arg_ShortIntPtr | wxFormatString::Arg_Pointer) wxFORMAT_STRING_SPECIFIER(long int*, wxFormatString::Arg_LongIntPtr | wxFormatString::Arg_Pointer) @@ -442,6 +482,10 @@ struct wxArgNormalizer // to printf-like format string or nullptr if the variadic function doesn't // use format string and 'index' is index of 'value' in variadic arguments // list (starting at 1) + // + // Because the format string and index are used for checking for the format + // specifier mismatches and can be nullptr and 0, respectively, if they had + // been already checked using wxFormatString::Validate(). wxArgNormalizer(T value, const wxFormatString *fmt, unsigned index) : m_value(value) @@ -467,6 +511,7 @@ struct wxArgNormalizerWchar : public wxArgNormalizer const wxFormatString *fmt, unsigned index) : wxArgNormalizer(value, fmt, index) {} }; + #endif // !wxUSE_UTF8_LOCALE_ONLY // normalizer for passing arguments to functions working with UTF-8 encoded @@ -834,9 +879,7 @@ struct wxArgNormalizerNarrowChar wxASSERT_ARG_TYPE( fmt, index, wxFormatString::Arg_Char | wxFormatString::Arg_Int ); - // We use char if there is no format string at all, i.e. when used like - // e.g. Foo("foo", "bar", 'c', nullptr), but is this the bast choice? - if ( !fmt || fmt->GetArgumentType(index) == wxFormatString::Arg_Char ) + if ( fmt && fmt->GetArgumentType(index) == wxFormatString::Arg_Char ) m_value = wx_truncate_cast(T, wxUniChar(value).GetValue()); else m_value = value; diff --git a/include/wx/wxcrtvararg.h b/include/wx/wxcrtvararg.h index ccfbb758e4..4f1f043fba 100644 --- a/include/wx/wxcrtvararg.h +++ b/include/wx/wxcrtvararg.h @@ -237,21 +237,43 @@ // ---------------------------------------------------------------------------- wxGCC_ONLY_WARNING_SUPPRESS(format-nonliteral) +wxGCC_ONLY_WARNING_SUPPRESS(format-security) -WX_DEFINE_VARARG_FUNC_SANS_N0(int, wxPrintf, 1, (const wxFormatString&), - wxCRT_PrintfW, wxCRT_PrintfA) -inline int wxPrintf(const wxFormatString& s) +template +int wxPrintf(const wxFormatString& format, Targs... args) { - return wxPrintf(wxASCII_STR("%s"), s.InputAsString()); + format.Validate({wxFormatStringSpecifier::value...}); + +#if wxUSE_UNICODE_UTF8 + #if !wxUSE_UTF8_LOCALE_ONLY + if ( wxLocaleIsUtf8 ) + #endif + return wxCRT_PrintfA(format, wxArgNormalizerUtf8{args, nullptr, 0}.get()...); +#endif // wxUSE_UNICODE_UTF8 + +#if !wxUSE_UTF8_LOCALE_ONLY + return wxCRT_PrintfW(format, wxArgNormalizerWchar{args, nullptr, 0}.get()...); +#endif // !wxUSE_UTF8_LOCALE_ONLY } -WX_DEFINE_VARARG_FUNC_SANS_N0(int, wxFprintf, 2, (FILE*, const wxFormatString&), - wxCRT_FprintfW, wxCRT_FprintfA) -inline int wxFprintf(FILE *f, const wxFormatString& s) +template +int wxFprintf(FILE* fp, const wxFormatString& format, Targs... args) { - return wxFprintf(f, wxASCII_STR("%s"), s.InputAsString()); + format.Validate({wxFormatStringSpecifier::value...}); + +#if wxUSE_UNICODE_UTF8 + #if !wxUSE_UTF8_LOCALE_ONLY + if ( wxLocaleIsUtf8 ) + #endif + return wxCRT_FprintfA(fp, format, wxArgNormalizerUtf8{args, nullptr, 0}.get()...); +#endif // wxUSE_UNICODE_UTF8 + +#if !wxUSE_UTF8_LOCALE_ONLY + return wxCRT_FprintfW(fp, format, wxArgNormalizerWchar{args, nullptr, 0}.get()...); +#endif // !wxUSE_UTF8_LOCALE_ONLY } +wxGCC_ONLY_WARNING_RESTORE(format-security) wxGCC_ONLY_WARNING_RESTORE(format-nonliteral) // va_list versions of printf functions simply forward to the respective @@ -296,49 +318,70 @@ wxVfprintf(FILE *f, const wxString& format, va_list ap) #if !wxUSE_UTF8_LOCALE_ONLY int WXDLLIMPEXP_BASE wxDoSprintfWchar(char *str, const wxChar *format, ...); +int WXDLLIMPEXP_BASE wxDoSprintfWchar(wchar_t *str, const wxChar *format, ...); #endif #if wxUSE_UNICODE_UTF8 int WXDLLIMPEXP_BASE wxDoSprintfUtf8(char *str, const char *format, ...); +int WXDLLIMPEXP_BASE wxDoSprintfUtf8(wchar_t *str, const char *format, ...); #endif -WX_DEFINE_VARARG_FUNC(int, wxSprintf, 2, (char*, const wxFormatString&), - wxDoSprintfWchar, wxDoSprintfUtf8) + +template +int wxSprintf(CharType* str, const wxFormatString& format, Targs... args) +{ + format.Validate({wxFormatStringSpecifier::value...}); + +#if wxUSE_UNICODE_UTF8 + #if !wxUSE_UTF8_LOCALE_ONLY + if ( wxLocaleIsUtf8 ) + #endif + return wxDoSprintfUtf8(str, format, wxArgNormalizerUtf8{args, nullptr, 0}.get()...); +#endif // wxUSE_UNICODE_UTF8 + +#if !wxUSE_UTF8_LOCALE_ONLY + return wxDoSprintfWchar(str, format, wxArgNormalizerWchar{args, nullptr, 0}.get()...); +#endif // !wxUSE_UTF8_LOCALE_ONLY +} int WXDLLIMPEXP_BASE wxVsprintf(char *str, const wxString& format, va_list argptr); #if !wxUSE_UTF8_LOCALE_ONLY int WXDLLIMPEXP_BASE wxDoSnprintfWchar(char *str, size_t size, const wxChar *format, ...); +int WXDLLIMPEXP_BASE wxDoSnprintfWchar(wchar_t *str, size_t size, const wxChar *format, ...); #endif #if wxUSE_UNICODE_UTF8 int WXDLLIMPEXP_BASE wxDoSnprintfUtf8(char *str, size_t size, const char *format, ...); +int WXDLLIMPEXP_BASE wxDoSnprintfUtf8(wchar_t *str, size_t size, const char *format, ...); #endif -WX_DEFINE_VARARG_FUNC(int, wxSnprintf, 3, (char*, size_t, const wxFormatString&), - wxDoSnprintfWchar, wxDoSnprintfUtf8) + +template +int wxSnprintf(CharType* str, size_t size, const wxFormatString& format, Targs... args) +{ + format.Validate({wxFormatStringSpecifier::value...}); + +#if wxUSE_UNICODE_UTF8 + #if !wxUSE_UTF8_LOCALE_ONLY + if ( wxLocaleIsUtf8 ) + #endif + return wxDoSnprintfUtf8(str, size, format, wxArgNormalizerUtf8{args, nullptr, 0}.get()...); +#endif // wxUSE_UNICODE_UTF8 + +#if !wxUSE_UTF8_LOCALE_ONLY + return wxDoSnprintfWchar(str, size, format, wxArgNormalizerWchar{args, nullptr, 0}.get()...); +#endif // !wxUSE_UTF8_LOCALE_ONLY +} int WXDLLIMPEXP_BASE wxVsnprintf(char *str, size_t size, const wxString& format, va_list argptr); #if !wxUSE_UTF8_LOCALE_ONLY -int WXDLLIMPEXP_BASE wxDoSprintfWchar(wchar_t *str, const wxChar *format, ...); #endif #if wxUSE_UNICODE_UTF8 -int WXDLLIMPEXP_BASE wxDoSprintfUtf8(wchar_t *str, const char *format, ...); #endif -WX_DEFINE_VARARG_FUNC(int, wxSprintf, 2, (wchar_t*, const wxFormatString&), - wxDoSprintfWchar, wxDoSprintfUtf8) int WXDLLIMPEXP_BASE wxVsprintf(wchar_t *str, const wxString& format, va_list argptr); -#if !wxUSE_UTF8_LOCALE_ONLY -int WXDLLIMPEXP_BASE wxDoSnprintfWchar(wchar_t *str, size_t size, const wxChar *format, ...); -#endif -#if wxUSE_UNICODE_UTF8 -int WXDLLIMPEXP_BASE wxDoSnprintfUtf8(wchar_t *str, size_t size, const char *format, ...); -#endif -WX_DEFINE_VARARG_FUNC(int, wxSnprintf, 3, (wchar_t*, size_t, const wxFormatString&), - wxDoSnprintfWchar, wxDoSnprintfUtf8) - int WXDLLIMPEXP_BASE wxVsnprintf(wchar_t *str, size_t size, const wxString& format, va_list argptr); diff --git a/interface/wx/mimetype.h b/interface/wx/mimetype.h index e1f7b21f3c..346fbc30c0 100644 --- a/interface/wx/mimetype.h +++ b/interface/wx/mimetype.h @@ -433,16 +433,19 @@ public: /** Constructor allowing to specify all the fields at once. - This is a vararg constructor taking an arbitrary number of extensions - after the first four required parameters. The list must be terminated - by @NULL. + This is a variadic constructor taking an arbitrary number of extensions + (which can be strings of any kind) after the four required parameters. + + In wxWidgets versions before 3.3.0 the list of extensions had to be + terminated with @NULL, but this is not the case any more, trailing + @NULL is still allowed, but will be ignored. */ + template wxFileTypeInfo(const wxString& mimeType, const wxString& openCmd, const wxString& printCmd, const wxString& description, - const wxString& extension, - ...); + Targs... extensions); /** Constructor using an array of string elements corresponding to the diff --git a/src/common/appbase.cpp b/src/common/appbase.cpp index 78b537fe81..2ce0050244 100644 --- a/src/common/appbase.cpp +++ b/src/common/appbase.cpp @@ -972,7 +972,7 @@ wxString wxAppTraitsBase::GetAssertStackTrace() #if !defined(__WINDOWS__) // on Unix stack frame generation may take some time, depending on the // size of the executable mainly... warn the user that we are working - wxFprintf(stderr, "Collecting stack trace information, please wait..."); + wxFputs("Collecting stack trace information, please wait...", stderr); fflush(stderr); #endif // !__WINDOWS__ diff --git a/src/common/filename.cpp b/src/common/filename.cpp index 82811c56bf..285beeefc2 100644 --- a/src/common/filename.cpp +++ b/src/common/filename.cpp @@ -1341,7 +1341,7 @@ bool wxFileName::Rmdir(const wxString& dir, int flags) { // SHFileOperation may return non-Win32 error codes, so don't use // wxLogApiError() as the error message used by it could be wrong. - wxLogDebug(wxS("SHFileOperation(FO_DELETE) failed: error 0x%08lx"), + wxLogDebug(wxS("SHFileOperation(FO_DELETE) failed: error 0x%08x"), ret); return false; } diff --git a/src/common/mimecmn.cpp b/src/common/mimecmn.cpp index df1570dfed..ff84a35027 100644 --- a/src/common/mimecmn.cpp +++ b/src/common/mimecmn.cpp @@ -103,55 +103,6 @@ wxString wxMimeTypeCommands::GetVerbCmd(size_t n) const // wxFileTypeInfo // ---------------------------------------------------------------------------- -void wxFileTypeInfo::DoVarArgInit(const wxString& mimeType, - const wxString& openCmd, - const wxString& printCmd, - const wxString& desc, - va_list argptr) -{ - m_mimeType = mimeType; - m_openCmd = openCmd; - m_printCmd = printCmd; - m_desc = desc; - - for ( ;; ) - { - // icc gives this warning in its own va_arg() macro, argh -#ifdef __INTELC__ - #pragma warning(push) - #pragma warning(disable: 1684) -#endif - - wxArgNormalizedString ext(WX_VA_ARG_STRING(argptr)); - -#ifdef __INTELC__ - #pragma warning(pop) -#endif - if ( !ext ) - { - // nullptr terminates the list - break; - } - - m_exts.Add(ext.GetString()); - } -} - -void wxFileTypeInfo::VarArgInit(const wxString *mimeType, - const wxString *openCmd, - const wxString *printCmd, - const wxString *desc, - ...) -{ - va_list argptr; - va_start(argptr, desc); - - DoVarArgInit(*mimeType, *openCmd, *printCmd, *desc, argptr); - - va_end(argptr); -} - - wxFileTypeInfo::wxFileTypeInfo(const wxArrayString& sArray) : m_mimeType(sArray[0u]) , m_openCmd( sArray[1u]) diff --git a/src/common/msgout.cpp b/src/common/msgout.cpp index dcce3d4e1d..73111f6fba 100644 --- a/src/common/msgout.cpp +++ b/src/common/msgout.cpp @@ -69,34 +69,6 @@ wxMessageOutput* wxMessageOutput::Set(wxMessageOutput* msgout) return old; } -#if !wxUSE_UTF8_LOCALE_ONLY -void wxMessageOutput::DoPrintfWchar(const wxChar *format, ...) -{ - va_list args; - va_start(args, format); - wxString out; - - out.PrintfV(format, args); - va_end(args); - - Output(out); -} -#endif // !wxUSE_UTF8_LOCALE_ONLY - -#if wxUSE_UNICODE_UTF8 -void wxMessageOutput::DoPrintfUtf8(const char *format, ...) -{ - va_list args; - va_start(args, format); - wxString out; - - out.PrintfV(format, args); - va_end(args); - - Output(out); -} -#endif // wxUSE_UNICODE_UTF8 - // ---------------------------------------------------------------------------- // wxMessageOutputBest // ---------------------------------------------------------------------------- diff --git a/src/common/string.cpp b/src/common/string.cpp index b763b65f4e..0c1b2c9107 100644 --- a/src/common/string.cpp +++ b/src/common/string.cpp @@ -1679,38 +1679,6 @@ wxString wxString::FromCDouble(double val, int precision) // formatted output // --------------------------------------------------------------------------- -#if !wxUSE_UTF8_LOCALE_ONLY -/* static */ -wxString wxString::DoFormatWchar(const wxChar *format, ...) -{ - va_list argptr; - va_start(argptr, format); - - wxString s; - s.PrintfV(format, argptr); - - va_end(argptr); - - return s; -} -#endif // !wxUSE_UTF8_LOCALE_ONLY - -#if wxUSE_UNICODE_UTF8 -/* static */ -wxString wxString::DoFormatUtf8(const char *format, ...) -{ - va_list argptr; - va_start(argptr, format); - - wxString s; - s.PrintfV(format, argptr); - - va_end(argptr); - - return s; -} -#endif // wxUSE_UNICODE_UTF8 - /* static */ wxString wxString::FormatV(const wxString& format, va_list argptr) { diff --git a/src/common/strvararg.cpp b/src/common/strvararg.cpp index fa6fb40bdd..dafdd2cf74 100644 --- a/src/common/strvararg.cpp +++ b/src/common/strvararg.cpp @@ -653,31 +653,9 @@ wxString wxFormatString::InputAsString() const namespace { -template -wxFormatString::ArgumentType DoGetArgumentType(const CharType *format, - unsigned n) +wxFormatString::ArgumentType ArgTypeFromParamType(wxPrintfArgType type) { - wxCHECK_MSG( format, wxFormatString::Arg_Unknown, - "empty format string not allowed here" ); - - wxPrintfConvSpecParser parser(format); - - if ( n > parser.nargs ) - { - // The n-th argument doesn't appear in the format string and is unused. - // This can happen e.g. if a translation of the format string is used - // and the translation language tends to avoid numbers in singular forms. - // The translator would then typically replace "%d" with "One" (e.g. in - // Hebrew). Passing too many vararg arguments does not harm, so its - // better to be more permissive here and allow legitimate uses in favour - // of catching harmless errors. - return wxFormatString::Arg_Unused; - } - - wxCHECK_MSG( parser.pspec[n-1] != nullptr, wxFormatString::Arg_Unknown, - "requested argument not found - invalid format string?" ); - - switch ( parser.pspec[n-1]->m_type ) + switch ( type ) { case wxPAT_CHAR: case wxPAT_WCHAR: @@ -727,6 +705,91 @@ wxFormatString::ArgumentType DoGetArgumentType(const CharType *format, return wxFormatString::Arg_Unknown; } +template +wxFormatString::ArgumentType DoGetArgumentType(const CharType *format, + unsigned n) +{ + wxCHECK_MSG( format, wxFormatString::Arg_Unknown, + "empty format string not allowed here" ); + + wxPrintfConvSpecParser parser(format); + + if ( n > parser.nargs ) + { + // The n-th argument doesn't appear in the format string and is unused. + // This can happen e.g. if a translation of the format string is used + // and the translation language tends to avoid numbers in singular forms. + // The translator would then typically replace "%d" with "One" (e.g. in + // Hebrew). Passing too many vararg arguments does not harm, so its + // better to be more permissive here and allow legitimate uses in favour + // of catching harmless errors. + return wxFormatString::Arg_Unused; + } + + wxCHECK_MSG( parser.pspec[n-1] != nullptr, wxFormatString::Arg_Unknown, + "requested argument not found - invalid format string?" ); + + return ArgTypeFromParamType(parser.pspec[n-1]->m_type); +} + +#if wxDEBUG_LEVEL + +template +void DoValidateFormat(const CharType* format, const std::vector& argTypes) +{ + wxPrintfConvSpecParser parser(format); + + // For the reasons mentioned in the comment in DoGetArgumentType() above, + // we ignore any extraneous argument types, so we only check that the + // format format specifiers we actually have match the types. + for ( unsigned n = 0; n < parser.nargs; ++n ) + { + if ( n == argTypes.size() ) + { + wxFAIL_MSG + ( + wxString::Format + ( + "Not enough arguments, %zu given but at least %u needed", + argTypes.size(), + parser.nargs + ) + ); + + // Useless to continue further. + return; + } + + auto const pspec = parser.pspec[n]; + if ( !pspec ) + { + wxFAIL_MSG + ( + wxString::Format + ( + "Missing format specifier for argument %u in \"%s\"", + n + 1, format + ) + ); + + continue; + } + + auto const ptype = ArgTypeFromParamType(pspec->m_type); + wxASSERT_MSG + ( + (ptype & argTypes[n]) == ptype, + wxString::Format + ( + "Format specifier mismatch for argument %u of \"%s\"", + n + 1, format + ) + ); + } +} + +#endif // wxDEBUG_LEVEL + } // anonymous namespace wxFormatString::ArgumentType wxFormatString::GetArgumentType(unsigned n) const @@ -743,3 +806,19 @@ wxFormatString::ArgumentType wxFormatString::GetArgumentType(unsigned n) const wxFAIL_MSG( "unreachable code" ); return Arg_Unknown; } + +#if wxDEBUG_LEVEL + +void wxFormatString::Validate(const std::vector& argTypes) const +{ + if ( m_char ) + DoValidateFormat(m_char.data(), argTypes); + else if ( m_wchar ) + DoValidateFormat(m_wchar.data(), argTypes); + else if ( m_str ) + DoValidateFormat(m_str->wx_str(), argTypes); + else if ( m_cstr ) + DoValidateFormat(m_cstr->AsInternal(), argTypes); +} + +#endif // wxDEBUG_LEVEL diff --git a/tests/misc/misctests.cpp b/tests/misc/misctests.cpp index a95527f2c0..39edeae3dd 100644 --- a/tests/misc/misctests.cpp +++ b/tests/misc/misctests.cpp @@ -17,6 +17,7 @@ #include "wx/defs.h" #include "wx/math.h" +#include "wx/mimetype.h" // just some classes using wxRTTI for wxStaticCast() test #include "wx/tarstrm.h" @@ -213,3 +214,30 @@ TEST_CASE("wxMulDivInt32", "[math]") // Check that it doesn't overflow. CHECK( wxMulDivInt32((INT_MAX - 1)/2, 200, 100) == INT_MAX - 1 ); } + +#if wxUSE_MIMETYPE +TEST_CASE("wxFileTypeInfo", "[mime]") +{ + SECTION("no extensions") + { + wxFileTypeInfo fti("binary/*", "", wxString{}, L"plain binary"); + REQUIRE( fti.GetExtensionsCount() == 0 ); + } + + SECTION("extension without null at the end") + { + wxFileTypeInfo fti("image/png", "", wxEmptyString, "PNG image", "png"); + REQUIRE( fti.GetExtensionsCount() == 1 ); + CHECK( fti.GetExtensions()[0] == "png" ); + } + + SECTION("two extensions with null at the end") + { + wxFileTypeInfo fti("image/jpeg", "", "", "JPEG image", + "jpg", L"jpeg", nullptr); + REQUIRE( fti.GetExtensionsCount() == 2 ); + CHECK( fti.GetExtensions()[0] == "jpg" ); + CHECK( fti.GetExtensions()[1] == "jpeg" ); + } +} +#endif // wxUSE_MIMETYPE diff --git a/tests/strings/vararg.cpp b/tests/strings/vararg.cpp index 4bb8ea0faf..e5902d60a5 100644 --- a/tests/strings/vararg.cpp +++ b/tests/strings/vararg.cpp @@ -232,6 +232,7 @@ TEST_CASE("ArgsValidation", "[wxString][vararg][error]") #endif // but these are not: + WX_ASSERT_FAILS_WITH_ASSERT( wxString::Format("%d + %d = %d", 2, 2) ); WX_ASSERT_FAILS_WITH_ASSERT( wxString::Format("%i", "foo") ); WX_ASSERT_FAILS_WITH_ASSERT( wxString::Format("%s", (void*)&written) ); WX_ASSERT_FAILS_WITH_ASSERT( wxString::Format("%d", ptr) ); diff --git a/utils/wxrc/wxrc.cpp b/utils/wxrc/wxrc.cpp index 5e0cff4473..137e6978eb 100644 --- a/utils/wxrc/wxrc.cpp +++ b/utils/wxrc/wxrc.cpp @@ -417,7 +417,7 @@ wxArrayString XmlResApp::PrepareTempFiles() for (size_t i = 0; i < parFiles.GetCount(); i++) { if (flagVerbose) - wxPrintf(wxT("processing ") + parFiles[i] + wxT("...\n")); + wxPrintf(wxT("processing %s...\n"), parFiles[i]); wxXmlDocument doc; @@ -529,7 +529,7 @@ void XmlResApp::FindFilesInXML(wxXmlNode *node, wxArrayString& flist, const wxSt fullname = inputPath + wxFILE_SEP_PATH + n->GetContent(); if (flagVerbose) - wxPrintf(wxT("adding ") + fullname + wxT("...\n")); + wxPrintf(wxT("adding %s...\n"), fullname); wxString filename = GetInternalFileName(n->GetContent(), flist); n->SetContent(filename); @@ -569,7 +569,7 @@ void XmlResApp::MakePackageZIP(const wxArrayString& flist) files.RemoveLast(); if (flagVerbose) - wxPrintf(wxT("compressing ") + parOutput + wxT("...\n")); + wxPrintf(wxT("compressing %s...\n"), parOutput); wxString cwd = wxGetCwd(); wxSetWorkingDirectory(parOutputPath); @@ -639,7 +639,7 @@ void XmlResApp::MakePackageCPP(const wxArrayString& flist) unsigned i; if (flagVerbose) - wxPrintf(wxT("creating C++ source file ") + parOutput + wxT("...\n")); + wxPrintf(wxT("creating C++ source file %s...\n"), parOutput); file.Write("" "//\n" @@ -797,7 +797,7 @@ void XmlResApp::MakePackagePython(const wxArrayString& flist) unsigned i; if (flagVerbose) - wxPrintf(wxT("creating Python source file ") + parOutput + wxT("...\n")); + wxPrintf(wxT("creating Python source file %s...\n"), parOutput); file.Write( "#\n" @@ -881,7 +881,7 @@ ExtractedStrings XmlResApp::FindStrings() for (size_t i = 0; i < parFiles.GetCount(); i++) { if (flagVerbose) - wxPrintf(wxT("processing ") + parFiles[i] + wxT("...\n")); + wxPrintf(wxT("processing %s...\n"), parFiles[i]); wxXmlDocument doc; if (!doc.Load(parFiles[i]))