]> git.saurik.com Git - wxWidgets.git/blobdiff - include/wx/strvararg.h
using Run of base class
[wxWidgets.git] / include / wx / strvararg.h
index 4cbe39b3aa0dfb940c5e33a51fd497cdc561e015..dc566e94434cd58fafc04e9f5b987e45f14ea981 100644 (file)
@@ -3,7 +3,6 @@
 // Purpose:     macros for implementing type-safe vararg passing of strings
 // Author:      Vaclav Slavik
 // Created:     2007-02-19
 // Purpose:     macros for implementing type-safe vararg passing of strings
 // Author:      Vaclav Slavik
 // Created:     2007-02-19
-// RCS-ID:      $Id$
 // Copyright:   (c) 2007 REA Elektronik GmbH
 // Licence:     wxWindows licence
 ///////////////////////////////////////////////////////////////////////////////
 // Copyright:   (c) 2007 REA Elektronik GmbH
 // Licence:     wxWindows licence
 ///////////////////////////////////////////////////////////////////////////////
 #include "wx/buffer.h"
 #include "wx/unichar.h"
 
 #include "wx/buffer.h"
 #include "wx/unichar.h"
 
+#if defined(HAVE_TYPE_TRAITS)
+    #include <type_traits>
+#elif defined(HAVE_TR1_TYPE_TRAITS)
+    #ifdef __VISUALC__
+        #include <type_traits>
+    #else
+        #include <tr1/type_traits>
+    #endif
+#endif
+
 class WXDLLIMPEXP_FWD_BASE wxCStrData;
 class WXDLLIMPEXP_FWD_BASE wxString;
 
 class WXDLLIMPEXP_FWD_BASE wxCStrData;
 class WXDLLIMPEXP_FWD_BASE wxString;
 
