Merge branch 'use-template-for-vararg-funcs'

Use variadic templates for implementing the vararg functions.

See #22981.
This commit is contained in:
Vadim Zeitlin 2022-11-26 16:53:41 +01:00
commit d0af75e88d
18 changed files with 395 additions and 399 deletions

View file

@ -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

View file

@ -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 <typename... Targs>
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 <typename... Targs>
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 <typename... Targs>
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 <typename... Targs>
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 <typename... Targs>
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 <typename... Targs> \
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 <typename... Targs> \
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);

View file

@ -1111,6 +1111,12 @@ namespace std
// it doesn't seem necessary).
#include "wx/strvararg.h"
template<>
struct wxFormatStringSpecifier<wxLongLong>
{
enum { value = wxFormatString::Arg_LongLongInt };
};
template<>
struct WXDLLIMPEXP_BASE wxArgNormalizer<wxLongLong>
{

View file

@ -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 <typename... Targs>
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 <typename... Targs>
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

View file

@ -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 <typename FormatString, typename... Targs>
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;
};

View file

@ -43,6 +43,7 @@
#include "wx/beforestd.h"
#include <string>
#include <utility>
#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 <typename... Targs>
int Printf(const wxFormatString& format, Targs... args)
{
format.Validate({wxFormatStringSpecifier<Targs>::value...});
#if wxUSE_UNICODE_UTF8
#if !wxUSE_UTF8_LOCALE_ONLY
if ( wxLocaleIsUtf8 )
#endif
return DoPrintfUtf8(format, wxArgNormalizerUtf8<Targs>{args, nullptr, 0}.get()...);
#endif // wxUSE_UNICODE_UTF8
#if !wxUSE_UTF8_LOCALE_ONLY
return DoPrintfWchar(format, wxArgNormalizerWchar<Targs>{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 <typename... Targs>
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 <typename... Targs>
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:

View file

@ -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 <string>
#include <type_traits>
#include <vector>
#ifdef __cpp_lib_string_view
#include <string_view>
@ -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<int>& 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<wxWCharBuffer>
#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<int>& WXUNUSED(argTypes)) const
{
}
#endif // wxDEBUG_LEVEL/!wxDEBUG_LEVEL
@ -384,6 +407,9 @@ struct wxFormatStringSpecifier<const T*>
};
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<T>
const wxFormatString *fmt, unsigned index)
: wxArgNormalizer<T>(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;

View file

@ -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 <typename... Targs>
int wxPrintf(const wxFormatString& format, Targs... args)
{
return wxPrintf(wxASCII_STR("%s"), s.InputAsString());
format.Validate({wxFormatStringSpecifier<Targs>::value...});
#if wxUSE_UNICODE_UTF8
#if !wxUSE_UTF8_LOCALE_ONLY
if ( wxLocaleIsUtf8 )
#endif
return wxCRT_PrintfA(format, wxArgNormalizerUtf8<Targs>{args, nullptr, 0}.get()...);
#endif // wxUSE_UNICODE_UTF8
#if !wxUSE_UTF8_LOCALE_ONLY
return wxCRT_PrintfW(format, wxArgNormalizerWchar<Targs>{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 <typename... Targs>
int wxFprintf(FILE* fp, const wxFormatString& format, Targs... args)
{
return wxFprintf(f, wxASCII_STR("%s"), s.InputAsString());
format.Validate({wxFormatStringSpecifier<Targs>::value...});
#if wxUSE_UNICODE_UTF8
#if !wxUSE_UTF8_LOCALE_ONLY
if ( wxLocaleIsUtf8 )
#endif
return wxCRT_FprintfA(fp, format, wxArgNormalizerUtf8<Targs>{args, nullptr, 0}.get()...);
#endif // wxUSE_UNICODE_UTF8
#if !wxUSE_UTF8_LOCALE_ONLY
return wxCRT_FprintfW(fp, format, wxArgNormalizerWchar<Targs>{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 <typename CharType, typename... Targs>
int wxSprintf(CharType* str, const wxFormatString& format, Targs... args)
{
format.Validate({wxFormatStringSpecifier<Targs>::value...});
#if wxUSE_UNICODE_UTF8
#if !wxUSE_UTF8_LOCALE_ONLY
if ( wxLocaleIsUtf8 )
#endif
return wxDoSprintfUtf8(str, format, wxArgNormalizerUtf8<Targs>{args, nullptr, 0}.get()...);
#endif // wxUSE_UNICODE_UTF8
#if !wxUSE_UTF8_LOCALE_ONLY
return wxDoSprintfWchar(str, format, wxArgNormalizerWchar<Targs>{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 <typename CharType, typename... Targs>
int wxSnprintf(CharType* str, size_t size, const wxFormatString& format, Targs... args)
{
format.Validate({wxFormatStringSpecifier<Targs>::value...});
#if wxUSE_UNICODE_UTF8
#if !wxUSE_UTF8_LOCALE_ONLY
if ( wxLocaleIsUtf8 )
#endif
return wxDoSnprintfUtf8(str, size, format, wxArgNormalizerUtf8<Targs>{args, nullptr, 0}.get()...);
#endif // wxUSE_UNICODE_UTF8
#if !wxUSE_UTF8_LOCALE_ONLY
return wxDoSnprintfWchar(str, size, format, wxArgNormalizerWchar<Targs>{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);

View file

@ -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 <typename... Targs>
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

View file

@ -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__

View file

@ -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;
}

View file

@ -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])

View file

@ -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
// ----------------------------------------------------------------------------

View file

@ -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)
{

View file

@ -653,31 +653,9 @@ wxString wxFormatString::InputAsString() const
namespace
{
template<typename CharType>
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<CharType> 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<typename CharType>
wxFormatString::ArgumentType DoGetArgumentType(const CharType *format,
unsigned n)
{
wxCHECK_MSG( format, wxFormatString::Arg_Unknown,
"empty format string not allowed here" );
wxPrintfConvSpecParser<CharType> 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<typename CharType>
void DoValidateFormat(const CharType* format, const std::vector<int>& argTypes)
{
wxPrintfConvSpecParser<CharType> 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<int>& 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

View file

@ -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

View file

@ -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) );

View file

@ -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]))