From 0bf14ab8b14d3a999fe1159d2bec33740e066a53 Mon Sep 17 00:00:00 2001 From: Jaakko Salli Date: Fri, 16 Apr 2010 14:33:52 +0000 Subject: [PATCH] wxVariant <-> wxAny conversion functionality. Includes implicit construction of wxAny from wxVariant, and vice versa. wxVariant->wxAny conversion is implemented by adding new virtual member function into wxVariantData. wxAny->wxVariant conversion is implemented by associating wxAnyValueTypes with functions that generate wxVariantData instances (using a hash map). git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@64000 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- include/wx/any.h | 134 +++++++++++++++++++++++++++ include/wx/variant.h | 95 ++++++++++++++++++- interface/wx/any.h | 28 +++++- interface/wx/variant.h | 83 ++++++++++++++++- src/common/any.cpp | 155 ++++++++++++++++++++++++++++++- src/common/variant.cpp | 205 +++++++++++++++++++++++++++++++++++++++++ tests/any/anytest.cpp | 177 ++++++++++++++++++++++++++++++++++- 7 files changed, 868 insertions(+), 9 deletions(-) diff --git a/include/wx/any.h b/include/wx/any.h index 4ddd7b4c1c..e20ab0e78d 100644 --- a/include/wx/any.h +++ b/include/wx/any.h @@ -521,6 +521,76 @@ public: \ //wxDECLARE_ANY_TYPE(wxArrayString, WXDLLIMPEXP_BASE) +#if wxUSE_VARIANT + +class WXDLLIMPEXP_FWD_BASE wxAnyToVariantRegistration; + +// Because of header inter-dependencies, cannot include this earlier +#include "wx/variant.h" + +// +// wxVariantData* data type implementation. For cases when appropriate +// wxAny<->wxVariant conversion code is missing. +// + +class WXDLLIMPEXP_BASE wxAnyValueTypeImplVariantData : + public wxAnyValueTypeImplBase +{ + WX_DECLARE_ANY_VALUE_TYPE(wxAnyValueTypeImplVariantData) +public: + wxAnyValueTypeImplVariantData() : + wxAnyValueTypeImplBase() { } + virtual ~wxAnyValueTypeImplVariantData() { } + + virtual void DeleteValue(wxAnyValueBuffer& buf) const + { + wxVariantData* data = static_cast(buf.m_ptr); + if ( data ) + data->DecRef(); + } + + virtual void CopyBuffer(const wxAnyValueBuffer& src, + wxAnyValueBuffer& dst) const + { + wxVariantData* data = static_cast(src.m_ptr); + if ( data ) + data->IncRef(); + dst.m_ptr = data; + } + + static void SetValue(wxVariantData* value, + wxAnyValueBuffer& buf) + { + value->IncRef(); + buf.m_ptr = value; + } + + static wxVariantData* GetValue(const wxAnyValueBuffer& buf) + { + return static_cast(buf.m_ptr); + } + + virtual bool ConvertValue(const wxAnyValueBuffer& src, + wxAnyValueType* dstType, + wxAnyValueBuffer& dst) const + { + wxUnusedVar(src); + wxUnusedVar(dstType); + wxUnusedVar(dst); + return false; + } +}; + +template<> +class wxAnyValueTypeImpl : + public wxAnyValueTypeImplVariantData +{ +public: + wxAnyValueTypeImpl() : wxAnyValueTypeImplVariantData() { } + virtual ~wxAnyValueTypeImpl() { } +}; + +#endif // wxUSE_VARIANT #ifdef __VISUALC6__ // Re-enable useless VC6 warnings @@ -563,6 +633,22 @@ bool operator==(TUS value) const \ } +#if wxUSE_VARIANT + +// Note that the following functions are implemented outside wxAny class +// so that it can reside entirely in header and lack the export declaration. + +// Helper function used to associate wxAnyValueType with a wxVariantData. +extern WXDLLIMPEXP_BASE void +wxPreRegisterAnyToVariant(wxAnyToVariantRegistration* reg); + +// This function performs main wxAny to wxVariant conversion duties. +extern WXDLLIMPEXP_BASE bool +wxConvertAnyToVariant(const wxAny& any, wxVariant* variant); + +#endif // wxUSE_VARIANT + + // // The wxAny class represents a container for any type. A variant's value // can be changed at run time, possibly to a different type of value. @@ -610,6 +696,14 @@ public: AssignAny(any); } +#if wxUSE_VARIANT + wxAny(const wxVariant& variant) + { + m_type = wxAnyNullValueType; + AssignVariant(variant); + } +#endif + template wxAny(const T& value) { @@ -676,6 +770,14 @@ public: return *this; } +#if wxUSE_VARIANT + wxAny& operator=(const wxVariant &variant) + { + AssignVariant(variant); + return *this; + } +#endif + template wxAny& operator=(const T &value) { @@ -810,6 +912,14 @@ public: return true; } +#if wxUSE_VARIANT + // GetAs() wxVariant specialization + bool GetAs(wxVariant* value) const + { + return wxConvertAnyToVariant(*this, value); + } +#endif + private: // Assignment functions void AssignAny(const wxAny& any) @@ -825,6 +935,30 @@ private: newType->CopyBuffer(any.m_buffer, m_buffer); } +#if wxUSE_VARIANT + void AssignVariant(const wxVariant& variant) + { + wxVariantData* data = variant.GetData(); + + if ( data && data->GetAsAny(this) ) + return; + + m_type->DeleteValue(m_buffer); + + if ( variant.IsNull() ) + { + // Init as Null + m_type = wxAnyNullValueType; + } + else + { + // If everything else fails, wrap the whole wxVariantData + m_type = wxAnyValueTypeImpl::sm_instance; + wxAnyValueTypeImpl::SetValue(data, m_buffer); + } + } +#endif + template void Assign(const T &value) { diff --git a/include/wx/variant.h b/include/wx/variant.h index b1b18da47f..2f54c1b404 100644 --- a/include/wx/variant.h +++ b/include/wx/variant.h @@ -29,6 +29,8 @@ #include "wx/iosfwrap.h" +class wxAny; + /* * wxVariantData stores the actual data in a wxVariant object, * to allow it to store any type of data. @@ -78,6 +80,11 @@ public: // a copy of the data. virtual wxVariantData* Clone() const { return NULL; } +#if wxUSE_ANY + // Converts value to wxAny, if possible. Return true if successful. + virtual bool GetAsAny(wxAny* WXUNUSED(any)) const { return false; } +#endif + protected: // Protected dtor should make some incompatible code // break more louder. That is, they should do data->DecRef() @@ -101,6 +108,9 @@ public: wxVariant(const wxVariant& variant); wxVariant(wxVariantData* data, const wxString& name = wxEmptyString); +#if wxUSE_ANY + wxVariant(const wxAny& any); +#endif virtual ~wxVariant(); // generic assignment @@ -150,6 +160,10 @@ public: // write contents to a string (e.g. for debugging) wxString MakeString() const; +#if wxUSE_ANY + wxAny GetAny() const; +#endif + // double wxVariant(double val, const wxString& name = wxEmptyString); bool operator== (double value) const; @@ -341,6 +355,83 @@ private: DECLARE_DYNAMIC_CLASS(wxVariant) }; + +// +// wxVariant <-> wxAny conversion code +// +#if wxUSE_ANY + +#include "wx/any.h" + +// In order to convert wxAny to wxVariant, we need to be able to associate +// wxAnyValueType with a wxVariantData factory function. +typedef wxVariantData* (*wxVariantDataFactory)(const wxAny& any); + +// Actual Any-to-Variant registration must be postponed to a time when all +// global variables have been initialized. Hence this arrangement. +// wxAnyToVariantRegistration instances are kept in global scope and +// wxAnyValueTypeGlobals in any.cpp will use their data when the time is +// right. +class WXDLLIMPEXP_BASE wxAnyToVariantRegistration +{ +public: + wxAnyToVariantRegistration(wxVariantDataFactory factory); + + virtual wxAnyValueType* GetAssociatedType() = 0; + wxVariantDataFactory GetFactory() const { return m_factory; } +private: + wxVariantDataFactory m_factory; +}; + +template +class wxAnyToVariantRegistrationImpl : public wxAnyToVariantRegistration +{ +public: + wxAnyToVariantRegistrationImpl(wxVariantDataFactory factory) + : wxAnyToVariantRegistration(factory) + { + } + + virtual wxAnyValueType* GetAssociatedType() + { + return wxAnyValueTypeImpl::GetInstance(); + } +private: +}; + +#define DECLARE_WXANY_CONVERSION() \ +virtual bool GetAsAny(wxAny* any) const; \ +static wxVariantData* VariantDataFactory(const wxAny& any); + +#define REGISTER_WXANY_CONVERSION(T, CLASSNAME) \ +static wxAnyToVariantRegistrationImpl \ + gs_##CLASSNAME##AnyToVariantRegistration = \ + wxAnyToVariantRegistrationImpl(&CLASSNAME::VariantDataFactory); + +#define IMPLEMENT_TRIVIAL_WXANY_CONVERSION(T, CLASSNAME) \ +bool CLASSNAME::GetAsAny(wxAny* any) const \ +{ \ + *any = m_value; \ + return true; \ +} \ +wxVariantData* CLASSNAME::VariantDataFactory(const wxAny& any) \ +{ \ + return new CLASSNAME(wxANY_AS(any, T)); \ +} \ +REGISTER_WXANY_CONVERSION(T, CLASSNAME) + +// This is needed for wxVariantList conversion +WX_DECLARE_LIST_WITH_DECL(wxAny, wxAnyList, class WXDLLIMPEXP_BASE); + +#else // if !wxUSE_ANY + +#define DECLARE_WXANY_CONVERSION() +#define REGISTER_WXANY_CONVERSION(T, CLASSNAME) +#define IMPLEMENT_TRIVIAL_WXANY_CONVERSION(T, CLASSNAME) + +#endif // wxUSE_ANY/!wxUSE_ANY + + #define DECLARE_VARIANT_OBJECT(classname) \ DECLARE_VARIANT_OBJECT_EXPORTED(classname, wxEMPTY_PARAMETER_VALUE) @@ -367,6 +458,7 @@ public:\ \ virtual wxVariantData* Clone() const { return new classname##VariantData(m_value); } \ \ + DECLARE_WXANY_CONVERSION() \ protected:\ classname m_value; \ };\ @@ -395,7 +487,8 @@ expdecl wxVariant& operator << ( wxVariant &variant, const classname &value )\ classname##VariantData *data = new classname##VariantData( value );\ variant.SetData( data );\ return variant;\ -} +} \ +IMPLEMENT_TRIVIAL_WXANY_CONVERSION(classname, classname##VariantData) // implements a wxVariantData-derived class using for the Eq() method the // operator== which must have been provided by "classname" diff --git a/interface/wx/any.h b/interface/wx/any.h index 61b214b449..826d2b7a14 100644 --- a/interface/wx/any.h +++ b/interface/wx/any.h @@ -13,9 +13,9 @@ The wxAny class represents a container for any type. Its value can be changed at run time, possibly to a different type of value. - wxAny is a backwards incompatible successor class for wxVariant, - essentially doing the same thing in a more modern, template-based manner - and with transparent support for any user data type. + wxAny is a backwards-incompatible (but convertible) successor class for + wxVariant, essentially doing the same thing in a more modern, template- + based manner and with transparent support for any user data type. Some pseudo-code'ish example of use with arbitrary user data: @@ -87,6 +87,17 @@ public: */ wxAny(const wxAny& any); + /** + Constructs wxAny, converting value from wxVariant. + + @remarks Because of this conversion, it is not usually possible to + have wxAny that actually holds a wxVariant. If wxVariant + cannot be converted to a specific data type, wxAny will then + hold and manage reference to wxVariantData* similar to how + wxVariant does. + */ + wxAny(const wxVariant& variant); + /** Destructor. */ @@ -127,6 +138,16 @@ public: template bool GetAs(T* value) const; + /** + Specialization of GetAs() that allows conversion of wxAny into + wxVariant. + + @return Returns @true if conversion was successful. Conversion usually + only fails if variant used custom wxVariantData that did not + implement the wxAny to wxVariant conversion functions. + */ + bool GetAs(wxVariant* value) const; + /** Returns the value type as wxAnyValueType instance. @@ -154,6 +175,7 @@ public: template wxAny& operator=(const T &value); wxAny& operator=(const wxAny &any); + wxAny& operator=(const wxVariant &variant); //@} //@{ diff --git a/interface/wx/variant.h b/interface/wx/variant.h index 42575ddc77..eebfe36a0a 100644 --- a/interface/wx/variant.h +++ b/interface/wx/variant.h @@ -12,6 +12,11 @@ The wxVariant class represents a container for any type. A variant's value can be changed at run time, possibly to a different type of value. + @note As of wxWidgets 2.9.1, wxAny has become the preferred variant class. + While most controls still use wxVariant in their interface, you + can start using wxAny in your code because of an implicit conversion + layer. See below for more information. + As standard, wxVariant can store values of type bool, wxChar, double, long, string, string list, time, date, void pointer, list of strings, and list of variants. However, an application can extend wxVariant's capabilities by @@ -85,10 +90,71 @@ wxDynamicCast(), to use C++ RTTI type information instead of wxWidgets RTTI. + @section variant_wxanyconversion wxVariant to wxAny Conversion Layer + + wxAny is a more modern, template-based variant class. It is not + directly compatible with wxVariant, but there is a transparent conversion + layer. + + Following is an example how to use these conversions with wxPropertyGrid's + property class wxPGProperty (which currently uses wxVariants both + internally and in the public API): + + @code + // Get property value as wxAny instead of wxVariant + wxAny value = property->GetValue(); + + // Do something with it + DoSomethingWithString(value.As()); + + // Write back new value to property + value = "New Value"; + property->SetValue(value); + + @endcode + + Some caveats: + @li In wxAny, there are no separate types for handling integers of + different sizes, so converting wxAny with 'long long' value + will yield wxVariant of "long" type when the value is small + enough to fit in without overflow. Otherwise, variant type + "longlong" is used. Also note that wxAny holding unsigned integer + will always be converted to "ulonglong" wxVariant type. + + @li Unlike wxVariant, wxAny does not store a (rarely needed) name string. + + @li Because of implicit conversion of wxVariant to wxAny, wxAny cannot + usually contain value of type wxVariant. In other words, + any.CheckType() can never return @true. + + Supplied conversion functions will automatically work with all + built-in wxVariant types, and also with all user-specified types generated + using IMPLEMENT_VARIANT_OBJECT(). For hand-built wxVariantData classes, + you will need to use supplied macros in a following manner: + + @code + + // Declare wxVariantData for data type Foo + class wxVariantDataFoo: public wxVariantData + { + public: + // interface + // ... + + DECLARE_WXANY_CONVERSION() + protected: + // data storage etc + // ... + }; + + IMPLEMENT_TRIVIAL_WXANY_CONVERSION(Foo, wxVariantDataFoo) + + @endcode + @library{wxbase} @category{data} - @see wxVariantData + @see wxVariantData, wxAny */ class wxVariant : public wxObject { @@ -111,6 +177,11 @@ public: */ wxVariant(const wxVariant& variant); + /** + Constructs a variant by converting it from wxAny. + */ + wxVariant(const wxAny& any); + /** Constructs a variant from a wide string literal. */ @@ -265,6 +336,11 @@ public: bool Convert(wxDateTime* value) const; //@} + /** + Converts wxVariant into wxAny. + */ + wxAny GetAny() const; + /** Returns the string array value. */ @@ -552,6 +628,11 @@ public: */ virtual bool Eq(wxVariantData& data) const = 0; + /** + Converts value to wxAny, if possible. Return @true if successful. + */ + virtual bool GetAny(wxAny* any) const; + /** Returns the string type of the data. */ diff --git a/src/common/any.cpp b/src/common/any.cpp index ff7b1c8a30..1049b42efa 100644 --- a/src/common/any.cpp +++ b/src/common/any.cpp @@ -27,6 +27,7 @@ #include "wx/vector.h" #include "wx/module.h" +#include "wx/hashmap.h" using namespace wxPrivate; @@ -34,9 +35,19 @@ using namespace wxPrivate; // wxAnyValueTypeGlobals //------------------------------------------------------------------------- +#if wxUSE_VARIANT + +WX_DECLARE_HASH_MAP(wxAnyValueType*, + wxVariantDataFactory, + wxPointerHash, + wxPointerEqual, + wxAnyTypeToVariantDataFactoryMap); + +#endif + // -// Helper class to manage wxAnyValueType instances and other -// related global variables. +// Helper class to manage wxAnyValueType instances and and other +// related global variables (such as wxAny<->wxVariant type association). // // NB: We really need to have wxAnyValueType instances allocated // in heap. They are stored as static template member variables, @@ -51,6 +62,9 @@ public: } ~wxAnyValueTypeGlobals() { + #if wxUSE_VARIANT + m_anyToVariant.clear(); + #endif for ( size_t i=0; isecond; + + // Not found, handle pre-registrations + size_t i = m_anyToVariantRegs.size(); + while ( i > 0 ) + { + i--; + wxAnyToVariantRegistration* reg = m_anyToVariantRegs[i]; + wxAnyValueType* assocType = reg->GetAssociatedType(); + if ( assocType ) + { + // Both variant data and wxAnyValueType have been + // now been properly initialized, so remove the + // pre-registration entry and move data to anyToVarian + // map. + anyToVariant[assocType] = reg->GetFactory(); + m_anyToVariantRegs.erase( m_anyToVariantRegs.begin() + i ); + } + } + + // Then try again + it = anyToVariant.find(type); + if ( it != anyToVariant.end() ) + return it->second; + + // Finally, attempt to find a compatible type + for ( it = anyToVariant.begin(); it != anyToVariant.end(); it++ ) + { + if ( type->IsSameType(it->first) ) + { + wxVariantDataFactory f = it->second; + anyToVariant[type] = f; + return f; + } + } + + // Nothing found + return NULL; + } +#endif + private: - wxVector m_valueTypes; + wxVector m_valueTypes; +#if wxUSE_VARIANT + wxAnyTypeToVariantDataFactoryMap m_anyToVariant; + wxVector m_anyToVariantRegs; +#endif }; static wxAnyValueTypeGlobals* g_wxAnyValueTypeGlobals = NULL; +#if wxUSE_VARIANT + +WX_IMPLEMENT_ANY_VALUE_TYPE(wxAnyValueTypeImplVariantData) + +void wxPreRegisterAnyToVariant(wxAnyToVariantRegistration* reg) +{ + if ( !g_wxAnyValueTypeGlobals ) + g_wxAnyValueTypeGlobals = new wxAnyValueTypeGlobals(); + g_wxAnyValueTypeGlobals->PreRegisterAnyToVariant(reg); +} + +bool wxConvertAnyToVariant(const wxAny& any, wxVariant* variant) +{ + if ( any.IsNull() ) + { + variant->MakeNull(); + return true; + } + + // (signed) integer is a special case, because there is only one type + // in wxAny, and two ("long" and "longlong") in wxVariant. For better + // backwards compatibility, convert all values that fit in "long", + // and others to "longlong". + if ( wxANY_CHECK_TYPE(any, signed int) ) + { +#ifdef wxLongLong_t + wxLongLong_t ll; + if ( any.GetAs(&ll) ) + { + if ( ll > LONG_MAX ) + *variant = wxLongLong(ll); + else + *variant = (long) wxLongLong(ll).GetLo(); + } + else + { + return false; + } +#else + long l; + if ( any.GetAs(&l) ) + *variant = l; + else + return false; +#endif + return true; + } + + // Find matching factory function + wxVariantDataFactory f = + g_wxAnyValueTypeGlobals->FindVariantDataFactory(any.GetType()); + + wxVariantData* data; + + if ( f ) + { + data = f(any); + } + else + { + // Check if wxAny wrapped wxVariantData* + if ( !any.GetAs(&data) ) + return false; + + // Wrapper's GetValue() does not increase reference + // count, se have to do it before the data gets passed + // to a new variant. + data->IncRef(); + } + + variant->SetData(data); + return true; +} + +#endif // wxUSE_VARIANT + // // This class is to make sure that wxAnyValueType instances // etc. get freed correctly. We must use a separate wxAnyValueTypeGlobals diff --git a/src/common/variant.cpp b/src/common/variant.cpp index b1c8838981..81117ab99b 100644 --- a/src/common/variant.cpp +++ b/src/common/variant.cpp @@ -193,6 +193,48 @@ bool wxVariant::IsValueKindOf(const wxClassInfo* type) const return info ? info->IsKindOf(type) : false ; } +// ----------------------------------------------------------------- +// wxVariant <-> wxAny conversion code +// ----------------------------------------------------------------- + +#if wxUSE_ANY + +wxAnyToVariantRegistration:: + wxAnyToVariantRegistration(wxVariantDataFactory factory) + : m_factory(factory) +{ + wxPreRegisterAnyToVariant(this); +} + +wxVariant::wxVariant(const wxAny& any) + : wxObject() +{ + wxVariant variant; + if ( !any.GetAs(&variant) ) + { + wxFAIL_MSG("wxAny of this type cannot be converted to wxVariant"); + return; + } + + *this = variant; +} + +wxAny wxVariant::GetAny() const +{ + if ( IsNull() ) + return wxAny(); + + wxVariantData* data = GetData(); + wxAny any; + + if ( data->GetAsAny(&any) ) + return any; + + // If everything else fails, wrap the whole wxVariantData + return wxAny(data); +} + +#endif // wxUSE_ANY // ----------------------------------------------------------------- // wxVariantDataLong @@ -224,10 +266,27 @@ public: virtual wxString GetType() const { return wxT("long"); } + // Since wxAny does not have separate type for integers shorter than + // longlong, we do not usually implement wxVariant->wxAny conversion + // here (but in wxVariantDataLongLong instead). +#ifndef wxLongLong_t + DECLARE_WXANY_CONVERSION() +#else + bool GetAsAny(wxAny* any) const + { + *any = m_value; + return true; + } +#endif + protected: long m_value; }; +#ifndef wxLongLong_t +IMPLEMENT_TRIVIAL_WXANY_CONVERSION(long, wxVariantDataLong) +#endif + bool wxVariantDataLong::Eq(wxVariantData& data) const { wxASSERT_MSG( (data.GetType() == wxT("long")), wxT("wxVariantDataLong::Eq: argument mismatch") ); @@ -373,10 +432,14 @@ public: virtual wxString GetType() const { return wxT("double"); } wxVariantData* Clone() const { return new wxVariantDoubleData(m_value); } + + DECLARE_WXANY_CONVERSION() protected: double m_value; }; +IMPLEMENT_TRIVIAL_WXANY_CONVERSION(double, wxVariantDoubleData) + bool wxVariantDoubleData::Eq(wxVariantData& data) const { wxASSERT_MSG( (data.GetType() == wxT("double")), wxT("wxVariantDoubleData::Eq: argument mismatch") ); @@ -509,10 +572,14 @@ public: virtual wxString GetType() const { return wxT("bool"); } wxVariantData* Clone() const { return new wxVariantDataBool(m_value); } + + DECLARE_WXANY_CONVERSION() protected: bool m_value; }; +IMPLEMENT_TRIVIAL_WXANY_CONVERSION(bool, wxVariantDataBool) + bool wxVariantDataBool::Eq(wxVariantData& data) const { wxASSERT_MSG( (data.GetType() == wxT("bool")), wxT("wxVariantDataBool::Eq: argument mismatch") ); @@ -646,10 +713,13 @@ public: virtual wxString GetType() const { return wxT("char"); } wxVariantData* Clone() const { return new wxVariantDataChar(m_value); } + DECLARE_WXANY_CONVERSION() protected: wxUniChar m_value; }; +IMPLEMENT_TRIVIAL_WXANY_CONVERSION(wxUniChar, wxVariantDataChar) + bool wxVariantDataChar::Eq(wxVariantData& data) const { wxASSERT_MSG( (data.GetType() == wxT("char")), wxT("wxVariantDataChar::Eq: argument mismatch") ); @@ -798,10 +868,13 @@ public: virtual wxString GetType() const { return wxT("string"); } wxVariantData* Clone() const { return new wxVariantDataString(m_value); } + DECLARE_WXANY_CONVERSION() protected: wxString m_value; }; +IMPLEMENT_TRIVIAL_WXANY_CONVERSION(wxString, wxVariantDataString) + bool wxVariantDataString::Eq(wxVariantData& data) const { wxASSERT_MSG( (data.GetType() == wxT("string")), wxT("wxVariantDataString::Eq: argument mismatch") ); @@ -954,10 +1027,13 @@ public: virtual wxClassInfo* GetValueClassInfo(); + DECLARE_WXANY_CONVERSION() protected: wxObject* m_value; }; +IMPLEMENT_TRIVIAL_WXANY_CONVERSION(wxObject*, wxVariantDataWxObjectPtr) + bool wxVariantDataWxObjectPtr::Eq(wxVariantData& data) const { wxASSERT_MSG( data.GetType() == GetType(), wxT("wxVariantDataWxObjectPtr::Eq: argument mismatch") ); @@ -1073,10 +1149,13 @@ public: virtual wxString GetType() const { return wxT("void*"); } virtual wxVariantData* Clone() const { return new wxVariantDataVoidPtr(m_value); } + DECLARE_WXANY_CONVERSION() protected: void* m_value; }; +IMPLEMENT_TRIVIAL_WXANY_CONVERSION(void*, wxVariantDataVoidPtr) + bool wxVariantDataVoidPtr::Eq(wxVariantData& data) const { wxASSERT_MSG( data.GetType() == wxT("void*"), wxT("wxVariantDataVoidPtr::Eq: argument mismatch") ); @@ -1185,10 +1264,12 @@ public: virtual wxString GetType() const { return wxT("datetime"); } virtual wxVariantData* Clone() const { return new wxVariantDataDateTime(m_value); } + DECLARE_WXANY_CONVERSION() protected: wxDateTime m_value; }; +IMPLEMENT_TRIVIAL_WXANY_CONVERSION(wxDateTime, wxVariantDataDateTime) bool wxVariantDataDateTime::Eq(wxVariantData& data) const { @@ -1316,10 +1397,13 @@ public: virtual wxString GetType() const { return wxT("arrstring"); } virtual wxVariantData* Clone() const { return new wxVariantDataArrayString(m_value); } + DECLARE_WXANY_CONVERSION() protected: wxArrayString m_value; }; +IMPLEMENT_TRIVIAL_WXANY_CONVERSION(wxArrayString, wxVariantDataArrayString) + bool wxVariantDataArrayString::Eq(wxVariantData& data) const { wxASSERT_MSG( data.GetType() == GetType(), wxT("wxVariantDataArrayString::Eq: argument mismatch") ); @@ -1449,10 +1533,48 @@ public: virtual wxString GetType() const { return wxS("longlong"); } + DECLARE_WXANY_CONVERSION() protected: wxLongLong m_value; }; +// +// wxLongLong type requires customized wxAny conversion code +// +#if wxUSE_ANY +#ifdef wxLongLong_t + +bool wxVariantDataLongLong::GetAsAny(wxAny* any) const +{ + *any = m_value.GetValue(); + return true; +} + +wxVariantData* wxVariantDataLongLong::VariantDataFactory(const wxAny& any) +{ + return new wxVariantDataLongLong(wxANY_AS(any, wxLongLong_t)); +} + +REGISTER_WXANY_CONVERSION(wxLongLong_t, wxVariantDataLongLong) + +#else // if !defined(wxLongLong_t) + +bool wxVariantDataLongLong::GetAsAny(wxAny* any) const +{ + *any = m_value; + return true; +} + +wxVariantData* wxVariantDataLongLong::VariantDataFactory(const wxAny& any) +{ + return new wxVariantDataLongLong(wxANY_AS(any, wxLongLong)); +} + +REGISTER_WXANY_CONVERSION(wxLongLong, wxVariantDataLongLong) + +#endif // defined(wxLongLong_t)/!defined(wxLongLong_t) +#endif // wxUSE_ANY + bool wxVariantDataLongLong::Eq(wxVariantData& data) const { wxASSERT_MSG( (data.GetType() == wxS("longlong")), @@ -1610,10 +1732,49 @@ public: virtual wxString GetType() const { return wxS("ulonglong"); } + DECLARE_WXANY_CONVERSION() protected: wxULongLong m_value; }; +// +// wxULongLong type requires customized wxAny conversion code +// +#if wxUSE_ANY +#ifdef wxLongLong_t + +bool wxVariantDataULongLong::GetAsAny(wxAny* any) const +{ + *any = m_value.GetValue(); + return true; +} + +wxVariantData* wxVariantDataULongLong::VariantDataFactory(const wxAny& any) +{ + return new wxVariantDataULongLong(wxANY_AS(any, wxULongLong_t)); +} + +REGISTER_WXANY_CONVERSION(wxULongLong_t, wxVariantDataULongLong) + +#else // if !defined(wxLongLong_t) + +bool wxVariantDataULongLong::GetAsAny(wxAny* any) const +{ + *any = m_value; + return true; +} + +wxVariantData* wxVariantDataULongLong::VariantDataFactory(const wxAny& any) +{ + return new wxVariantDataULongLong(wxANY_AS(any, wxULongLong)); +} + +REGISTER_WXANY_CONVERSION(wxULongLong, wxVariantDataULongLong) + +#endif // defined(wxLongLong_t)/!defined(wxLongLong_t) +#endif // wxUSE_ANY + + bool wxVariantDataULongLong::Eq(wxVariantData& data) const { wxASSERT_MSG( (data.GetType() == wxS("ulonglong")), @@ -1764,10 +1925,54 @@ public: void Clear(); wxVariantData* Clone() const { return new wxVariantDataList(m_value); } + + DECLARE_WXANY_CONVERSION() protected: wxVariantList m_value; }; +#if wxUSE_ANY + +// +// Convert to/from list of wxAnys +// + +WX_DEFINE_LIST(wxAnyList) + +bool wxVariantDataList::GetAsAny(wxAny* any) const +{ + wxAnyList dst; + wxVariantList::compatibility_iterator node = m_value.GetFirst(); + while (node) + { + wxVariant* pVar = node->GetData(); + dst.push_back(new wxAny(*pVar)); + node = node->GetNext(); + } + + *any = dst; + return true; +} + +wxVariantData* wxVariantDataList::VariantDataFactory(const wxAny& any) +{ + wxAnyList src = wxANY_AS(any, wxAnyList); + wxVariantList dst; + wxAnyList::compatibility_iterator node = src.GetFirst(); + while (node) + { + wxAny* pAny = node->GetData(); + dst.push_back(new wxVariant(*pAny)); + node = node->GetNext(); + } + + return new wxVariantDataList(dst); +} + +REGISTER_WXANY_CONVERSION(wxAnyList, wxVariantDataList) + +#endif // wxUSE_ANY + wxVariantDataList::wxVariantDataList(const wxVariantList& list) { SetValue(list); diff --git a/tests/any/anytest.cpp b/tests/any/anytest.cpp index afffedaabc..6912bd622f 100644 --- a/tests/any/anytest.cpp +++ b/tests/any/anytest.cpp @@ -36,6 +36,7 @@ private: CPPUNIT_TEST( As ); CPPUNIT_TEST( GetAs ); CPPUNIT_TEST( Null ); + CPPUNIT_TEST( wxVariantConversions ); CPPUNIT_TEST( CustomTemplateSpecialization ); CPPUNIT_TEST_SUITE_END(); @@ -43,6 +44,7 @@ private: void As(); void GetAs(); void Null(); + void wxVariantConversions(); void CustomTemplateSpecialization(); wxDateTime m_testDateTime; @@ -66,6 +68,7 @@ private: wxAny m_anyWxObjectPtr1; wxAny m_anyVoidPtr1; wxAny m_anyDateTime1; + wxAny m_anyUniChar1; wxAny m_anySignedChar2; wxAny m_anySignedShort2; @@ -156,6 +159,7 @@ wxAnyTestCase::wxAnyTestCase() m_anyFloatDouble2 = (float)TEST_FLOAT_CONST; m_anyDoubleDouble2 = (double)TEST_FLOAT_CONST; m_anyDateTime2 = m_testDateTime; + m_anyUniChar1 = wxUniChar('A'); m_anyWxObjectPtr2 = dummyWxObjectPointer; m_anyVoidPtr2 = dummyVoidPointer; } @@ -248,6 +252,8 @@ void wxAnyTestCase::As() CPPUNIT_ASSERT_DOUBLES_EQUAL(o, TEST_FLOAT_CONST, FEQ_DELTA); double p = wxANY_AS(m_anyDoubleDouble1, double); CPPUNIT_ASSERT_DOUBLES_EQUAL(p, TEST_FLOAT_CONST, FEQ_DELTA); + wxUniChar chr = wxANY_AS(m_anyUniChar1, wxUniChar); + CPPUNIT_ASSERT(chr == 'A'); wxDateTime q = wxANY_AS(m_anyDateTime1, wxDateTime); CPPUNIT_ASSERT(q == m_testDateTime); wxObject* r = wxANY_AS(m_anyWxObjectPtr1, wxObject*); @@ -370,8 +376,10 @@ void wxAnyTestCase::GetAs() CPPUNIT_ASSERT_DOUBLES_EQUAL(d2, TEST_FLOAT_CONST, FEQ_DELTA); } + // -// Test user data type specialization of wxAnyValueTypeImpl +// Test user data type for wxAnyValueTypeImpl specialization +// any hand-built wxVariantData // class MyClass @@ -392,6 +400,173 @@ private: }; +#if wxUSE_VARIANT + +// For testing purposes, create dummy variant data implementation +// that does not have wxAny conversion code +class wxMyVariantData : public wxVariantData +{ +public: + wxMyVariantData(const MyClass& value) + { + m_value = value; + } + + virtual bool Eq(wxVariantData& WXUNUSED(data)) const + { + return false; + } + + // What type is it? Return a string name. + virtual wxString GetType() const { return "MyClass"; } + + virtual wxVariantData* Clone() const + { + return new wxMyVariantData(m_value); + } + +protected: + MyClass m_value; +}; + +#endif // wxUSE_VARIANT + + +void wxAnyTestCase::wxVariantConversions() +{ +#if wxUSE_VARIANT + // + // Test various conversions to and from wxVariant + // + bool res; + + // Prepare wxVariants + wxVariant vLong(123L); + wxVariant vString("ABC"); + wxVariant vDouble(TEST_FLOAT_CONST); + wxVariant vBool((bool)true); + wxVariant vChar('A'); +#ifdef wxLongLong_t + wxVariant vLongLong(wxLongLong(wxLL(0xFFFFFFFFFF))); + wxVariant vULongLong(wxULongLong(wxULL(123456))); +#endif + wxArrayString arrstr; + arrstr.push_back("test string"); + wxVariant vArrayString(arrstr); + wxVariant vDateTime(m_testDateTime); + wxVariant vVoidPtr(dummyVoidPointer); + wxVariant vCustomType(new wxMyVariantData(MyClass(101))); + wxVariant vList; + + vList.NullList(); + vList.Append(15); + vList.Append("abc"); + + // Convert to wxAnys, and then back to wxVariants + wxVariant variant; + + wxAny any(vLong); + CPPUNIT_ASSERT(any == 123L); + res = any.GetAs(&variant); + CPPUNIT_ASSERT(res); + CPPUNIT_ASSERT(variant == 123L); + + // Make sure integer variant has correct type information + CPPUNIT_ASSERT(variant.GetLong() == 123); + CPPUNIT_ASSERT(variant.GetType() == "long"); + + // Unsigned long wxAny should convert to "ulonglong" wxVariant + any = 1000UL; + res = any.GetAs(&variant); + CPPUNIT_ASSERT(res); + CPPUNIT_ASSERT(variant.GetType() == "ulonglong"); + CPPUNIT_ASSERT(variant.GetLong() == 1000); + + any = vString; + CPPUNIT_ASSERT(any == "ABC"); + res = any.GetAs(&variant); + CPPUNIT_ASSERT(res); + CPPUNIT_ASSERT(variant.GetString() == "ABC"); + + any = vDouble; + double d = wxANY_AS(any, double); + CPPUNIT_ASSERT_DOUBLES_EQUAL(d, TEST_FLOAT_CONST, FEQ_DELTA); + res = any.GetAs(&variant); + CPPUNIT_ASSERT(res); + CPPUNIT_ASSERT_DOUBLES_EQUAL(variant.GetDouble(), + TEST_FLOAT_CONST, + FEQ_DELTA); + + any = vBool; + CPPUNIT_ASSERT(wxANY_AS(any, bool) == true); + res = any.GetAs(&variant); + CPPUNIT_ASSERT(res); + CPPUNIT_ASSERT(variant.GetBool() == true); + + any = vChar; + //CPPUNIT_ASSERT(wxANY_AS(any, wxUniChar) == 'A'); + res = any.GetAs(&variant); + CPPUNIT_ASSERT(res); + CPPUNIT_ASSERT(variant.GetChar() == 'A'); + +#ifdef wxLongLong_t + any = vLongLong; + CPPUNIT_ASSERT(any == wxLL(0xFFFFFFFFFF)); + res = any.GetAs(&variant); + CPPUNIT_ASSERT(res); + CPPUNIT_ASSERT(variant.GetLongLong() == wxLongLong(wxLL(0xFFFFFFFFFF))); + CPPUNIT_ASSERT(variant.GetType() == "longlong"); + + any = vULongLong; + CPPUNIT_ASSERT(any == wxLL(123456)); + res = any.GetAs(&variant); + CPPUNIT_ASSERT(res); + CPPUNIT_ASSERT(variant.GetULongLong() == wxULongLong(wxULL(123456))); +#endif + + // Cannot test equality for the rest, just test that they convert + // back correctly. + any = vArrayString; + res = any.GetAs(&variant); + CPPUNIT_ASSERT(res); + wxArrayString arrstr2 = variant.GetArrayString(); + CPPUNIT_ASSERT(arrstr2 == arrstr); + + any = m_testDateTime; + CPPUNIT_ASSERT(wxANY_AS(any, wxDateTime) == m_testDateTime); + any = vDateTime; + CPPUNIT_ASSERT(wxANY_AS(any, wxDateTime) == m_testDateTime); + res = any.GetAs(&variant); + CPPUNIT_ASSERT(res); + CPPUNIT_ASSERT(variant == m_testDateTime); + + any = vVoidPtr; + res = any.GetAs(&variant); + CPPUNIT_ASSERT(res); + CPPUNIT_ASSERT(variant.GetVoidPtr() == dummyVoidPointer); + + any = vList; + CPPUNIT_ASSERT(wxANY_CHECK_TYPE(any, wxAnyList)); + wxAnyList anyList = wxANY_AS(any, wxAnyList); + CPPUNIT_ASSERT(anyList.GetCount() == 2); + CPPUNIT_ASSERT(wxANY_AS((*anyList[0]), int) == 15); + CPPUNIT_ASSERT(wxANY_AS((*anyList[1]), wxString) == "abc"); + res = any.GetAs(&variant); + CPPUNIT_ASSERT(res); + CPPUNIT_ASSERT(variant.GetType() == "list"); + CPPUNIT_ASSERT(variant.GetCount() == 2); + CPPUNIT_ASSERT(variant[0].GetLong() == 15); + CPPUNIT_ASSERT(variant[1].GetString() == "abc"); + + any = vCustomType; + CPPUNIT_ASSERT(wxANY_CHECK_TYPE(any, wxVariantData*)); + res = any.GetAs(&variant); + CPPUNIT_ASSERT(res); + CPPUNIT_ASSERT(variant.GetType() == "MyClass"); + +#endif // wxUSE_VARIANT +} + template<> class wxAnyValueTypeImpl : public wxAnyValueTypeImplBase -- 2.45.2