@@ -128,24 +137,58 @@ class WXDLLIMPEXP_BASE wxFormatString
 {
 public:
     wxFormatString(const char *str)
 {
 public:
     wxFormatString(const char *str)
-        : m_char(wxCharBuffer::CreateNonOwned(str)), m_str(NULL), m_cstr(NULL) {}
+        : m_char(wxScopedCharBuffer::CreateNonOwned(str)), m_str(NULL), m_cstr(NULL) {}
     wxFormatString(const wchar_t *str)
     wxFormatString(const wchar_t *str)
-        : m_wchar(wxWCharBuffer::CreateNonOwned(str)), m_str(NULL), m_cstr(NULL) {}
+        : m_wchar(wxScopedWCharBuffer::CreateNonOwned(str)), m_str(NULL), m_cstr(NULL) {}
     wxFormatString(const wxString& str)
         : m_str(&str), m_cstr(NULL) {}
     wxFormatString(const wxCStrData& str)
         : m_str(NULL), m_cstr(&str) {}
     wxFormatString(const wxString& str)
         : m_str(&str), m_cstr(NULL) {}
     wxFormatString(const wxCStrData& str)
         : m_str(NULL), m_cstr(&str) {}
-    wxFormatString(const wxCharBuffer& str)
+    wxFormatString(const wxScopedCharBuffer& str)
         : m_char(str), m_str(NULL), m_cstr(NULL)  {}
         : m_char(str), m_str(NULL), m_cstr(NULL)  {}
-    wxFormatString(const wxWCharBuffer& str)
+    wxFormatString(const wxScopedWCharBuffer& str)
         : m_wchar(str), m_str(NULL), m_cstr(NULL) {}
 
         : m_wchar(str), m_str(NULL), m_cstr(NULL) {}
 
-
+    // Possible argument types. These are or-combinable for wxASSERT_ARG_TYPE
+    // convenience. Some of the values are or-combined with another value, this
+    // expresses "supertypes" for use with wxASSERT_ARG_TYPE masks. For example,
+    // a char* string is also a pointer and an integer is also a char.
     enum ArgumentType
     {
     enum ArgumentType
     {
-        Arg_Char,    // character as char
+        Arg_Char        = 0x0001,    // character as char %c
+        Arg_Pointer     = 0x0002,    // %p
+        Arg_String      = 0x0004 | Arg_Pointer, // any form of string (%s and %p too)
+
+        Arg_Int         = 0x0008 | Arg_Char, // (ints can be used with %c)
+#if SIZEOF_INT == SIZEOF_LONG
+        Arg_LongInt     = Arg_Int,
+#else
+        Arg_LongInt     = 0x0010,
+#endif
+#if defined(SIZEOF_LONG_LONG) && SIZEOF_LONG_LONG == SIZEOF_LONG
+        Arg_LongLongInt = Arg_LongInt,
+#elif defined(wxLongLong_t)
+        Arg_LongLongInt = 0x0020,
+#endif
 
 
-        Arg_Other    // something else, for example int for %d
+        Arg_Double      = 0x0040,
+        Arg_LongDouble  = 0x0080,
+
+#if defined(wxSIZE_T_IS_UINT)
+        Arg_Size_t      = Arg_Int,
+#elif defined(wxSIZE_T_IS_ULONG)
+        Arg_Size_t      = Arg_LongInt,
+#elif defined(SIZEOF_LONG_LONG) && SIZEOF_SIZE_T == SIZEOF_LONG_LONG
+        Arg_Size_t      = Arg_LongLongInt,
+#else
+        Arg_Size_t      = 0x0100,
+#endif
+
+        Arg_IntPtr      = 0x0200,    // %n -- store # of chars written
+        Arg_ShortIntPtr = 0x0400,
+        Arg_LongIntPtr  = 0x0800,
+
+        Arg_Unknown     = 0x8000     // unrecognized specifier (likely error)
     };
 
     // returns the type of format specifier for n-th variadic argument (this is
     };
 
     // returns the type of format specifier for n-th variadic argument (this is
@@ -154,32 +197,36 @@ public:
     // n-th variadic argument desired representation
     ArgumentType GetArgumentType(unsigned n) const;
 
     // n-th variadic argument desired representation
     ArgumentType GetArgumentType(unsigned n) const;
 
+    // returns the value passed to ctor, only converted to wxString, similarly
+    // to other InputAsXXX() methods
+    wxString InputAsString() const;
+
 #if !wxUSE_UNICODE_WCHAR
     operator const char*() const
 #if !wxUSE_UNICODE_WCHAR
     operator const char*() const
-        { return wx_const_cast(wxFormatString*, this)->AsChar(); }
+        { return const_cast<wxFormatString*>(this)->AsChar(); }
 private:
 private:
-    // InputAsChar() returns the value converted passed to ctor, only converted
-    // to char, while AsChar() takes the the string returned by InputAsChar()
+    // InputAsChar() returns the value passed to ctor, only converted
+    // to char, while AsChar() takes the string returned by InputAsChar()
     // and does format string conversion on it as well (and similarly for
     // ..AsWChar() below)
     const char* InputAsChar();
     const char* AsChar();
     // and does format string conversion on it as well (and similarly for
     // ..AsWChar() below)
     const char* InputAsChar();
     const char* AsChar();
-    wxCharBuffer m_convertedChar;
+    wxScopedCharBuffer m_convertedChar;
 #endif // !wxUSE_UNICODE_WCHAR
 
 #if wxUSE_UNICODE && !wxUSE_UTF8_LOCALE_ONLY
 public:
     operator const wchar_t*() const
 #endif // !wxUSE_UNICODE_WCHAR
 
 #if wxUSE_UNICODE && !wxUSE_UTF8_LOCALE_ONLY
 public:
     operator const wchar_t*() const
-        { return wx_const_cast(wxFormatString*, this)->AsWChar(); }
+        { return const_cast<wxFormatString*>(this)->AsWChar(); }
 private:
     const wchar_t* InputAsWChar();
     const wchar_t* AsWChar();
 private:
     const wchar_t* InputAsWChar();
     const wchar_t* AsWChar();
-    wxWCharBuffer m_convertedWChar;
+    wxScopedWCharBuffer m_convertedWChar;
 #endif // wxUSE_UNICODE && !wxUSE_UTF8_LOCALE_ONLY
 
 private:
 #endif // wxUSE_UNICODE && !wxUSE_UTF8_LOCALE_ONLY
 
 private:
-    wxCharBuffer  m_char;
-    wxWCharBuffer m_wchar;
+    wxScopedCharBuffer  m_char;
+    wxScopedWCharBuffer m_wchar;
 
     // NB: we can use a pointer here, because wxFormatString is only used
     //     as function argument, so it has shorter life than the string
 
     // NB: we can use a pointer here, because wxFormatString is only used
     //     as function argument, so it has shorter life than the string
@@ -187,7 +234,7 @@ private:
     const wxString * const m_str;
     const wxCStrData * const m_cstr;
 
     const wxString * const m_str;
     const wxCStrData * const m_cstr;
 
-    DECLARE_NO_ASSIGN_CLASS(wxFormatString)
+    wxDECLARE_NO_ASSIGN_CLASS(wxFormatString);
 };
 
 // these two helper classes are used to find wxFormatString argument among fixed
 };
 
 // these two helper classes are used to find wxFormatString argument among fixed
