You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1042 lines
33 KiB

  1. /* /////////////////////////////////////////////////////////////////////////
  2. * File: comstl/conversion/interface_cast.hpp
  3. *
  4. * Purpose: Safe interface casting functions.
  5. *
  6. * Created: 25th June 2002
  7. * Updated: 6th May 2010
  8. *
  9. * Home: http://stlsoft.org/
  10. *
  11. * Copyright (c) 2002-2010, Matthew Wilson and Synesis Software
  12. * All rights reserved.
  13. *
  14. * Redistribution and use in source and binary forms, with or without
  15. * modification, are permitted provided that the following conditions are met:
  16. *
  17. * - Redistributions of source code must retain the above copyright notice, this
  18. * list of conditions and the following disclaimer.
  19. * - Redistributions in binary form must reproduce the above copyright notice,
  20. * this list of conditions and the following disclaimer in the documentation
  21. * and/or other materials provided with the distribution.
  22. * - Neither the name(s) of Matthew Wilson and Synesis Software nor the names of
  23. * any contributors may be used to endorse or promote products derived from
  24. * this software without specific prior written permission.
  25. *
  26. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  27. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  28. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  29. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  30. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  31. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  32. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  33. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  34. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  35. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  36. * POSSIBILITY OF SUCH DAMAGE.
  37. *
  38. * ////////////////////////////////////////////////////////////////////// */
  39. /** \file comstl/conversion/interface_cast.hpp
  40. *
  41. * \brief [C++ only] Definition of the
  42. * comstl::interface_cast,
  43. * comstl::interface_cast_test
  44. * and
  45. * comstl::try_interface_cast
  46. * cast functions
  47. * and the
  48. * comstl::interface_cast_addref
  49. * and
  50. * comstl::interface_cast_noaddref
  51. * cast classes.
  52. * (\ref group__library__conversion "Conversion" Library).
  53. */
  54. #ifndef COMSTL_INCL_COMSTL_CONVERSION_HPP_INTERFACE_CAST
  55. #define COMSTL_INCL_COMSTL_CONVERSION_HPP_INTERFACE_CAST
  56. #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
  57. # define COMSTL_VER_COMSTL_CONVERSION_HPP_INTERFACE_CAST_MAJOR 5
  58. # define COMSTL_VER_COMSTL_CONVERSION_HPP_INTERFACE_CAST_MINOR 2
  59. # define COMSTL_VER_COMSTL_CONVERSION_HPP_INTERFACE_CAST_REVISION 4
  60. # define COMSTL_VER_COMSTL_CONVERSION_HPP_INTERFACE_CAST_EDIT 117
  61. #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
  62. /* /////////////////////////////////////////////////////////////////////////
  63. * Compatibility
  64. */
  65. /*
  66. [Incompatibilies-start]
  67. STLSOFT_COMPILER_IS_MSVC: _MSC_VER<1200
  68. [Incompatibilies-end]
  69. */
  70. /* /////////////////////////////////////////////////////////////////////////
  71. * Includes
  72. */
  73. #ifndef COMSTL_INCL_COMSTL_H_COMSTL
  74. # include <comstl/comstl.h>
  75. #endif /* !COMSTL_INCL_COMSTL_H_COMSTL */
  76. #if defined(STLSOFT_COMPILER_IS_MSVC) && \
  77. _MSC_VER < 1200
  78. # error comstl/conversion/interface_cast.hpp is not compatible with Visual C++ 5.0 or earlier
  79. #endif /* compiler */
  80. #ifndef COMSTL_INCL_COMSTL_UTIL_H_REFCOUNT_FUNCTIONS
  81. # include <comstl/util/refcount_functions.h>
  82. #endif /* !COMSTL_INCL_COMSTL_UTIL_H_REFCOUNT_FUNCTIONS */
  83. #ifndef COMSTL_INCL_COMSTL_UTIL_HPP_INTERFACE_TRAITS
  84. # include <comstl/util/interface_traits.hpp>
  85. #endif /* !COMSTL_INCL_COMSTL_UTIL_HPP_INTERFACE_TRAITS */
  86. #ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  87. # ifndef COMSTL_INCL_COMSTL_ERROR_HPP_BAD_INTERFACE_CAST
  88. # include <comstl/error/bad_interface_cast.hpp>
  89. # endif /* !COMSTL_INCL_COMSTL_ERROR_HPP_BAD_INTERFACE_CAST */
  90. #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  91. #ifndef STLSOFT_INCL_STLSOFT_SMARTPTR_HPP_REF_PTR
  92. # include <stlsoft/smartptr/ref_ptr.hpp>
  93. #endif /* !STLSOFT_INCL_STLSOFT_SMARTPTR_HPP_REF_PTR */
  94. #ifndef STLSOFT_INCL_STLSOFT_UTIL_HPP_OPERATOR_BOOL
  95. # include <stlsoft/util/operator_bool.hpp>
  96. #endif /* !STLSOFT_INCL_STLSOFT_UTIL_HPP_OPERATOR_BOOL */
  97. /* /////////////////////////////////////////////////////////////////////////
  98. * Namespace
  99. */
  100. #ifndef _COMSTL_NO_NAMESPACE
  101. # if defined(_STLSOFT_NO_NAMESPACE) || \
  102. defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
  103. /* There is no stlsoft namespace, so must define ::comstl */
  104. namespace comstl
  105. {
  106. # else
  107. /* Define stlsoft::comstl_project */
  108. namespace stlsoft
  109. {
  110. namespace comstl_project
  111. {
  112. # endif /* _STLSOFT_NO_NAMESPACE */
  113. #endif /* !_COMSTL_NO_NAMESPACE */
  114. /* /////////////////////////////////////////////////////////////////////////
  115. * Functions
  116. */
  117. // This helper converts from an interface pointer to itself.
  118. //
  119. // It explicitly takes and returns pointer so that it disambiguates from any
  120. // overload that takes an interface wrapper instance by value/reference.
  121. template <ss_typename_param_k I>
  122. inline I* simple_interface_cast(I* pi)
  123. {
  124. return pi;
  125. }
  126. /* /////////////////////////////////////////////////////////////////////////
  127. * Functionals
  128. */
  129. /** \brief A function class that does not throw any exceptions. For use with
  130. * comstl::interface_cast_noaddref and comstl::interface_cast_addref cast
  131. * classes.
  132. *
  133. * \ingroup group__library__conversion
  134. */
  135. // [[synesis:class:exception-policy: ignore_interface_cast_exception]]
  136. struct ignore_interface_cast_exception
  137. {
  138. public:
  139. /// The exception type
  140. struct thrown_type
  141. {
  142. };
  143. public:
  144. /// The function call operator, which does not throw an exception
  145. ///
  146. /// \param hr The HRESULT that caused the error
  147. /// \param riid The REFIID that could not be acquired
  148. void operator ()(HRESULT hr, REFIID riid) stlsoft_throw_0()
  149. {
  150. STLSOFT_SUPPRESS_UNUSED(hr);
  151. STLSOFT_SUPPRESS_UNUSED(riid);
  152. // Do nothing
  153. }
  154. };
  155. #ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  156. /** \brief A function class that throws the
  157. * \link comstl::bad_interface_cast bad_interface_cast\endlink exception
  158. * class. For use with
  159. * comstl::interface_cast_noaddref and comstl::interface_cast_addref cast
  160. * classes.
  161. *
  162. * \ingroup group__library__conversion
  163. */
  164. // [[synesis:class:exception-policy: throw_bad_interface_cast_exception]]
  165. struct throw_bad_interface_cast_exception
  166. {
  167. public:
  168. /// The exception type
  169. typedef bad_interface_cast thrown_type;
  170. public:
  171. /// The function call operator, which throws the exception
  172. ///
  173. /// \param hr The HRESULT that caused the error
  174. /// \param riid The REFIID that could not be acquired
  175. void operator ()(HRESULT hr, REFIID riid) stlsoft_throw_1(bad_interface_cast)
  176. {
  177. STLSOFT_THROW_X(bad_interface_cast(riid, hr));
  178. }
  179. };
  180. #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  181. /** \brief A function class that calls Release() on the interface. For use with
  182. * comstl::interface_cast_noaddref and comstl::interface_cast_addref cast
  183. * classes.
  184. *
  185. * \ingroup group__library__conversion
  186. */
  187. template <ss_typename_param_k I>
  188. struct noaddref_release
  189. {
  190. public:
  191. /// The function call operator, that calls Release() on the interface
  192. ///
  193. /// \param pi The interface pointer
  194. void operator ()(I pi)
  195. {
  196. release(pi);
  197. }
  198. };
  199. /** \brief A function class that does not call Release() on the interface. For use with
  200. * comstl::interface_cast_noaddref and comstl::interface_cast_addref cast
  201. * classes.
  202. *
  203. * \ingroup group__library__conversion
  204. */
  205. template <ss_typename_param_k I>
  206. struct addref_release
  207. {
  208. public:
  209. /// The function call operator, which does not call Release() on the interface
  210. ///
  211. /// \param pi The interface pointer
  212. void operator ()(I pi)
  213. {
  214. STLSOFT_SUPPRESS_UNUSED(pi);
  215. }
  216. };
  217. /* /////////////////////////////////////////////////////////////////////////
  218. * Raw-pointer safety
  219. *
  220. * Alas this requires partial template specialisation, so is not available in
  221. * all environments.
  222. */
  223. #ifdef STLSOFT_CF_TEMPLATE_PARTIAL_SPECIALISATION_SUPPORT
  224. # ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
  225. template <ss_typename_param_k I>
  226. struct interface_pointer_traits;
  227. template <ss_typename_param_k I>
  228. struct interface_pointer_traits<I*>
  229. {
  230. typedef I interface_type;
  231. };
  232. # endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
  233. /** \brief A veneer interface that hides the AddRef() and Release() methods.
  234. * For use with comstl::interface_cast_noaddref and
  235. * comstl::interface_cast_addref cast classes.
  236. *
  237. * \ingroup group__library__conversion
  238. */
  239. template <ss_typename_param_k I>
  240. interface protect_refcount
  241. : public I
  242. {
  243. private:
  244. STDMETHOD_(ULONG, AddRef)()
  245. {
  246. I* pi = static_cast<I*>(this);
  247. return pi->AddRef();
  248. }
  249. STDMETHOD_(ULONG, Release)()
  250. {
  251. I* pi = static_cast<I*>(this);
  252. return pi->Release();
  253. }
  254. };
  255. #endif /* STLSOFT_CF_TEMPLATE_PARTIAL_SPECIALISATION_SUPPORT */
  256. /* /////////////////////////////////////////////////////////////////////////
  257. * Classes
  258. */
  259. /** \brief Base class for the interface cast classes
  260. * comstl::interface_cast_noaddref and comstl::interface_cast_addref.
  261. *
  262. * \ingroup group__library__conversion
  263. *
  264. * This class serves only as a base, and cannot be used in isolation
  265. *
  266. * \param I The interface pointer type
  267. * \param R The release type
  268. * \param X The exception type
  269. */
  270. // [[synesis:class:implementation: comstl::interface_cast_base]]
  271. template< ss_typename_param_k I
  272. , ss_typename_param_k R
  273. , ss_typename_param_k X
  274. >
  275. class interface_cast_base
  276. {
  277. /// \name Member Types
  278. /// @{
  279. public:
  280. /// The interface pointer type
  281. typedef I interface_pointer_type;
  282. #ifdef STLSOFT_CF_TEMPLATE_PARTIAL_SPECIALISATION_SUPPORT
  283. /// The interface type
  284. typedef ss_typename_type_k interface_pointer_traits<I>::interface_type interface_type;
  285. #endif /* STLSOFT_CF_TEMPLATE_PARTIAL_SPECIALISATION_SUPPORT */
  286. /// The release type
  287. typedef R release_type;
  288. /// The exception type
  289. typedef X exception_policy_type;
  290. /// The thrown type
  291. typedef ss_typename_type_k exception_policy_type::thrown_type thrown_type;
  292. /// The type of the current parameterisation
  293. typedef interface_cast_base<I, R, X> class_type;
  294. /// @}
  295. /// \name Member Constants
  296. /// @{
  297. protected:
  298. enum NullThrowPermission
  299. {
  300. allowNull
  301. , throwOnNull
  302. };
  303. /// @}
  304. /// \name Construction
  305. /// @{
  306. protected:
  307. /// Constructor that attempts the speculative cast
  308. #ifdef STLSOFT_CF_MEMBER_TEMPLATE_CTOR_SUPPORT
  309. template <ss_typename_param_k J>
  310. ss_explicit_k interface_cast_base(J &j, NullThrowPermission permission)
  311. : m_pi(do_cast(simple_interface_cast(j), permission))
  312. {}
  313. #else /* ? STLSOFT_CF_MEMBER_TEMPLATE_CTOR_SUPPORT */
  314. ss_explicit_k interface_cast_base(LPUNKNOWN punk, NullThrowPermission permission)
  315. : m_pi(do_cast(punk, permission))
  316. {}
  317. #endif /* STLSOFT_CF_MEMBER_TEMPLATE_CTOR_SUPPORT */
  318. /// Constructor that directly casts (without calling QueryInterface())
  319. ss_explicit_k interface_cast_base(interface_pointer_type pi)
  320. : m_pi(pi)
  321. {
  322. addref(m_pi);
  323. }
  324. /// Releases the acquired interface pointer according to the \c release_type policy
  325. ~interface_cast_base() stlsoft_throw_0()
  326. {
  327. if(NULL != m_pi)
  328. {
  329. release_type()(m_pi);
  330. }
  331. }
  332. /// @}
  333. /// \name Implementation
  334. /// @{
  335. private:
  336. /// Perform the cast, throwing the \c exception_policy_type's \c thrown_type if the
  337. /// requested interface cannot be acquired.
  338. ///
  339. /// \param punk The interface pointer to cast
  340. /// \return The converted interface pointer
  341. static interface_pointer_type do_cast(LPUNKNOWN punk, NullThrowPermission permission) stlsoft_throw_1(thrown_type)
  342. {
  343. interface_pointer_type pi;
  344. if(NULL == punk)
  345. {
  346. if(throwOnNull == permission)
  347. {
  348. exception_policy_type()(E_INVALIDARG, IID_traits<interface_pointer_type>().iid());
  349. COMSTL_MESSAGE_ASSERT("The derived class does not support null pointers, but the exception policy failed to throw an exception: the program's behaviour will be undefined!", 0);
  350. }
  351. pi = NULL;
  352. }
  353. else
  354. {
  355. REFIID iid = IID_traits<interface_pointer_type>().iid();
  356. HRESULT hr = punk->QueryInterface(iid, reinterpret_cast<void**>(&pi));
  357. if(FAILED(hr))
  358. {
  359. exception_policy_type()(hr, iid);
  360. pi = NULL;
  361. }
  362. }
  363. return pi;
  364. }
  365. /// @}
  366. /// \name Accessors
  367. /// @{
  368. protected:
  369. /// Returns a non-mutating reference to the acquired interface pointer
  370. interface_pointer_type const& get_pointer_()
  371. {
  372. return m_pi;
  373. }
  374. /// Returns a copy of the acquired interface pointer
  375. interface_pointer_type get_pointer_() const
  376. {
  377. return m_pi;
  378. }
  379. /// @}
  380. /// \name Members
  381. /// @{
  382. private:
  383. interface_pointer_type const m_pi;
  384. /// @}
  385. /// \name Not to be implemented
  386. /// @{
  387. protected:
  388. interface_cast_base(class_type const& rhs);
  389. private:
  390. class_type const& operator =(class_type const& rhs);
  391. /// @}
  392. };
  393. #ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  394. /** \brief Interface cast for raw pointers that does not add a net reference count
  395. *
  396. * \ingroup group__library__conversion
  397. *
  398. * This class provides a cast between interface pointers, but does not add a net reference count
  399. *
  400. * \param I The interface pointer type
  401. * \param X The exception type
  402. *
  403. * \note This class is not defined when exception-handling is not supported.
  404. */
  405. // [[synesis:class:cast: comstl::interface_cast_noaddref]]
  406. template< ss_typename_param_k I
  407. , ss_typename_param_k X = throw_bad_interface_cast_exception
  408. >
  409. class interface_cast_noaddref
  410. : protected interface_cast_base<I, noaddref_release<I>, X>
  411. {
  412. private: // Member Types
  413. typedef interface_cast_base<I, noaddref_release<I>, X> parent_class_type;
  414. public:
  415. /// The type of the current parameterisation
  416. typedef interface_cast_noaddref<I, X> class_type;
  417. /// The interface pointer type
  418. typedef ss_typename_type_k parent_class_type::interface_pointer_type interface_pointer_type;
  419. # ifdef STLSOFT_CF_TEMPLATE_PARTIAL_SPECIALISATION_SUPPORT
  420. /// The interface type
  421. typedef ss_typename_type_k parent_class_type::interface_type interface_type;
  422. typedef protect_refcount<interface_type>* protected_interface_pointer_type;
  423. # else /* ? STLSOFT_CF_TEMPLATE_PARTIAL_SPECIALISATION_SUPPORT */
  424. typedef interface_pointer_type protected_interface_pointer_type;
  425. # endif /* STLSOFT_CF_TEMPLATE_PARTIAL_SPECIALISATION_SUPPORT */
  426. public: // Construction
  427. /// Constructor that attempts the speculative cast
  428. # ifdef STLSOFT_CF_MEMBER_TEMPLATE_CTOR_SUPPORT
  429. template <ss_typename_param_k J>
  430. ss_explicit_k interface_cast_noaddref(J &j)
  431. : parent_class_type(j, parent_class_type::throwOnNull)
  432. {
  433. COMSTL_MESSAGE_ASSERT("Cannot initialise with a null pointer. Program behaviour will be undefined when it this instance is dereference", NULL != this->parent_class_type::get_pointer_());
  434. }
  435. # else /* ? STLSOFT_CF_MEMBER_TEMPLATE_CTOR_SUPPORT */
  436. ss_explicit_k interface_cast_noaddref(LPUNKNOWN punk)
  437. : parent_class_type(punk, parent_class_type::throwOnNull)
  438. {
  439. COMSTL_MESSAGE_ASSERT("Cannot initialise with a null pointer. Program behaviour will be undefined when it this instance is dereference", NULL != this->parent_class_type::get_pointer_());
  440. }
  441. # endif /* STLSOFT_CF_MEMBER_TEMPLATE_CTOR_SUPPORT */
  442. /// Constructor that directly casts (without calling QueryInterface())
  443. ss_explicit_k interface_cast_noaddref(interface_pointer_type pi)
  444. : parent_class_type(pi, parent_class_type::throwOnNull)
  445. {
  446. COMSTL_MESSAGE_ASSERT("Cannot initialise with a null pointer. Program behaviour will be undefined when it this instance is dereference", NULL != this->parent_class_type::get_pointer_());
  447. }
  448. # ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
  449. ~interface_cast_noaddref() stlsoft_throw_0()
  450. {} // We need to provide this to persuade VC6 to call the parent class dtor
  451. # endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
  452. // Accessors
  453. public:
  454. /// Access the members of the interface
  455. protected_interface_pointer_type operator -> () const
  456. {
  457. COMSTL_MESSAGE_ASSERT("Attempting to dereference null pointer. Exception model should not be null", NULL != this->parent_class_type::get_pointer_());
  458. return static_cast<protected_interface_pointer_type>(this->parent_class_type::get_pointer_());
  459. }
  460. // Not to be implemented
  461. private:
  462. interface_cast_noaddref(class_type const& rhs);
  463. class_type const& operator =(class_type const& rhs);
  464. // These are defined to placate Borland C/C++
  465. void* operator new(cs_size_t /* si */) { return 0; }
  466. void operator delete(void* /* pv */) {}
  467. };
  468. #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  469. /** \brief Interface cast for raw pointers that does add a net reference count
  470. *
  471. * \ingroup group__library__conversion
  472. *
  473. * This class provides a cast between interface pointers, that adds a net reference count
  474. *
  475. * \param I The interface pointer type
  476. * \param X The exception type, defaulted to ignore_interface_cast_exception
  477. */
  478. // [[synesis:class:cast: comstl::interface_cast_addref]]
  479. template< ss_typename_param_k I
  480. , ss_typename_param_k X = ignore_interface_cast_exception
  481. >
  482. class interface_cast_addref
  483. : protected interface_cast_base<I, addref_release<I>, X>
  484. {
  485. private: // Member Types
  486. typedef interface_cast_base<I, addref_release<I>, X> parent_class_type;
  487. public:
  488. /// The type of the current parameterisation
  489. typedef interface_cast_addref<I, X> class_type;
  490. /// The interface pointer type
  491. typedef ss_typename_type_k parent_class_type::interface_pointer_type interface_pointer_type;
  492. public: // Construction
  493. /// Constructor that attempts the speculative cast
  494. #ifdef STLSOFT_CF_MEMBER_TEMPLATE_CTOR_SUPPORT
  495. template <ss_typename_param_k J>
  496. ss_explicit_k interface_cast_addref(J j)
  497. : parent_class_type(j, parent_class_type::allowNull)
  498. {}
  499. #else /* ? STLSOFT_CF_MEMBER_TEMPLATE_CTOR_SUPPORT */
  500. ss_explicit_k interface_cast_addref(LPUNKNOWN punk)
  501. : parent_class_type(punk, parent_class_type::allowNull)
  502. {}
  503. #endif /* STLSOFT_CF_MEMBER_TEMPLATE_CTOR_SUPPORT */
  504. /// Constructor that directly casts (without calling QueryInterface())
  505. ss_explicit_k interface_cast_addref(interface_pointer_type pi)
  506. : parent_class_type(pi, parent_class_type::allowNull)
  507. {}
  508. #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
  509. ~interface_cast_addref() stlsoft_throw_0()
  510. {} // We need to provide this to persuade VC6 to call the parent class dtor
  511. #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
  512. // Accessors
  513. public:
  514. /// A pointer to the acquired interface
  515. operator interface_pointer_type ()
  516. {
  517. return this->parent_class_type::get_pointer_();
  518. }
  519. // Not to be implemented
  520. private:
  521. interface_cast_addref(class_type const& rhs);
  522. class_type const& operator =(class_type const& rhs);
  523. // These are defined to placate Borland C/C++
  524. void* operator new(cs_size_t /* si */) { return 0; }
  525. void operator delete(void* /* pv */) {}
  526. };
  527. #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
  528. // [[synesis:class:cast: comstl::interface_cast_tester]]
  529. template< ss_typename_param_k I
  530. >
  531. class interface_cast_tester
  532. : protected interface_cast_base<I, noaddref_release<I>, ignore_interface_cast_exception>
  533. {
  534. private: // Member Types
  535. typedef interface_cast_base<I, noaddref_release<I>, ignore_interface_cast_exception> parent_class_type;
  536. public:
  537. /// The type of the current parameterisation
  538. typedef interface_cast_tester<I> class_type;
  539. /// The interface pointer type
  540. typedef ss_typename_type_k parent_class_type::interface_pointer_type interface_pointer_type;
  541. #ifdef STLSOFT_CF_TEMPLATE_PARTIAL_SPECIALISATION_SUPPORT
  542. /// The interface type
  543. typedef ss_typename_type_k parent_class_type::interface_type interface_type;
  544. typedef protect_refcount<interface_type>* protected_interface_pointer_type;
  545. #else /* ? STLSOFT_CF_TEMPLATE_PARTIAL_SPECIALISATION_SUPPORT */
  546. typedef interface_pointer_type protected_interface_pointer_type;
  547. #endif /* STLSOFT_CF_TEMPLATE_PARTIAL_SPECIALISATION_SUPPORT */
  548. public: // Construction
  549. /// Constructor that attempts the speculative cast
  550. #ifdef STLSOFT_CF_MEMBER_TEMPLATE_CTOR_SUPPORT
  551. template <ss_typename_param_k J>
  552. ss_explicit_k interface_cast_tester(J &j)
  553. : parent_class_type(j, parent_class_type::allowNull)
  554. {}
  555. #else /* ? STLSOFT_CF_MEMBER_TEMPLATE_CTOR_SUPPORT */
  556. ss_explicit_k interface_cast_tester(LPUNKNOWN punk)
  557. : parent_class_type(punk, parent_class_type::allowNull)
  558. {}
  559. #endif /* STLSOFT_CF_MEMBER_TEMPLATE_CTOR_SUPPORT */
  560. /// Constructor that directly casts (without calling QueryInterface())
  561. ss_explicit_k interface_cast_tester(interface_pointer_type pi)
  562. : parent_class_type(pi, parent_class_type::allowNull)
  563. {}
  564. #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
  565. ~interface_cast_tester() stlsoft_throw_0()
  566. {} // We need to provide this to persuade VC6 to call the parent class dtor
  567. #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
  568. /// \name State
  569. /// @{
  570. private:
  571. STLSOFT_DEFINE_OPERATOR_BOOL_TYPES_T(class_type, operator_bool_generator_type, operator_bool_type);
  572. public:
  573. /// Represents whether the cast succeeded
  574. ///
  575. /// \retval true The interface cast succeeded
  576. /// \retval false The interface cast failed
  577. operator operator_bool_type() const
  578. {
  579. return operator_bool_generator_type::translate(NULL != this->parent_class_type::get_pointer_());
  580. }
  581. /// Represents whether the cast failed
  582. ///
  583. /// \retval true The interface cast failed
  584. /// \retval false The interface cast succeeded
  585. cs_bool_t operator !() const
  586. {
  587. return NULL == this->parent_class_type::get_pointer_();
  588. }
  589. /// @}
  590. // Not to be implemented
  591. private:
  592. interface_cast_tester(class_type const& rhs);
  593. class_type const& operator =(class_type const& rhs);
  594. // These are defined to placate Borland C/C++
  595. void* operator new(cs_size_t /* si */) { return 0; }
  596. void operator delete(void* /* pv */) {}
  597. };
  598. #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
  599. /** \brief Determines whether an interface is available on an object
  600. *
  601. * \ingroup group__library__conversion
  602. *
  603. * \param src The object whose capabilities will be tested. May be NULL
  604. *
  605. \code
  606. IStream* stm = . . .
  607. if(comstl::interface_cast_test<IStorage*>(stm))
  608. {
  609. printf("Object has IStorage interface\n");
  610. }
  611. else
  612. {
  613. printf("Object does not have IStorage interface\n");
  614. }
  615. \endcode
  616. */
  617. template< ss_typename_param_k IDest
  618. , ss_typename_param_k ISrc
  619. >
  620. #if defined(STLSOFT_COMPILER_IS_MSVC) && \
  621. _MSC_VER < 1300
  622. // This workaround is required to stop the poor dear from instantiating
  623. // interface_cast_tester on ISrc rather than IDest.
  624. inline cs_bool_t interface_cast_test(ISrc* src, IDest* = NULL)
  625. #else /* ? compiler */
  626. inline cs_bool_t interface_cast_test(ISrc* src)
  627. #endif /* compiler */
  628. {
  629. interface_cast_tester<IDest> b(src);
  630. return !!b;
  631. }
  632. /** \brief Determines whether an interface is available on an object
  633. *
  634. * \ingroup group__library__conversion
  635. *
  636. \code
  637. stlsoft::ref_ptr<IStream> stm = . . .
  638. if(comstl::interface_cast_test<IStorage>(stm))
  639. {
  640. printf("Wrapper object has IStorage interface\n");
  641. }
  642. else
  643. {
  644. printf("Wrapper object does not have IStorage interface\n");
  645. }
  646. \endcode
  647. *
  648. * \param src wrapper instance holding the object whose capabilities
  649. * will be tested. May be empty.
  650. */
  651. template< ss_typename_param_k IDest
  652. , ss_typename_param_k ISrc
  653. >
  654. #if defined(STLSOFT_COMPILER_IS_MSVC) && \
  655. _MSC_VER < 1300
  656. // This workaround is required to stop the poor dear from instantiating
  657. // interface_cast_tester on ISrc rather than IDest.
  658. inline cs_bool_t interface_cast_test(stlsoft_ns_qual(ref_ptr)<ISrc> &src, IDest* = NULL)
  659. #else /* ? compiler */
  660. inline cs_bool_t interface_cast_test(stlsoft_ns_qual(ref_ptr)<ISrc> &src)
  661. #endif /* compiler */
  662. {
  663. return interface_cast_test<IDest*>(src.get());
  664. }
  665. #ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  666. /** \brief Casts a raw interface pointer to a wrapped instance.
  667. *
  668. * \ingroup group__library__conversion
  669. *
  670. \code
  671. IStream* stm = . . .
  672. try
  673. {
  674. stlsoft::ref_ptr<IStorage> stg = comstl::interface_cast<IStorage>(stm);
  675. printf("Object has IStorage interface\n");
  676. }
  677. catch(comstl::bad_interface_cast &)
  678. {
  679. printf("Object does not have IStorage interface\n");
  680. }
  681. \endcode
  682. *
  683. * \note For technical reasons, the cast destination type differs from the
  684. * conventional behaviour. Rather than specifying the actual resultant
  685. * type, e.g. <code>stlsoft::ref_ptr<IStream></code>, just the destination
  686. * interface type must be specified, e.g.
  687. * <code>interface_cast<IStream></code>.
  688. *
  689. * \exception comstl::bad_interface_cast When compiling with exception -
  690. * detected when <code>STLSOFT_CF_EXCEPTION_SUPPORT</code> is defined -
  691. * this will throw an instance of comstl::bad_interface_cast if the
  692. * requested interface cannot be acquired. When compiling absent exception
  693. * support, this cast function is not defined; instead use
  694. * comstl::try_interface_cast.
  695. */
  696. template< ss_typename_param_k IDest
  697. , ss_typename_param_k ISrc
  698. >
  699. inline stlsoft_ns_qual(ref_ptr)<IDest> interface_cast(ISrc* src)
  700. {
  701. interface_cast_addref<IDest*, throw_bad_interface_cast_exception> ptr(src); // This has to be separate, otherwise G++ has a spit
  702. return stlsoft_ns_qual(ref_ptr)<IDest>(static_cast<IDest*>(ptr), false);
  703. }
  704. /** \brief Casts between instances of wrapped instances
  705. *
  706. * \ingroup group__library__conversion
  707. *
  708. \code
  709. stlsoft::ref_ptr<IStream> stm = . . .
  710. try
  711. {
  712. stlsoft::ref_ptr<IStorage> stg = comstl::interface_cast<IStorage>(stm);
  713. printf("Wrapper object has IStorage interface\n");
  714. }
  715. catch(comstl::bad_interface_cast &)
  716. {
  717. printf("Wrapper object does not have IStorage interface\n");
  718. }
  719. \endcode
  720. *
  721. * \note For technical reasons, the cast destination type differs from the
  722. * conventional behaviour. Rather than specifying the actual resultant
  723. * type, e.g. <code>stlsoft::ref_ptr<IStream></code>, just the destination
  724. * interface type must be specified, e.g.
  725. * <code>interface_cast<IStream></code>.
  726. *
  727. * \exception comstl::bad_interface_cast When compiling with exception -
  728. * detected when <code>STLSOFT_CF_EXCEPTION_SUPPORT</code> is defined -
  729. * this will throw an instance of comstl::bad_interface_cast if the
  730. * requested interface cannot be acquired. When compiling absent exception
  731. * support, this cast function is not defined; instead use
  732. * comstl::try_interface_cast.
  733. */
  734. template< ss_typename_param_k IDest
  735. , ss_typename_param_k ISrc
  736. >
  737. inline stlsoft_ns_qual(ref_ptr)<IDest> interface_cast(stlsoft_ns_qual(ref_ptr)<ISrc> src)
  738. {
  739. return interface_cast<IDest>(src.get());
  740. }
  741. #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  742. /** \brief Attempts to cast a raw interface pointer to a wrapped instance.
  743. *
  744. * \ingroup group__library__conversion
  745. *
  746. \code
  747. IStream* pstm = . . .
  748. stlsoft::ref_ptr<IStorage> stg = comstl::interface_cast<IStorage>(pstm);
  749. if(!stg.empty())
  750. {
  751. . . . // use stg->
  752. }
  753. \endcode
  754. *
  755. * \note For technical reasons, the cast destination type differs from the
  756. * conventional behaviour. Rather than specifying the actual resultant
  757. * type, e.g. <code>stlsoft::ref_ptr<IStream></code>, just the destination
  758. * interface type must be specified, e.g.
  759. * <code>interface_cast<IStream></code>.
  760. *
  761. * \return
  762. */
  763. template< ss_typename_param_k IDest
  764. , ss_typename_param_k ISrc
  765. >
  766. inline stlsoft_ns_qual(ref_ptr)<IDest> try_interface_cast(ISrc* src)
  767. {
  768. interface_cast_addref<IDest*> ptr(src); // This has to be separate, otherwise G++ has a spit
  769. return stlsoft_ns_qual(ref_ptr)<IDest>(static_cast<IDest*>(ptr), false);
  770. }
  771. /** \brief Attempts to cast between instances of wrapped instances
  772. *
  773. * \ingroup group__library__conversion
  774. *
  775. * \note For technical reasons, the cast destination type differs from the
  776. * conventional behaviour. Rather than specifying the actual resultant
  777. * type, e.g. <code>stlsoft::ref_ptr<IStream></code>, just the destination
  778. * interface type must be specified, e.g.
  779. * <code>interface_cast<IStream></code>.
  780. */
  781. template< ss_typename_param_k IDest
  782. , ss_typename_param_k ISrc
  783. >
  784. inline stlsoft_ns_qual(ref_ptr)<IDest> try_interface_cast(stlsoft_ns_qual(ref_ptr)<ISrc> src)
  785. {
  786. return try_interface_cast<IDest>(src.get());
  787. }
  788. /* /////////////////////////////////////////////////////////////////////////
  789. * Shims
  790. */
  791. #ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  792. # if !defined(STLSOFT_COMPILER_IS_COMO) && \
  793. !defined(STLSOFT_COMPILER_IS_GCC)
  794. /** \brief Attribute shim to retrieve the interface pointer of the given cast instance
  795. *
  796. * \ingroup group__concept__shim__pointer_attribute__get_ptr
  797. *
  798. * \param p The cast instance
  799. */
  800. template< ss_typename_param_k I
  801. , ss_typename_param_k X
  802. >
  803. inline I get_ptr(comstl_ns_qual(interface_cast_noaddref)<I, X> &p)
  804. {
  805. return p.operator -> ();
  806. }
  807. # endif /* compiler */
  808. /** \brief Attribute shim to retrieve the interface pointer of the given cast instance
  809. *
  810. * \ingroup group__concept__shim__pointer_attribute__get_ptr
  811. *
  812. * \param p The cast instance
  813. */
  814. template< ss_typename_param_k I
  815. , ss_typename_param_k X
  816. >
  817. inline I get_ptr(comstl_ns_qual(interface_cast_noaddref)<I, X> const& p)
  818. {
  819. return p.operator -> ();
  820. }
  821. #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  822. /** \brief Attribute shim to retrieve the interface pointer of the given cast instance
  823. *
  824. * \ingroup group__concept__shim__pointer_attribute__get_ptr
  825. *
  826. * \param p The cast instance
  827. */
  828. template< ss_typename_param_k I
  829. , ss_typename_param_k X
  830. >
  831. inline I get_ptr(comstl_ns_qual(interface_cast_addref)<I, X> &p)
  832. {
  833. return p;
  834. }
  835. /** \brief Attribute shim to retrieve the interface pointer of the given cast instance
  836. *
  837. * \ingroup group__concept__shim__pointer_attribute__get_ptr
  838. *
  839. * \param p The cast instance
  840. */
  841. template< ss_typename_param_k I
  842. , ss_typename_param_k X
  843. >
  844. inline I const get_ptr(comstl_ns_qual(interface_cast_addref)<I, X> const& p)
  845. {
  846. return p;
  847. }
  848. /* /////////////////////////////////////////////////////////////////////////
  849. * Deprecated Shims
  850. */
  851. #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
  852. # ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  853. template< ss_typename_param_k I
  854. , ss_typename_param_k X
  855. >
  856. inline cs_bool_t is_empty(comstl_ns_qual(interface_cast_noaddref)<I, X> const& p)
  857. {
  858. return NULL != get_ptr(p);
  859. }
  860. # endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  861. template< ss_typename_param_k I
  862. , ss_typename_param_k X
  863. >
  864. inline cs_bool_t is_empty(comstl_ns_qual(interface_cast_addref)<I, X> const& p)
  865. {
  866. return NULL != get_ptr(p);
  867. }
  868. #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
  869. ////////////////////////////////////////////////////////////////////////////
  870. // Unit-testing
  871. #ifdef STLSOFT_UNITTEST
  872. # include "./unittest/interface_cast_unittest_.h"
  873. #endif /* STLSOFT_UNITTEST */
  874. /* ////////////////////////////////////////////////////////////////////// */
  875. #ifndef _COMSTL_NO_NAMESPACE
  876. # if defined(_STLSOFT_NO_NAMESPACE) || \
  877. defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
  878. } // namespace comstl
  879. # else
  880. } // namespace comstl_project
  881. } // namespace stlsoft
  882. # endif /* _STLSOFT_NO_NAMESPACE */
  883. #endif /* !_COMSTL_NO_NAMESPACE */
  884. /* /////////////////////////////////////////////////////////////////////////
  885. * Namespace
  886. *
  887. * The string access shims exist either in the stlsoft namespace, or in the
  888. * global namespace. This is required by the lookup rules.
  889. *
  890. */
  891. #ifndef _COMSTL_NO_NAMESPACE
  892. # if !defined(_STLSOFT_NO_NAMESPACE) && \
  893. !defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
  894. namespace stlsoft
  895. {
  896. # else /* ? _STLSOFT_NO_NAMESPACE */
  897. /* There is no stlsoft namespace, so must define in the global namespace */
  898. # endif /* !_STLSOFT_NO_NAMESPACE */
  899. using ::comstl::get_ptr;
  900. #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
  901. using ::comstl::is_empty;
  902. #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
  903. # if !defined(_STLSOFT_NO_NAMESPACE) && \
  904. !defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
  905. } // namespace stlsoft
  906. # else /* ? _STLSOFT_NO_NAMESPACE */
  907. /* There is no stlsoft namespace, so must define in the global namespace */
  908. # endif /* !_STLSOFT_NO_NAMESPACE */
  909. #endif /* !_COMSTL_NO_NAMESPACE */
  910. /* ////////////////////////////////////////////////////////////////////// */
  911. #endif /* !COMSTL_INCL_COMSTL_CONVERSION_HPP_INTERFACE_CAST */
  912. /* ///////////////////////////// end of file //////////////////////////// */