/* ///////////////////////////////////////////////////////////////////////// * File: comstl/util/variant.hpp (originally MOVriant.h/.cpp, ::SynesisCom) * * Purpose: variant class. * * Created: 12th December 1996 * Updated: 5th March 2011 * * Home: http://stlsoft.org/ * * Copyright (c) 1996-2011, Matthew Wilson and Synesis Software * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name(s) of Matthew Wilson and Synesis Software nor the names of * any contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * ////////////////////////////////////////////////////////////////////// */ /** \file comstl/util/variant.hpp * * \brief [C++ only; requires COM] Definition of the comstl::variant class * (\ref group__library__utility__com "COM Utility" Library). */ #ifndef COMSTL_INCL_COMSTL_UTIL_HPP_COMSTL_VARIANT #define COMSTL_INCL_COMSTL_UTIL_HPP_COMSTL_VARIANT #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION # define _COMSTL_VER_COMSTL_UTIL_HPP_COMSTL_VARIANT_MAJOR 2 # define _COMSTL_VER_COMSTL_UTIL_HPP_COMSTL_VARIANT_MINOR 3 # define _COMSTL_VER_COMSTL_UTIL_HPP_COMSTL_VARIANT_REVISION 5 # define _COMSTL_VER_COMSTL_UTIL_HPP_COMSTL_VARIANT_EDIT 158 #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */ /* ///////////////////////////////////////////////////////////////////////// * Includes */ #ifndef COMSTL_INCL_COMSTL_H_COMSTL # include #endif /* !COMSTL_INCL_COMSTL_H_COMSTL */ #ifndef COMSTL_INCL_COMSTL_STRING_H_BSTR_FUNCTIONS # include #endif /* !COMSTL_INCL_COMSTL_STRING_H_BSTR_FUNCTIONS */ #ifndef COMSTL_INCL_COMSTL_SHIMS_ACCESS_HPP_STRING # include #endif /* !COMSTL_INCL_COMSTL_SHIMS_ACCESS_HPP_STRING */ #ifdef STLSOFT_CF_EXCEPTION_SUPPORT # ifndef COMSTL_INCL_COMSTL_ERROR_HPP_EXCEPTIONS # include # endif /* !COMSTL_INCL_COMSTL_ERROR_HPP_EXCEPTIONS */ #endif /* !STLSOFT_CF_EXCEPTION_SUPPORT */ #ifndef COMSTL_INCL_COMSTL_UTIL_HPP_INTERFACE_TRAITS # include #endif /* !COMSTL_INCL_COMSTL_UTIL_HPP_INTERFACE_TRAITS */ #ifndef COMSTL_INCL_COMSTL_UTIL_H_VARIANT_FUNCTIONS # include #endif /* !COMSTL_INCL_COMSTL_UTIL_H_VARIANT_FUNCTIONS */ #ifndef STLSOFT_INCL_STLSOFT_UTIL_HPP_CONSTRAINTS # include #endif /* !STLSOFT_INCL_STLSOFT_UTIL_HPP_CONSTRAINTS */ /* ///////////////////////////////////////////////////////////////////////// * Namespace */ #ifndef _COMSTL_NO_NAMESPACE # if defined(_STLSOFT_NO_NAMESPACE) || \ defined(STLSOFT_DOCUMENTATION_SKIP_SECTION) /* There is no stlsoft namespace, so must define ::comstl */ namespace comstl { # else /* Define stlsoft::comstl_project */ namespace stlsoft { namespace comstl_project { # endif /* _STLSOFT_NO_NAMESPACE */ #endif /* !_COMSTL_NO_NAMESPACE */ /* ///////////////////////////////////////////////////////////////////////// * Classes */ /** \brief Facade for the COM VARIANT type * * \ingroup group__library__utility__com * * \remarks comstl::variant publicly derives from \c VARIANT as a measure * of expedience, rather than as an act of design sophistication. Any * manual manipulation of the instances, or their member variables, is * at the user's risk. Notwithstanding, this is helped by the fact that * comstl::variant declares no member variables and no virtual member * functions - for most compilers this means that the Empty Derived * Optimisation (see Section 12.4 of Imperfect C++) will apply. */ class variant : public VARIANT { /// Member Types public: typedef variant class_type; typedef cs_bool_t bool_type; typedef cs_size_t size_type; /// Conversion public: /** Default constructor * * Initialises the instance * * \post assert(VT_EMPTY == this->vt) * * \exception - Does not throw an exception */ variant(); /** Copying constructor * * Initialises the instance with a copy of the given \c VARIANT * * \post assert(rhs == *this) * * \exception comstl::com_exception If the copy fails */ variant(VARIANT const& rhs); /** Copy constructor * * \post assert(rhs == *this) * * \exception comstl::com_exception If the copy fails */ variant(class_type const& rhs); /** Copy assignment operator * * \post assert(rhs == *this) * * \exception comstl::com_exception If the copy fails */ class_type& operator =(class_type const& rhs); /** Conversion constructor * * Initialises the instance with the given boolean value * * \post assert(VT_BOOL == this->vt) * \post assert(b == (VARIANT_TRUE == this->boolVal)) * * \exception - Does not throw an exception */ variant(bool b); /** Conversion constructor * * Initialises the instance with the given 8-bit signed integer value * * \post assert(VT_I1 == this->vt) * \post assert(i == this->cVal) * * \exception - Does not throw an exception */ variant(stlsoft::sint8_t i); /** Conversion constructor * * Initialises the instance with the given 8-bit unsigned integer value * * \post assert(VT_UI1 == this->vt) * \post assert(i == this->bVal) * * \exception - Does not throw an exception */ variant(stlsoft::uint8_t i); /** Conversion constructor * * Initialises the instance with the given 16-bit signed integer value * * \post assert(VT_I2 == this->vt) * \post assert(i == this->iVal) * * \exception - Does not throw an exception */ variant(stlsoft::sint16_t i); /** Conversion constructor * * Initialises the instance with the given 16-bit unsigned integer value * * \post assert(VT_UI2 == this->vt) * \post assert(i == this->uiVal) * * \exception - Does not throw an exception */ variant(stlsoft::uint16_t i); /** Conversion constructor * * Initialises the instance with the given 32-bit signed integer value * * \post assert(VT_I4 == this->vt) * \post assert(i == this->lVal) * * \exception - Does not throw an exception */ variant(stlsoft::sint32_t i); /** Conversion constructor * * Initialises the instance with the given 32-bit unsigned integer value * * \post assert(VT_UI4 == this->vt) * \post assert(i == this->ulVal) * * \exception - Does not throw an exception */ variant(stlsoft::uint32_t i); //#ifdef STLSOFT_CF_64BIT_INT_SUPPORT // variant(stlsoft::sint64_t i); // variant(stlsoft::uint64_t i); //#endif /* STLSOFT_CF_64BIT_INT_SUPPORT */ #ifdef STLSOFT_CF_SHORT_DISTINCT_INT_TYPE /** Conversion constructor * * Initialises the instance with the given \c short value * * \post assert(VT_I2 == this->vt) * \post assert(i == this->iVal) * * \exception - Does not throw an exception */ variant(short i); /** Conversion constructor * * Initialises the instance with the given * unsigned short value * * \post assert(VT_UI2 == this->vt) * \post assert(i == this->uiVal) * * \exception - Does not throw an exception */ variant(unsigned short i); #endif /* STLSOFT_CF_SHORT_DISTINCT_INT_TYPE */ #ifdef STLSOFT_CF_INT_DISTINCT_INT_TYPE /** Conversion constructor * * Initialises the instance with the given \c int value * * \post assert(VT_I4 == this->vt) * \post assert(i == this->lVal) * * \exception - Does not throw an exception */ variant(int i); /** Conversion constructor * * Initialises the instance with the given * unsigned int value * * \post assert(VT_UI4 == this->vt) * \post assert(i == this->ulVal) * * \exception - Does not throw an exception */ variant(unsigned int i); #endif /* STLSOFT_CF_INT_DISTINCT_INT_TYPE */ #ifdef STLSOFT_CF_LONG_DISTINCT_INT_TYPE /** Conversion constructor * * Initialises the instance with the given \c long value * * \post assert(VT_I4 == this->vt) * \post assert(i == this->lVal) * * \exception - Does not throw an exception */ variant(long i); /** Conversion constructor * * Initialises the instance with the given * unsigned long value * * \post assert(VT_UI4 == this->vt) * \post assert(i == this->ulVal) * * \exception - Does not throw an exception */ variant(unsigned long i); #endif /* STLSOFT_CF_LONG_DISTINCT_INT_TYPE */ /** Conversion constructor * * Initialises the instance with the given \c float value * * \post assert(VT_R4 == this->vt) * \post assert(r == this->fltVal) * * \exception - Does not throw an exception */ variant(float r); /** Conversion constructor * * Initialises the instance with the given \c double value * * \post assert(VT_R8 == this->vt) * \post assert(r == this->dblVal) * * \exception - Does not throw an exception */ variant(double r); /** Conversion constructor * * Initialises the instance with the given currency (\c CY) value * * \post assert(VT_CY == this->vt) * \post assert(r == this->cyVal) * * \exception - Does not throw an exception */ variant(CY cy); /** Conversion constructor * * Initialises the instance with the given \c DECIMAL value * * \post assert(VT_DECIMAL == this->vt) * \post assert(dec == this->decVal) * * \exception - Does not throw an exception */ variant(DECIMAL const& dec); variant(LPUNKNOWN punk, bool_type bAddRef); variant(LPDISPATCH pdisp, bool_type bAddRef); variant(cs_char_a_t const* s, int len = -1); variant(cs_char_w_t const* s, int len = -1); variant(VARIANT const& var, VARTYPE vt); /** Releases any resources associated with the underlying * VARIANT */ ~variant() stlsoft_throw_0() { stlsoft_constraint_must_be_same_size(class_type, VARIANT); ::VariantClear(this); } /** Clears the variant * * \post assert(VT_EMPTY == this->vt) */ void clear(); /// Operations public: HRESULT try_conversion_copy(VARIANT const& var, VARTYPE vt); HRESULT try_convert(VARTYPE vt); class_type& convert(VARTYPE vt); /** Returns a pointer to a specified interface on an object to which * a client currently holds an interface pointer. * * \return An HRESULT code indicating the success of the * operation. * \retval S_OK The interface is supported: * *ppv will hold the pointer to the requested interface * \retval E_INTERFACE The interface is not supported: the * value of *ppv is undefined. * * \pre NULL != ppv */ HRESULT QueryInterface(REFIID riid, void** ppv) const; /** Returns a pointer to a specified interface on an object to which * a client currently holds an interface pointer. * * \return An HRESULT code indicating the success of the * operation. * \retval S_OK The interface is supported: * *ppi will hold the pointer to the requested interface * \retval E_INTERFACE The interface is not supported: the * value of *ppi is undefined. * * \pre NULL != ppi */ template HRESULT QueryInterfaceValue(I** ppi) { return QueryInterface(IID_traits::iid(), reinterpret_cast(ppi)); } public: /** Swaps the contents with another instance */ void swap(class_type& rhs); /// Comparison public: bool_type equal(class_type const& rhs) const; bool_type equal(VARIANT const& rhs) const; /// Operators public: private: static void swap_(VARIANT& lhs, VARIANT& rhs); void handle_error_(char const* message, HRESULT hr); }; /* ///////////////////////////////////////////////////////////////////////// * String access shims */ // No string access shims are defined, because there're already a set // defined for VARIANT, in comstl/shims/access/string.hpp, which is included // by this file. /* ///////////////////////////////////////////////////////////////////////// * Operators */ inline cs_bool_t operator ==(variant const& lhs, variant const& rhs) { return lhs.equal(rhs); } inline cs_bool_t operator !=(variant const& lhs, variant const& rhs) { return !operator ==(lhs, rhs); } inline cs_bool_t operator ==(variant const& lhs, VARIANT const& rhs) { return lhs.equal(rhs); } inline cs_bool_t operator !=(variant const& lhs, VARIANT const& rhs) { return !operator ==(lhs, rhs); } inline cs_bool_t operator ==(VARIANT const& lhs, variant const& rhs) { return rhs.equal(lhs); } inline cs_bool_t operator !=(VARIANT const& lhs, variant const& rhs) { return !operator ==(lhs, rhs); } //////////////////////////////////////////////////////////////////////////// // Unit-testing #ifdef STLSOFT_UNITTEST # include "./unittest/variant_unittest_.h" #endif /* STLSOFT_UNITTEST */ /* ///////////////////////////////////////////////////////////////////////// * Implementation */ #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION inline void variant::handle_error_(char const* message, HRESULT hr) { #ifdef STLSOFT_CF_EXCEPTION_SUPPORT STLSOFT_THROW_X(com_exception(message, hr)); #else /* ? STLSOFT_CF_EXCEPTION_SUPPORT */ STLSOFT_SUPPRESS_UNUSED(message); ::VariantClear(this); this->vt = VT_ERROR; this->scode = hr; #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */ } inline /* static */ void variant::swap_(VARIANT& lhs, VARIANT& rhs) { VARIANT t; ::memcpy(&t, &lhs, sizeof(VARIANT)); ::memcpy(&lhs, &rhs, sizeof(VARIANT)); ::memcpy(&rhs, &t, sizeof(VARIANT)); } inline variant::variant() { ::VariantInit(this); } inline variant::variant(class_type const& rhs) { ::VariantInit(this); class_type& rhs_ = const_cast(rhs); HRESULT hr = ::VariantCopy(this, &rhs_); if(FAILED(hr)) { handle_error_("failed to copy variant", hr); } } inline variant::variant(VARIANT const& rhs) { ::VariantInit(this); HRESULT hr = ::VariantCopy(this, const_cast(&rhs)); if(FAILED(hr)) { handle_error_("failed to copy variant", hr); } } inline variant::class_type& variant::operator =(variant::class_type const& rhs) { class_type r(rhs); r.swap(*this); return *this; } inline variant::variant(bool b) { ::VariantInit(this); this->vt = VT_BOOL; this->boolVal = b ? VARIANT_TRUE : VARIANT_FALSE; } inline variant::variant(stlsoft::sint8_t i) { ::VariantInit(this); this->vt = VT_I1; this->cVal = static_cast(i); } inline variant::variant(stlsoft::uint8_t i) { ::VariantInit(this); this->vt = VT_UI1; this->bVal = static_cast(i); } inline variant::variant(stlsoft::sint16_t i) { ::VariantInit(this); this->vt = VT_I2; this->iVal = static_cast(i); } inline variant::variant(stlsoft::uint16_t i) { ::VariantInit(this); this->vt = VT_UI2; this->uiVal = static_cast(i); } inline variant::variant(stlsoft::sint32_t i) { ::VariantInit(this); this->vt = VT_I4; this->lVal = static_cast(i); } inline variant::variant(stlsoft::uint32_t i) { ::VariantInit(this); this->vt = VT_UI4; this->ulVal = static_cast(i); } #ifdef STLSOFT_CF_SHORT_DISTINCT_INT_TYPE inline variant::variant(short i) { ::VariantInit(this); this->vt = VT_I2; this->iVal = i; } inline variant::variant(unsigned short i) { ::VariantInit(this); this->vt = VT_UI2; this->uiVal = i; } #endif /* STLSOFT_CF_SHORT_DISTINCT_INT_TYPE */ #ifdef STLSOFT_CF_INT_DISTINCT_INT_TYPE inline variant::variant(int i) { ::VariantInit(this); this->vt = VT_I4; this->lVal = i; } inline variant::variant(unsigned int i) { ::VariantInit(this); this->vt = VT_UI4; this->ulVal = i; } #endif /* STLSOFT_CF_INT_DISTINCT_INT_TYPE */ #ifdef STLSOFT_CF_LONG_DISTINCT_INT_TYPE inline variant::variant(long i) { ::VariantInit(this); this->vt = VT_I4; this->lVal = i; } inline variant::variant(unsigned long i) { ::VariantInit(this); this->vt = VT_UI4; this->ulVal = i; } #endif /* STLSOFT_CF_LONG_DISTINCT_INT_TYPE */ inline variant::variant(float r) { ::VariantInit(this); this->vt = VT_R4; this->fltVal = r; } inline variant::variant(double r) { ::VariantInit(this); this->vt = VT_R8; this->dblVal = r; } inline variant::variant(CY cy) { ::VariantInit(this); this->vt = VT_CY; this->cyVal = cy; } inline variant::variant(DECIMAL const& dec) { ::VariantInit(this); this->vt = VT_DECIMAL; this->decVal.scale = dec.scale; this->decVal.sign = dec.sign; this->decVal.Hi32 = dec.Hi32; this->decVal.Mid32 = dec.Mid32; this->decVal.Lo32 = dec.Lo32; } inline variant::variant(LPUNKNOWN punk, bool_type bAddRef) { ::VariantInit(this); this->vt = VT_UNKNOWN; this->punkVal = punk; if( bAddRef && NULL != punk) { punk->AddRef(); } } inline variant::variant(LPDISPATCH pdisp, bool_type bAddRef) { ::VariantInit(this); this->vt = VT_DISPATCH; this->pdispVal = pdisp; if( bAddRef && NULL != pdisp) { pdisp->AddRef(); } } inline variant::variant(cs_char_a_t const* s, int len /* = -1 */) { ::VariantInit(this); this->vt = VT_BSTR; this->bstrVal = (len < 0) ? bstr_create(s) : bstr_create(s, static_cast(len)); if(NULL == this->bstrVal) { if( NULL != s && '\0' != 0[s]) { handle_error_("could not initialise from string", E_OUTOFMEMORY); } } } inline variant::variant(cs_char_w_t const* s, int len /* = -1 */) { ::VariantInit(this); this->vt = VT_BSTR; this->bstrVal = (len < 0) ? bstr_create(s) : bstr_create(s, static_cast(len)); if(NULL == this->bstrVal) { if( NULL != s && '\0' != 0[s]) { handle_error_("could not initialise from string", E_OUTOFMEMORY); } } } inline variant::variant(VARIANT const& var, VARTYPE vt) { ::VariantInit(this); class_type copy; HRESULT hr = ::VariantChangeType(©, const_cast(&var), 0, vt); if(FAILED(hr)) { handle_error_("could not convert variant to requested type", hr); } else { copy.swap(*this); } } inline void variant::clear() { ::VariantClear(this); } inline HRESULT variant::try_conversion_copy(VARIANT const& var, VARTYPE vt) { HRESULT hr; if(vt == this->vt) { hr = S_FALSE; } else { class_type copy; hr = ::VariantChangeType(©, const_cast(&var), 0, vt); if(SUCCEEDED(hr)) { copy.swap(*this); } } return hr; } inline HRESULT variant::try_convert(VARTYPE vt) { return try_conversion_copy(*this, vt); } inline variant::class_type& variant::convert(VARTYPE vt) { HRESULT hr = try_convert(vt); if(FAILED(hr)) { handle_error_("could not convert variant to requested type", hr); } return *this; } inline HRESULT variant::QueryInterface(REFIID riid, void** ppv) const { COMSTL_ASSERT(NULL != ppv); if( VT_UNKNOWN == this->vt || VT_DISPATCH == this->vt) { return (NULL == this->punkVal) ? E_POINTER : this->punkVal->QueryInterface(riid, ppv); } return DISP_E_BADVARTYPE; } inline void variant::swap(variant::class_type& rhs) { swap_(*this, rhs); } inline variant::bool_type variant::equal(variant::class_type const& rhs) const { return equal(static_cast(rhs)); } inline variant::bool_type variant::equal(VARIANT const& rhs) const { HRESULT comparisonSucceeded; int areEqual = VARIANT_equal(*this, rhs, &comparisonSucceeded); if(FAILED(comparisonSucceeded)) { #ifdef STLSOFT_CF_EXCEPTION_SUPPORT STLSOFT_THROW_X(comstl::com_exception("support for comparison of variant type not currently supported", comparisonSucceeded)); #else /* ? STLSOFT_CF_EXCEPTION_SUPPORT */ OutputDebugStringA("support for comparison of variant type not currently supported\n"); return false; #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */ } return 0 != areEqual; } #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */ /* ////////////////////////////////////////////////////////////////////// */ #ifndef _COMSTL_NO_NAMESPACE # if defined(_STLSOFT_NO_NAMESPACE) || \ defined(STLSOFT_DOCUMENTATION_SKIP_SECTION) } // namespace comstl # else } // namespace stlsoft::comstl_project } // namespace stlsoft # endif /* _STLSOFT_NO_NAMESPACE */ #endif /* !_COMSTL_NO_NAMESPACE */ /* ///////////////////////////////////////////////////////////////////////// * Namespace * * The string access shims exist either in the stlsoft namespace, or in the * global namespace. This is required by the lookup rules. * */ #ifndef _COMSTL_NO_NAMESPACE # if !defined(_STLSOFT_NO_NAMESPACE) && \ !defined(STLSOFT_DOCUMENTATION_SKIP_SECTION) namespace stlsoft { # else /* ? _STLSOFT_NO_NAMESPACE */ /* There is no stlsoft namespace, so must define in the global namespace */ # endif /* !_STLSOFT_NO_NAMESPACE */ # if !defined(_STLSOFT_NO_NAMESPACE) && \ !defined(STLSOFT_DOCUMENTATION_SKIP_SECTION) } // namespace stlsoft # else /* ? _STLSOFT_NO_NAMESPACE */ /* There is no stlsoft namespace, so must define in the global namespace */ # endif /* !_STLSOFT_NO_NAMESPACE */ #endif /* !_COMSTL_NO_NAMESPACE */ /* ////////////////////////////////////////////////////////////////////// */ #endif /* !COMSTL_INCL_COMSTL_UTIL_HPP_COMSTL_VARIANT */ /* ///////////////////////////// end of file //////////////////////////// */