@@ -227,16 +274,166 @@ struct wxFormatStringArgumentFinder<const wxFormatString&>
 
 template<>
 struct wxFormatStringArgumentFinder<wxFormatString>
 
 template<>
 struct wxFormatStringArgumentFinder<wxFormatString>
-{
-    static wxFormatStringArgument find(const wxFormatString& arg)
-        { return wxFormatStringArgument(&arg); }
-};
+    : public wxFormatStringArgumentFinder<const wxFormatString&> {};
+
+// avoid passing big objects by value to wxFormatStringArgumentFinder::find()
+// (and especially wx[W]CharBuffer with its auto_ptr<> style semantics!):
+template<>
+struct wxFormatStringArgumentFinder<wxString>
+    : public wxFormatStringArgumentFinder<const wxString&> {};
+
+template<>
+struct wxFormatStringArgumentFinder<wxScopedCharBuffer>
+    : public wxFormatStringArgumentFinder<const wxScopedCharBuffer&> {};
+
+template<>
+struct wxFormatStringArgumentFinder<wxScopedWCharBuffer>
+    : public wxFormatStringArgumentFinder<const wxScopedWCharBuffer&> {};
+
+template<>
+struct wxFormatStringArgumentFinder<wxCharBuffer>
+    : public wxFormatStringArgumentFinder<const wxCharBuffer&> {};
+
+template<>
+struct wxFormatStringArgumentFinder<wxWCharBuffer>
+    : public wxFormatStringArgumentFinder<const wxWCharBuffer&> {};
 
 
 // ----------------------------------------------------------------------------
 // wxArgNormalizer*<T> converters
 // ----------------------------------------------------------------------------
 
 
 
 // ----------------------------------------------------------------------------
 // wxArgNormalizer*<T> converters
 // ----------------------------------------------------------------------------
 
+#if wxDEBUG_LEVEL
+    // Check that the format specifier for index-th argument in 'fmt' has
+    // the correct type (one of wxFormatString::Arg_XXX or-combination in
+    // 'expected_mask').
+    #define wxASSERT_ARG_TYPE(fmt, index, expected_mask)                    \
+        wxSTATEMENT_MACRO_BEGIN                                             \
+            if ( !fmt )                                                     \
+                break;                                                      \
+            const int argtype = fmt->GetArgumentType(index);                \
+            wxASSERT_MSG( (argtype & (expected_mask)) == argtype,           \
+                          "format specifier doesn't match argument type" ); \
+        wxSTATEMENT_MACRO_END
+#else
+    // Just define it to suppress "unused parameter" warnings for the
+    // parameters which we don't use otherwise
+    #define wxASSERT_ARG_TYPE(fmt, index, expected_mask)                      \
+        wxUnusedVar(fmt);                                                     \
+        wxUnusedVar(index)
+#endif // wxDEBUG_LEVEL/!wxDEBUG_LEVEL
+
+
+#if defined(HAVE_TYPE_TRAITS) || defined(HAVE_TR1_TYPE_TRAITS)
+
+// Note: this type is misnamed, so that the error message is easier to
+// understand (no error happens for enums, because the IsEnum=true case is
+// specialized).
+template<bool IsEnum>
+struct wxFormatStringSpecifierNonPodType {};
+
+template<>
+struct wxFormatStringSpecifierNonPodType<true>
+{
+    enum { value = wxFormatString::Arg_Int };
+};
+
+template<typename T>
+struct wxFormatStringSpecifier
+{
+#ifdef HAVE_TYPE_TRAITS
+    typedef std::is_enum<T> is_enum;
+#elif defined HAVE_TR1_TYPE_TRAITS
+    typedef std::tr1::is_enum<T> is_enum;
+#endif
+    enum { value = wxFormatStringSpecifierNonPodType<is_enum::value>::value };
+};
+
+#else // !HAVE_(TR1_)TYPE_TRAITS
+
+template<typename T>
+struct wxFormatStringSpecifier
+{
+    // We can't detect enums without is_enum, so the only thing we can
+    // do is to accept unknown types. However, the only acceptable unknown
+    // types still are enums, which are promoted to ints, so return Arg_Int
+    // here. This will at least catch passing of non-POD types through ... at
+    // runtime.
+    //
+    // Furthermore, if the compiler doesn't have partial template
+    // specialization, we didn't cover pointers either.
+#ifdef HAVE_PARTIAL_SPECIALIZATION
+    enum { value = wxFormatString::Arg_Int };
+#else
+    enum { value = wxFormatString::Arg_Int | wxFormatString::Arg_Pointer };
+#endif
+};
+
+#endif // HAVE_TR1_TYPE_TRAITS/!HAVE_TR1_TYPE_TRAITS
+
+
+#ifdef HAVE_PARTIAL_SPECIALIZATION
+template<typename T>
+struct wxFormatStringSpecifier<T*>
+{
+    enum { value = wxFormatString::Arg_Pointer };
+};
+
+template<typename T>
+struct wxFormatStringSpecifier<const T*>
+{
+    enum { value = wxFormatString::Arg_Pointer };
+};
+#endif // !HAVE_PARTIAL_SPECIALIZATION
+
+
+#define wxFORMAT_STRING_SPECIFIER(T, arg)                                   \
+    template<> struct wxFormatStringSpecifier<T>                            \
+    {                                                                       \
+        enum { value = arg };                                               \
+    };
+
+wxFORMAT_STRING_SPECIFIER(bool, 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)
+wxFORMAT_STRING_SPECIFIER(short unsigned int, wxFormatString::Arg_Int)
+wxFORMAT_STRING_SPECIFIER(long int, wxFormatString::Arg_LongInt)
+wxFORMAT_STRING_SPECIFIER(long unsigned int, wxFormatString::Arg_LongInt)
+#ifdef wxLongLong_t
+wxFORMAT_STRING_SPECIFIER(wxLongLong_t, wxFormatString::Arg_LongLongInt)
+wxFORMAT_STRING_SPECIFIER(wxULongLong_t, wxFormatString::Arg_LongLongInt)
+#endif
+wxFORMAT_STRING_SPECIFIER(float, wxFormatString::Arg_Double)
+wxFORMAT_STRING_SPECIFIER(double, wxFormatString::Arg_Double)
+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
+
+#if !wxUSE_UNICODE
+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)
+#endif
+
+wxFORMAT_STRING_SPECIFIER(char*, wxFormatString::Arg_String)
+wxFORMAT_STRING_SPECIFIER(unsigned char*, wxFormatString::Arg_String)
+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(wchar_t*, wxFormatString::Arg_String)
+wxFORMAT_STRING_SPECIFIER(const wchar_t*, 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)
+
+#undef wxFORMAT_STRING_SPECIFIER
+
+
 // Converts an argument passed to wxPrint etc. into standard form expected,
 // by wxXXX functions, e.g. all strings (wxString, char*, wchar_t*) are
 // converted into wchar_t* or char* depending on the build.
 // Converts an argument passed to wxPrint etc. into standard form expected,
 // by wxXXX functions, e.g. all strings (wxString, char*, wchar_t*) are
 // converted into wchar_t* or char* depending on the build.
@@ -248,8 +445,11 @@ struct wxArgNormalizer
     // use format string and 'index' is index of 'value' in variadic arguments
     // list (starting at 1)
     wxArgNormalizer(T value,
     // use format string and 'index' is index of 'value' in variadic arguments
     // list (starting at 1)
     wxArgNormalizer(T value,
-                    const wxFormatString *WXUNUSED(fmt), unsigned WXUNUSED(index))
-        : m_value(value) {}
+                    const wxFormatString *fmt, unsigned index)
+        : m_value(value)
+    {
+        wxASSERT_ARG_TYPE( fmt, index, wxFormatStringSpecifier<T>::value );
+    }
 
     // Returns the value in a form that can be safely passed to real vararg
     // functions. In case of strings, this is char* in ANSI build and wchar_t*
 
     // Returns the value in a form that can be safely passed to real vararg
     // functions. In case of strings, this is char* in ANSI build and wchar_t*
@@ -299,13 +499,16 @@ struct wxArgNormalizerWchar : public wxArgNormalizer<T>
 template<typename CharType>
 struct wxArgNormalizerWithBuffer
 {
 template<typename CharType>
 struct wxArgNormalizerWithBuffer
 {
-    typedef wxCharTypeBuffer<CharType> CharBuffer;
+    typedef wxScopedCharTypeBuffer<CharType> CharBuffer;
 
     wxArgNormalizerWithBuffer() {}
     wxArgNormalizerWithBuffer(const CharBuffer& buf,
 
     wxArgNormalizerWithBuffer() {}
     wxArgNormalizerWithBuffer(const CharBuffer& buf,
-                              const wxFormatString *WXUNUSED(fmt),
-                              unsigned WXUNUSED(index))
-        : m_value(buf) {}
+                              const wxFormatString *fmt,
+                              unsigned index)
+        : m_value(buf)
+    {
+        wxASSERT_ARG_TYPE( fmt, index, wxFormatString::Arg_String );
+    }
 
     const CharType *get() const { return m_value; }
 
 
     const CharType *get() const { return m_value; }
 
@@ -317,9 +520,12 @@ template<>
 struct WXDLLIMPEXP_BASE wxArgNormalizerNative<const wxString&>
 {
     wxArgNormalizerNative(const wxString& s,
 struct WXDLLIMPEXP_BASE wxArgNormalizerNative<const wxString&>
 {
     wxArgNormalizerNative(const wxString& s,
-                          const wxFormatString *WXUNUSED(fmt),
-                          unsigned WXUNUSED(index))
-        : m_value(s) {}
+                          const wxFormatString *fmt,
+                          unsigned index)
+        : m_value(s)
+    {
+        wxASSERT_ARG_TYPE( fmt, index, wxFormatString::Arg_String );
+    }
 
     const wxStringCharType *get() const;
 
 
     const wxStringCharType *get() const;
 
@@ -331,9 +537,12 @@ template<>
 struct WXDLLIMPEXP_BASE wxArgNormalizerNative<const wxCStrData&>
 {
     wxArgNormalizerNative(const wxCStrData& value,
 struct WXDLLIMPEXP_BASE wxArgNormalizerNative<const wxCStrData&>
 {
     wxArgNormalizerNative(const wxCStrData& value,
-                          const wxFormatString *WXUNUSED(fmt),
-                          unsigned WXUNUSED(index))
-        : m_value(value) {}
+                          const wxFormatString *fmt,
+                          unsigned index)
+        : m_value(value)
+    {
+        wxASSERT_ARG_TYPE( fmt, index, wxFormatString::Arg_String );
+    }
 
     const wxStringCharType *get() const;
 
 
     const wxStringCharType *get() const;
 
@@ -389,17 +598,19 @@ struct wxArgNormalizerUtf8<const char*>
     : public wxArgNormalizerWithBuffer<char>
 {
     wxArgNormalizerUtf8(const char* s,
     : public wxArgNormalizerWithBuffer<char>
 {
     wxArgNormalizerUtf8(const char* s,
-                        const wxFormatString *WXUNUSED(fmt),
-                        unsigned WXUNUSED(index))
+                        const wxFormatString *fmt,
+                        unsigned index)
     {
     {
+        wxASSERT_ARG_TYPE( fmt, index, wxFormatString::Arg_String );
+
         if ( wxLocaleIsUtf8 )
         {
         if ( wxLocaleIsUtf8 )
         {
-            m_value = wxCharBuffer::CreateNonOwned(s);
+            m_value = wxScopedCharBuffer::CreateNonOwned(s);
         }
         else
         {
             // convert to widechar string first:
         }
         else
         {
             // convert to widechar string first:
-            wxWCharBuffer buf(wxConvLibc.cMB2WC(s));
+            wxScopedWCharBuffer buf(wxConvLibc.cMB2WC(s));
 
             // then to UTF-8:
             if ( buf )
 
             // then to UTF-8:
             if ( buf )
@@ -470,6 +681,10 @@ WX_ARG_NORMALIZER_FORWARD(char*, const char*);
 WX_ARG_NORMALIZER_FORWARD(wchar_t*, const wchar_t*);
 
 // versions for passing wx[W]CharBuffer:
 WX_ARG_NORMALIZER_FORWARD(wchar_t*, const wchar_t*);
 
 // versions for passing wx[W]CharBuffer:
+WX_ARG_NORMALIZER_FORWARD(wxScopedCharBuffer, const char*);
+WX_ARG_NORMALIZER_FORWARD(const wxScopedCharBuffer&, const char*);
+WX_ARG_NORMALIZER_FORWARD(wxScopedWCharBuffer, const wchar_t*);
+WX_ARG_NORMALIZER_FORWARD(const wxScopedWCharBuffer&, const wchar_t*);
 WX_ARG_NORMALIZER_FORWARD(wxCharBuffer, const char*);
 WX_ARG_NORMALIZER_FORWARD(const wxCharBuffer&, const char*);
 WX_ARG_NORMALIZER_FORWARD(wxWCharBuffer, const wchar_t*);
 WX_ARG_NORMALIZER_FORWARD(wxCharBuffer, const char*);
 WX_ARG_NORMALIZER_FORWARD(const wxCharBuffer&, const char*);
 WX_ARG_NORMALIZER_FORWARD(wxWCharBuffer, const wchar_t*);
@@ -550,10 +765,13 @@ struct wxArgNormalizerNarrowChar
     wxArgNormalizerNarrowChar(T value,
                               const wxFormatString *fmt, unsigned index)
     {
     wxArgNormalizerNarrowChar(T value,
                               const wxFormatString *fmt, unsigned index)
     {
+        wxASSERT_ARG_TYPE( fmt, index,
+                           wxFormatString::Arg_Char | wxFormatString::Arg_Int );
+
         // FIXME-UTF8: which one is better default in absence of fmt string
         //             (i.e. when used like e.g. Foo("foo", "bar", 'c', NULL)?
         if ( !fmt || fmt->GetArgumentType(index) == wxFormatString::Arg_Char )
         // FIXME-UTF8: which one is better default in absence of fmt string
         //             (i.e. when used like e.g. Foo("foo", "bar", 'c', NULL)?
         if ( !fmt || fmt->GetArgumentType(index) == wxFormatString::Arg_Char )
-            m_value = wxUniChar(value).GetValue();
+            m_value = wx_truncate_cast(T, wxUniChar(value).GetValue());
         else
             m_value = value;
     }
         else
             m_value = value;
     }
@@ -580,6 +798,15 @@ struct wxArgNormalizer<unsigned char>
         : wxArgNormalizerNarrowChar<unsigned char>(value, fmt, index) {}
 };
 
         : wxArgNormalizerNarrowChar<unsigned char>(value, fmt, index) {}
 };
 
+template<>
+struct wxArgNormalizer<signed char>
+    : public wxArgNormalizerNarrowChar<signed char>
+{
+    wxArgNormalizer(signed char value,
+                    const wxFormatString *fmt, unsigned index)
+        : wxArgNormalizerNarrowChar<signed char>(value, fmt, index) {}
+};
+
 #endif // wxUSE_UNICODE
 
 // convert references:
 #endif // wxUSE_UNICODE
 
 // convert references:
@@ -590,11 +817,14 @@ WX_ARG_NORMALIZER_FORWARD(const wchar_t&, wchar_t);
 
 WX_ARG_NORMALIZER_FORWARD(const char&, char);
 WX_ARG_NORMALIZER_FORWARD(const unsigned char&, unsigned char);
 
 WX_ARG_NORMALIZER_FORWARD(const char&, char);
 WX_ARG_NORMALIZER_FORWARD(const unsigned char&, unsigned char);
+WX_ARG_NORMALIZER_FORWARD(const signed char&, signed char);
 
 
 #undef WX_ARG_NORMALIZER_FORWARD
 #undef _WX_ARG_NORMALIZER_FORWARD_IMPL
 
 
 
 #undef WX_ARG_NORMALIZER_FORWARD
 #undef _WX_ARG_NORMALIZER_FORWARD_IMPL
 
+// NB: Don't #undef wxASSERT_ARG_TYPE here as it's also used in wx/longlong.h.
+
 // ----------------------------------------------------------------------------
 // WX_VA_ARG_STRING
 // ----------------------------------------------------------------------------
 // ----------------------------------------------------------------------------
 // WX_VA_ARG_STRING
 // ----------------------------------------------------------------------------