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.

1033 lines
35 KiB

  1. /* /////////////////////////////////////////////////////////////////////////
  2. * File: comstl/collections/enumerator_sequence.hpp (originally MOEnSeq.h, ::SynesisCom)
  3. *
  4. * Purpose: STL sequence for IEnumXXXX enumerator interfaces.
  5. *
  6. * Created: 17th September 1998
  7. * Updated: 3rd February 2012
  8. *
  9. * Thanks: To Eduardo Bezerra and Vivi Orunitia for reporting
  10. * incompatibilities with Borland's 5.82 (Turbo C++). The awful
  11. * preprocessor hack around retrievalQuanta are the result. ;)
  12. *
  13. * Home: http://stlsoft.org/
  14. *
  15. * Copyright (c) 1998-2012, Matthew Wilson and Synesis Software
  16. * All rights reserved.
  17. *
  18. * Redistribution and use in source and binary forms, with or without
  19. * modification, are permitted provided that the following conditions are met:
  20. *
  21. * - Redistributions of source code must retain the above copyright notice, this
  22. * list of conditions and the following disclaimer.
  23. * - Redistributions in binary form must reproduce the above copyright notice,
  24. * this list of conditions and the following disclaimer in the documentation
  25. * and/or other materials provided with the distribution.
  26. * - Neither the name(s) of Matthew Wilson and Synesis Software nor the names of
  27. * any contributors may be used to endorse or promote products derived from
  28. * this software without specific prior written permission.
  29. *
  30. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  31. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  32. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  33. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  34. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  35. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  36. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  37. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  38. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  39. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  40. * POSSIBILITY OF SUCH DAMAGE.
  41. *
  42. * ////////////////////////////////////////////////////////////////////// */
  43. /** \file comstl/collections/enumerator_sequence.hpp
  44. *
  45. * \brief [C++ only] Definition of the comstl::enumerator_sequence
  46. * collection class template
  47. * (\ref group__library__collections "Collections" Library).
  48. */
  49. #ifndef COMSTL_INCL_COMSTL_COLLECTIONS_HPP_ENUMERATOR_SEQUENCE
  50. #define COMSTL_INCL_COMSTL_COLLECTIONS_HPP_ENUMERATOR_SEQUENCE
  51. #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
  52. # define COMSTL_VER_COMSTL_COLLECTIONS_HPP_ENUMERATOR_SEQUENCE_MAJOR 6
  53. # define COMSTL_VER_COMSTL_COLLECTIONS_HPP_ENUMERATOR_SEQUENCE_MINOR 1
  54. # define COMSTL_VER_COMSTL_COLLECTIONS_HPP_ENUMERATOR_SEQUENCE_REVISION 4
  55. # define COMSTL_VER_COMSTL_COLLECTIONS_HPP_ENUMERATOR_SEQUENCE_EDIT 251
  56. #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
  57. /* /////////////////////////////////////////////////////////////////////////
  58. * Compatibility
  59. */
  60. /*
  61. [Incompatibilies-start]
  62. STLSOFT_COMPILER_IS_WATCOM:
  63. [Incompatibilies-end]
  64. */
  65. /* /////////////////////////////////////////////////////////////////////////
  66. * Includes
  67. */
  68. #ifndef COMSTL_INCL_COMSTL_H_COMSTL
  69. # include <comstl/comstl.h>
  70. #endif /* !COMSTL_INCL_COMSTL_H_COMSTL */
  71. #ifndef COMSTL_INCL_COMSTL_COLLECTIONS_HPP_ENUMERATION_POLICIES
  72. # include <comstl/collections/enumeration_policies.hpp>
  73. #endif /* !COMSTL_INCL_COMSTL_COLLECTIONS_HPP_ENUMERATION_POLICIES */
  74. #ifndef STLSOFT_INCL_STLSOFT_UTIL_STD_HPP_ITERATOR_HELPER
  75. # include <stlsoft/util/std/iterator_helper.hpp>
  76. #endif /* !STLSOFT_INCL_STLSOFT_UTIL_STD_HPP_ITERATOR_HELPER */
  77. #ifndef STLSOFT_INCL_STLSOFT_COLLECTIONS_UTIL_HPP_COLLECTIONS
  78. # include <stlsoft/collections/util/collections.hpp>
  79. #endif /* !STLSOFT_INCL_STLSOFT_COLLECTIONS_UTIL_HPP_COLLECTIONS */
  80. #ifndef STLSOFT_INCL_STLSOFT_META_HPP_CAPABILITIES
  81. # include <stlsoft/meta/capabilities.hpp>
  82. #endif /* !STLSOFT_INCL_STLSOFT_META_HPP_CAPABILITIES */
  83. #ifdef STLSOFT_CF_TEMPLATE_PARTIAL_SPECIALISATION_SUPPORT
  84. # ifndef STLSOFT_INCL_STLSOFT_META_HPP_BASE_TYPE_TRAITS
  85. # include <stlsoft/meta/base_type_traits.hpp>
  86. # endif /* !STLSOFT_INCL_STLSOFT_META_HPP_BASE_TYPE_TRAITS */
  87. # ifndef STLSOFT_INCL_STLSOFT_META_HPP_SELECT_FIRST_TYPE_IF
  88. # include <stlsoft/meta/select_first_type_if.hpp>
  89. # endif /* !STLSOFT_INCL_STLSOFT_META_HPP_SELECT_FIRST_TYPE_IF */
  90. #endif /* !STLSOFT_CF_TEMPLATE_PARTIAL_SPECIALISATION_SUPPORT */
  91. #ifndef STLSOFT_INCL_ALGORITHM
  92. # define STLSOFT_INCL_ALGORITHM
  93. # include <algorithm>
  94. #endif /* !STLSOFT_INCL_ALGORITHM */
  95. #ifdef STLSOFT_UNITTEST
  96. # include <comstl/util/value_policies.hpp>
  97. # if !defined(STLSOFT_COMPILER_IS_DMC)
  98. # include "./unittest/_recls_COM_decl_.h"
  99. # endif /* compiler */
  100. # if !defined(STLSOFT_COMPILER_IS_COMO)
  101. # include <winstl/dl/dl_call.hpp>
  102. # endif /* compiler */
  103. #endif /* STLSOFT_UNITTEST */
  104. /* /////////////////////////////////////////////////////////////////////////
  105. * Namespace
  106. */
  107. #ifndef _COMSTL_NO_NAMESPACE
  108. # if defined(_STLSOFT_NO_NAMESPACE) || \
  109. defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
  110. /* There is no stlsoft namespace, so must define ::comstl */
  111. namespace comstl
  112. {
  113. # else
  114. /* Define stlsoft::comstl_project */
  115. namespace stlsoft
  116. {
  117. namespace comstl_project
  118. {
  119. # endif /* _STLSOFT_NO_NAMESPACE */
  120. #endif /* !_COMSTL_NO_NAMESPACE */
  121. /* /////////////////////////////////////////////////////////////////////////
  122. * Classes
  123. */
  124. /** \brief A template for adapting COM enumerators to STL-compatible
  125. * sequence iteration.
  126. *
  127. * \ingroup group__library__collections
  128. *
  129. * \param I Interface
  130. * \param V Value type
  131. * \param VP Value policy type
  132. * \param R Reference type. The type returned by the iterator's dereference
  133. * operator. Defaults to <code>V const&</code>. To make it mutable, set to
  134. * <code>V&</code>
  135. * \param CP Cloning policy type. Defaults to cloneable_cloning_policy&lt;I>
  136. * \param Q Quanta. The number of elements retrieved from the enumerator
  137. * with each invocation of Next(). Defaults to 10
  138. *
  139. * The various parameterising types are used to stipulate the interface and
  140. * the value type, and how they are to be handled.
  141. *
  142. * For example, the following parameterisation defines a sequence operating
  143. * over an <code>IEnumGUID</code> enumerator instance.
  144. *
  145. \code
  146. typedef enumerator_sequence<IEnumGUID
  147. , GUID
  148. , GUID_policy
  149. , GUID const&
  150. , forward_cloning_policy<IEnumGUID>
  151. , 5
  152. > enum_sequence_t;
  153. \endcode
  154. *
  155. * The value type is <code>GUID</code> and it is returned as a reference, as
  156. * the <code>GUID const&</code> in fact.
  157. *
  158. * The \ref group__project__comstl type
  159. * <code>GUID_policy</code> controls how the
  160. * <code>GUID</code> instances are initialised, copied and
  161. * destroyed.
  162. *
  163. * The \ref group__project__comstl type forward_cloning_policy allows the sequence to provide
  164. * <a href = "http://sgi.com/tech/stl/ForwardIterator.html">Forward Iterator</a>
  165. * semantics.
  166. *
  167. * And the <code>5</code> indicates that the sequence should
  168. * grab 5 values at a time, to save round trips to the enumerator.
  169. *
  170. * This would be used as follows:
  171. *
  172. \code
  173. void dump_GUID(GUID const&);
  174. LPENUMGUID penGUIDs = . . .; // Create an instance from wherever
  175. enum_sequence_t guids(penGUIDs, false); // Consume the reference
  176. std::for_each(guids.begin(), guids.end(), dump_GUID);
  177. \endcode
  178. *
  179. * \note The iterator instances returned by begin() and end() are valid outside
  180. * the lifetime of the collection instance from which they are obtained
  181. *
  182. * \remarks A detailed examination of the design and implementation of this
  183. * class template is described in Chapters 28 and 29 of
  184. * <a href="http://www.extendedstl.com/"><b>Extended STL, volume 1</b></a>
  185. * (published by Addison-Wesley, June 2007).
  186. *
  187. * \sa comstl::collection_sequence
  188. */
  189. template< ss_typename_param_k I /* Interface */
  190. , ss_typename_param_k V /* Value type */
  191. , ss_typename_param_k VP /* Value policy type */
  192. , ss_typename_param_k R = V const& /* Reference type */
  193. , ss_typename_param_k CP = cloneable_cloning_policy<I> /* Cloning policy type */
  194. , cs_size_t Q = 10 /* Quanta */
  195. >
  196. class enumerator_sequence
  197. : public stlsoft_ns_qual(stl_collection_tag)
  198. {
  199. /// \name Member Types
  200. /// @{
  201. public:
  202. /// \brief Interface type
  203. typedef I interface_type;
  204. /// \brief Value type
  205. typedef V value_type;
  206. /// \brief Value policy type
  207. typedef value_policy_adaptor<VP> value_policy_type;
  208. /// \brief Reference type
  209. typedef R reference;
  210. #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
  211. typedef R reference_type; // For backwards compatiblity
  212. #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
  213. /// \brief The mutating (non-const) pointer type
  214. #if defined(STLSOFT_META_HAS_SELECT_FIRST_TYPE_IF) && \
  215. !defined(STLSOFT_COMPILER_IS_BORLAND)
  216. typedef ss_typename_type_k stlsoft_ns_qual(select_first_type_if)< value_type const*
  217. , value_type*
  218. , stlsoft_ns_qual(base_type_traits)<R>::is_const
  219. >::type pointer;
  220. #else /* ? STLSOFT_META_HAS_SELECT_FIRST_TYPE_IF */
  221. typedef value_type* pointer;
  222. #endif /* !STLSOFT_META_HAS_SELECT_FIRST_TYPE_IF */
  223. /// \brief The non-mutating (const) pointer type
  224. typedef value_type const* const_pointer;
  225. /// \brief Cloning policy type
  226. typedef CP cloning_policy_type;
  227. /// \brief Iterator tag type
  228. typedef ss_typename_type_k cloning_policy_type::iterator_tag_type iterator_tag_type;
  229. #ifdef STLSOFT_COMPILER_IS_BORLAND
  230. # define retrievalQuanta Q
  231. #else /* ? compiler */
  232. /// \brief Retrieval quanta
  233. enum { retrievalQuanta = Q };
  234. #endif /* compiler */
  235. /// \brief Type of the current parameterisation
  236. typedef enumerator_sequence<I, V, VP, R, CP, Q> class_type;
  237. /// \brief Type of the current parameterisation
  238. typedef class_type sequence_type;
  239. /// \brief The size type
  240. typedef cs_size_t size_type;
  241. /// \brief The difference type
  242. typedef cs_ptrdiff_t difference_type;
  243. /// \brief The Boolean type
  244. typedef cs_bool_t bool_type;
  245. /// @}
  246. public:
  247. /// \brief Conversion constructor
  248. ///
  249. /// \param i The enumeration interface pointer to adapt
  250. /// \param bAddRef Causes a reference to be added if \c true, otherwise the sequence is deemed to <i>sink</i>, or consume, the interface pointer
  251. /// \param quanta The actual quanta required for this instance. Must be <= Q. Defaults to 0, which causes it to be Q
  252. /// \param bReset Determines whether the Reset() method is invoked on the enumerator instance upon construction. Defaults to true
  253. ///
  254. /// \note This does not throw an exception, so it is safe to be used to "eat" the
  255. /// reference. The only possible exception to this is if COMSTL_ASSERT() or COMSTL_MESSAGE_ASSERT(), which are
  256. /// used to validate that the given quanta size is within the limit specified in
  257. /// the specialisation, has been redefined to throw an exception. But since
  258. /// precondition violations are no more recoverable than any others (see the article
  259. /// "The Nuclear Reactor and the Deep Space Probe"), this does not represent
  260. /// a concerning contradiction to the no-throw status of the constructor.
  261. enumerator_sequence(interface_type* i, bool_type bAddRef, size_type quanta = 0, bool_type bReset = true)
  262. : m_root(i)
  263. , m_enumerator(NULL)
  264. , m_quanta(validate_quanta_(quanta))
  265. , m_bFirst(true)
  266. {
  267. COMSTL_MESSAGE_ASSERT("Precondition violation: interface cannot be NULL!", NULL != i);
  268. if(bAddRef)
  269. {
  270. m_root->AddRef();
  271. }
  272. if(bReset)
  273. {
  274. m_root->Reset();
  275. }
  276. // Here we instantiate m_enumerator
  277. //
  278. // If noncloneable, then just AddRef()
  279. // Otherwise Clone() and fail
  280. //
  281. // At this point, m_enumerator will be non-NULL, and
  282. // can be used in all invocations of begin(), or it
  283. // will be NULL, in which case the 2nd or subsequent
  284. // invocations of begin() must be directed to throw.
  285. m_enumerator = cloning_policy_type::get_working_instance(m_root);
  286. if(NULL != m_enumerator)
  287. {
  288. m_bFirst = false;
  289. }
  290. COMSTL_ASSERT(is_valid());
  291. }
  292. /// \brief Releases the adapted interface pointer
  293. ~enumerator_sequence() stlsoft_throw_0()
  294. {
  295. COMSTL_ASSERT(is_valid());
  296. m_root->Release();
  297. if(NULL != m_enumerator)
  298. {
  299. m_enumerator->Release();
  300. }
  301. }
  302. /// \name Iteration
  303. /// @{
  304. public:
  305. /// \brief Iterator for the enumerator_sequence class
  306. class iterator
  307. : public stlsoft_ns_qual(iterator_base)<iterator_tag_type
  308. , value_type
  309. , difference_type
  310. , pointer
  311. , reference
  312. >
  313. {
  314. public:
  315. typedef iterator class_type;
  316. #if defined(STLSOFT_COMPILER_IS_GCC)
  317. typedef ss_typename_type_k sequence_type::cloning_policy_type cloning_policy_type;
  318. typedef ss_typename_type_k sequence_type::value_type value_type;
  319. #endif /* compiler */
  320. private:
  321. struct enumeration_context
  322. {
  323. /// \name Member Types
  324. /// @{
  325. public:
  326. typedef enumeration_context class_type;
  327. typedef V value_type;
  328. typedef CP cloning_policy_type;
  329. /// @}
  330. /// \name Construction
  331. /// @{
  332. private:
  333. /// \brief Copying constructor
  334. ///
  335. /// This constructor copies the state of rhs, and is given a new
  336. /// cloned enumerator instance pointer.
  337. enumeration_context(interface_type* i, class_type const& rhs)
  338. : m_enumerator(i)
  339. , m_acquired(rhs.m_acquired)
  340. , m_current(rhs.m_current)
  341. , m_quanta(rhs.m_quanta)
  342. , m_refCount(1)
  343. , m_previousBlockTotal(rhs.m_previousBlockTotal)
  344. {
  345. COMSTL_ASSERT(rhs.m_acquired <= m_quanta);
  346. // Initialise all elements first, so that if a copy() throws an exception
  347. // all is cleared up simply.
  348. init_elements_(m_quanta);
  349. #ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  350. try
  351. #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  352. {
  353. value_type* begin = &m_values[0];
  354. value_type* end = &m_values[0] + m_quanta;
  355. value_type const* src_begin = &rhs.m_values[0];
  356. value_type const* src_end = &rhs.m_values[0] + rhs.m_acquired;
  357. // Copy each element up to the common extent ...
  358. for(; src_begin != src_end; ++begin, ++src_begin)
  359. {
  360. value_policy_type::copy(begin, src_begin);
  361. }
  362. COMSTL_ASSERT(begin <= end);
  363. STLSOFT_SUPPRESS_UNUSED(end);
  364. }
  365. #ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  366. catch(...)
  367. {
  368. // Must clear everything up here, since the enumeration_context will
  369. // not be destroyed (because it is not fully constructed).
  370. clear_elements_();
  371. throw;
  372. }
  373. #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  374. COMSTL_ASSERT(is_valid());
  375. COMSTL_ASSERT(this->index() == rhs.index());
  376. }
  377. public:
  378. /// \brief Sharing constructor
  379. ///
  380. /// The iterator is
  381. enumeration_context(interface_type* i, size_type quanta, bool_type bFirst)
  382. : m_enumerator(bFirst ? (i->AddRef(), i) : cloning_policy_type::share(i))
  383. , m_acquired(0)
  384. , m_current(0)
  385. , m_quanta(static_cast<ULONG>(quanta))
  386. , m_refCount(1)
  387. , m_previousBlockTotal(0)
  388. {
  389. COMSTL_ASSERT(quanta <= STLSOFT_NUM_ELEMENTS(m_values));
  390. init_elements_(m_quanta);
  391. // Note: We don't add a reference here, because share() increments the reference count.
  392. acquire_next_();
  393. COMSTL_ASSERT(is_valid());
  394. }
  395. ~enumeration_context() stlsoft_throw_0()
  396. {
  397. ++m_refCount;
  398. COMSTL_ASSERT(is_valid());
  399. --m_refCount;
  400. clear_elements_();
  401. if(NULL != m_enumerator)
  402. {
  403. m_enumerator->Release();
  404. }
  405. }
  406. void AddRef()
  407. {
  408. ++m_refCount;
  409. }
  410. void Release()
  411. {
  412. if(0 == --m_refCount)
  413. {
  414. delete this;
  415. }
  416. }
  417. static class_type* make_clone(class_type* ctxt)
  418. {
  419. if(NULL == ctxt)
  420. {
  421. return NULL;
  422. }
  423. else
  424. {
  425. COMSTL_ASSERT(NULL != ctxt->m_enumerator); // Must always have one, so can test its cloneability
  426. interface_type* copy;
  427. const bool bTrueClone = cloning_policy_type::clone(ctxt->m_enumerator, &copy);
  428. if(!bTrueClone)
  429. {
  430. COMSTL_ASSERT(NULL == copy);
  431. // Either forward_cloning_policy/input_cloning_policy that failed, or input_cloning_policy
  432. //
  433. // No reference will have been taken on m_ctxt->m_enumerator
  434. //
  435. // Just add ref on context, and return
  436. ctxt->AddRef();
  437. return ctxt;
  438. }
  439. else
  440. {
  441. COMSTL_ASSERT(NULL != copy);
  442. // Either forward_cloning_policy that succeeded, or degenerate_cloning_policy that succeeded
  443. //
  444. //
  445. class_type* newCtxt;
  446. #ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  447. try
  448. #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  449. {
  450. newCtxt = new class_type(copy, *ctxt);
  451. if(NULL == newCtxt)
  452. {
  453. copy->Release();
  454. }
  455. }
  456. #ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  457. catch(...)
  458. {
  459. copy->Release();
  460. throw;
  461. }
  462. #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  463. return newCtxt;
  464. }
  465. }
  466. }
  467. /// @}
  468. /// \name Iteration
  469. /// @{
  470. public:
  471. void advance() stlsoft_throw_0()
  472. {
  473. COMSTL_ASSERT(NULL != m_enumerator);
  474. // Four possibilities here:
  475. //
  476. // 1. Called when in an invalid state. This is determined by:
  477. // -
  478. // -
  479. // -
  480. // 2. next iteration point is within the number acquired
  481. // 3. need to acquire more elements from IEnumXXXX::Next()
  482. // 1.
  483. COMSTL_MESSAGE_ASSERT("Attempting to increment an invalid iterator: m_refCount < 1", 0 < m_refCount);
  484. COMSTL_MESSAGE_ASSERT("Attempting to increment an invalid iterator: 0 == m_acquired", 0 != m_acquired);
  485. COMSTL_MESSAGE_ASSERT("Attempting to increment an invalid iterator: m_current >= m_acquired", m_current < m_acquired);
  486. COMSTL_MESSAGE_ASSERT("Attempting to increment an invalid iterator: m_acquired > m_quanta", m_acquired <= m_quanta);
  487. COMSTL_MESSAGE_ASSERT("Attempting to increment an invalid iterator: m_quanta > dimensionof(m_values)", m_quanta <= STLSOFT_NUM_ELEMENTS(m_values));
  488. if(++m_current < m_acquired)
  489. {
  490. // 2.
  491. // Do nothing
  492. }
  493. else
  494. {
  495. COMSTL_MESSAGE_ASSERT("Attempting to increment an invalid iterator", NULL != m_enumerator);
  496. clear_elements_();
  497. // Reset enumeration
  498. m_current = 0;
  499. acquire_next_();
  500. }
  501. }
  502. value_type &current() stlsoft_throw_0()
  503. {
  504. COMSTL_ASSERT(!empty());
  505. return m_values[m_current];
  506. }
  507. size_type index() const stlsoft_throw_0()
  508. {
  509. return m_previousBlockTotal + m_current;
  510. }
  511. bool empty() const stlsoft_throw_0()
  512. {
  513. return 0 == m_acquired /* && NULL == m_enumerator */;
  514. }
  515. /// @}
  516. /// \name Invariant
  517. /// @{
  518. public:
  519. bool_type is_valid() const
  520. {
  521. if(m_refCount < 1)
  522. {
  523. #ifdef STLSOFT_UNITTEST
  524. fprintf(err, "invalid reference count (%ld) \n", m_refCount);
  525. #endif /* STLSOFT_UNITTEST */
  526. return false;
  527. }
  528. if( NULL == m_enumerator &&
  529. 0 == m_quanta)
  530. {
  531. if(0 != m_acquired)
  532. {
  533. #ifdef STLSOFT_UNITTEST
  534. fprintf(err, "m_acquired == %lu when m_quanta == 0\n", m_acquired);
  535. #endif /* STLSOFT_UNITTEST */
  536. return false;
  537. }
  538. if(0 != m_current)
  539. {
  540. #ifdef STLSOFT_UNITTEST
  541. fprintf(err, "m_current == %lu when m_quanta == 0\n", m_current);
  542. #endif /* STLSOFT_UNITTEST */
  543. return false;
  544. }
  545. if(0 != m_quanta)
  546. {
  547. return false;
  548. }
  549. }
  550. else
  551. {
  552. if(m_acquired < m_current)
  553. {
  554. #ifdef STLSOFT_UNITTEST
  555. fprintf(err, "m_acquired (%lu) not less than m_current (%lu)\n", m_acquired, m_current);
  556. #endif /* STLSOFT_UNITTEST */
  557. return false;
  558. }
  559. if(m_quanta < m_current)
  560. {
  561. #ifdef STLSOFT_UNITTEST
  562. fprintf(err, "m_quanta (%lu) not less than m_current (%lu)\n", m_quanta, m_current);
  563. #endif /* STLSOFT_UNITTEST */
  564. return false;
  565. }
  566. if(m_quanta < m_acquired)
  567. {
  568. #ifdef STLSOFT_UNITTEST
  569. fprintf(err, "m_quanta (%lu) not less than m_acquired (%lu)\n", m_quanta, m_acquired);
  570. #endif /* STLSOFT_UNITTEST */
  571. return false;
  572. }
  573. }
  574. return true;
  575. }
  576. /// @}
  577. /// \name Implementation
  578. /// @{
  579. private:
  580. void acquire_next_() stlsoft_throw_0()
  581. {
  582. // COMSTL_ASSERT(0 == m_acquired);
  583. COMSTL_ASSERT(0 == m_current);
  584. ULONG cFetched = 0;
  585. m_enumerator->Next(m_quanta, &m_values[0], &cFetched);
  586. m_acquired = cFetched;
  587. m_previousBlockTotal += cFetched;
  588. // We no longer checked for a FAILED(hr), since some enumerators
  589. // return invalid results. We rely on cFetched, which is the
  590. // only reliable guide when marshalling anyway
  591. }
  592. void clear_elements_() stlsoft_throw_0()
  593. {
  594. COMSTL_ASSERT(m_acquired <= STLSOFT_NUM_ELEMENTS(m_values));
  595. typedef ss_typename_type_k value_policy_type::clear_element clear_t;
  596. comstl_ns_qual_std(for_each)(&m_values[0], &m_values[0] + m_acquired, clear_t());
  597. }
  598. void init_elements_(size_type n) stlsoft_throw_0()
  599. {
  600. COMSTL_ASSERT(n <= STLSOFT_NUM_ELEMENTS(m_values));
  601. typedef ss_typename_type_k value_policy_type::init_element init_t;
  602. comstl_ns_qual_std(for_each)(&m_values[0], &m_values[0] + n, init_t());
  603. }
  604. /// @}
  605. /// \name Members
  606. /// @{
  607. private:
  608. interface_type* m_enumerator;
  609. size_type m_acquired;
  610. size_type m_current;
  611. ULONG const m_quanta;
  612. value_type m_values[retrievalQuanta];
  613. long m_refCount;
  614. size_type m_previousBlockTotal;
  615. /// @}
  616. // Not to be implemented
  617. private:
  618. enumeration_context(class_type const&);
  619. class_type& operator =(class_type const&);
  620. };
  621. /// \name Construction
  622. /// @{
  623. private:
  624. friend class enumerator_sequence<I, V, VP, R, CP, Q>;
  625. /// \brief Constructor
  626. iterator(interface_type* i, size_type quanta, bool_type &bFirst)
  627. : m_ctxt(new enumeration_context(i, quanta, bFirst))
  628. {
  629. bFirst = false;
  630. COMSTL_ASSERT(is_valid());
  631. }
  632. public:
  633. /// \brief Default constructor
  634. iterator()
  635. : m_ctxt(NULL)
  636. {
  637. COMSTL_ASSERT(is_valid());
  638. }
  639. /// \brief Copy constructor
  640. iterator(class_type const& rhs)
  641. : m_ctxt(enumeration_context::make_clone(rhs.m_ctxt))
  642. {
  643. COMSTL_ASSERT(is_valid());
  644. }
  645. /// \brief Releases any internal storage
  646. ~iterator() stlsoft_throw_0()
  647. {
  648. COMSTL_ASSERT(is_valid());
  649. if(NULL != m_ctxt)
  650. {
  651. m_ctxt->Release();
  652. }
  653. }
  654. class_type& operator =(class_type const& rhs)
  655. {
  656. enumeration_context *newCtxt = enumeration_context::make_clone(rhs.m_ctxt);
  657. if(NULL != m_ctxt)
  658. {
  659. m_ctxt->Release();
  660. }
  661. m_ctxt = newCtxt;
  662. return *this;
  663. }
  664. /// @}
  665. /// \name Forward Iterator Methods
  666. /// @{
  667. public:
  668. /// \brief Pre-increment operator
  669. class_type& operator ++()
  670. {
  671. COMSTL_ASSERT(is_valid());
  672. m_ctxt->advance();
  673. COMSTL_ASSERT(is_valid());
  674. return *this;
  675. }
  676. /// \brief Post-increment operator
  677. class_type operator ++(int)
  678. {
  679. COMSTL_ASSERT(is_valid());
  680. class_type r(*this);
  681. operator ++();
  682. COMSTL_ASSERT(is_valid());
  683. return r;
  684. }
  685. /// \brief Returns the value represented by the current iteration position
  686. reference operator *()
  687. {
  688. COMSTL_ASSERT(is_valid());
  689. COMSTL_MESSAGE_ASSERT("Attempting to dereference an invalid iterator", (NULL != m_ctxt && !m_ctxt->empty()));
  690. return m_ctxt->current();
  691. }
  692. /// \brief Returns the value represented by the current iteration position
  693. pointer operator ->()
  694. {
  695. COMSTL_ASSERT(is_valid());
  696. COMSTL_MESSAGE_ASSERT("Attempting to dereference an invalid iterator", (NULL != m_ctxt && !m_ctxt->empty()));
  697. return &m_ctxt->current();
  698. }
  699. private:
  700. static bool_type equal_(class_type const& lhs, class_type const& rhs, stlsoft_ns_qual_std(input_iterator_tag))
  701. {
  702. // The only valid comparison is when they both represent the end values.
  703. return lhs.is_end_point() && rhs.is_end_point();
  704. }
  705. static bool_type equal_(class_type const& lhs, class_type const& rhs, stlsoft_ns_qual_std(forward_iterator_tag))
  706. {
  707. // The iterators can be equal under two conditions:
  708. //
  709. // 1. The end iterator (as in the case for input iterators)
  710. // 2. Both have a context, and the index of both contexts are the same
  711. //
  712. // otherwise:
  713. //
  714. // 3. They're not equal
  715. if(lhs.is_end_point())
  716. {
  717. return rhs.is_end_point(); // 1 or 3
  718. }
  719. else
  720. {
  721. if(rhs.is_end_point())
  722. {
  723. return false; // 3
  724. }
  725. else
  726. {
  727. COMSTL_ASSERT(NULL != lhs.m_ctxt);
  728. COMSTL_ASSERT(NULL != rhs.m_ctxt);
  729. return lhs.m_ctxt->index() == rhs.m_ctxt->index(); // 2 or 3
  730. }
  731. }
  732. }
  733. public:
  734. /// \brief Evaluates whether \c this and \c rhs are equivalent
  735. bool_type equal(class_type const& rhs) const
  736. {
  737. COMSTL_ASSERT(is_valid());
  738. return class_type::equal_(*this, rhs, iterator_tag_type());
  739. }
  740. /// \brief Evaluates whether \c this and \c rhs are equivalent
  741. bool_type operator == (class_type const& rhs) const
  742. {
  743. COMSTL_ASSERT(is_valid());
  744. return this->equal(rhs);
  745. }
  746. /// \brief Evaluates whether \c this and \c rhs are not equivalent
  747. bool_type operator != (class_type const& rhs) const
  748. {
  749. COMSTL_ASSERT(is_valid());
  750. return !this->equal(rhs);
  751. }
  752. /// @}
  753. /// \name Invariant
  754. /// @{
  755. private:
  756. bool_type is_valid() const
  757. {
  758. return (NULL == m_ctxt) || m_ctxt->is_valid();
  759. }
  760. /// @}
  761. /// \name Implementation
  762. /// @{
  763. private:
  764. bool_type is_end_point() const
  765. {
  766. return NULL == m_ctxt || m_ctxt->empty();
  767. }
  768. /// @}
  769. /// \name Members
  770. /// @{
  771. private:
  772. enumeration_context *m_ctxt;
  773. /// @}
  774. };
  775. /// \brief The non-mutating (const) iterator type
  776. typedef iterator const_iterator;
  777. public:
  778. /// \brief Begins the iteration
  779. ///
  780. /// \return An iterator representing the start of the sequence
  781. ///
  782. /// \note The first time this is called, the iterated range represented by [begin(), end())
  783. /// directly uses that of the enumerator interface pointer passed to the constructor. When
  784. /// specialised with cloneable_cloning_policy and forward_cloning_policy policies, all
  785. /// subsequent calls to begin() will use a cloned enumerator instance, retrieved via
  786. /// I::Clone(). If the enumerator instance is not cloneable, then begin() will throw an
  787. /// instance of clone_failure on all subsequent invocations.
  788. iterator begin() const
  789. {
  790. COMSTL_ASSERT(is_valid());
  791. interface_type* en = NULL;
  792. if(NULL != m_enumerator)
  793. {
  794. en = m_enumerator;
  795. }
  796. else
  797. {
  798. if(!m_bFirst)
  799. {
  800. #ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  801. STLSOFT_THROW_X(clone_failure(E_NOTIMPL));
  802. #else /* ? STLSOFT_CF_EXCEPTION_SUPPORT */
  803. return end();
  804. #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  805. }
  806. else
  807. {
  808. en = m_root;
  809. }
  810. }
  811. COMSTL_ASSERT(NULL != en);
  812. return iterator(en, m_quanta, m_bFirst);
  813. }
  814. /// \brief Ends the iteration
  815. ///
  816. /// \return An iterator representing the end of the sequence
  817. iterator end() const
  818. {
  819. COMSTL_ASSERT(is_valid());
  820. return iterator();
  821. }
  822. /// @}
  823. /// \name Attributes
  824. /// @{
  825. public:
  826. static size_type quanta()
  827. {
  828. return retrievalQuanta;
  829. }
  830. /// @}
  831. /// \name Invariant
  832. /// @{
  833. private:
  834. bool_type is_valid() const
  835. {
  836. if(NULL == m_root)
  837. {
  838. #ifdef STLSOFT_UNITTEST
  839. fprintf(err, "enumerator_sequence: m_root is NULL\n");
  840. COMSTL_ASSERT(0);
  841. #endif /* STLSOFT_UNITTEST */
  842. return false;
  843. }
  844. return true;
  845. }
  846. /// @}
  847. // Implementation
  848. private:
  849. static size_type validate_quanta_(size_type quanta)
  850. {
  851. COMSTL_MESSAGE_ASSERT("Cannot set a quantum that exceeds the value specified in the template specialisation", quanta <= retrievalQuanta); // Could have named these things better!
  852. if( 0 == quanta ||
  853. quanta > retrievalQuanta)
  854. {
  855. quanta = retrievalQuanta;
  856. }
  857. return quanta;
  858. }
  859. // Members
  860. private:
  861. interface_type* m_root;
  862. interface_type* m_enumerator;
  863. size_type const m_quanta;
  864. ss_mutable_k bool_type m_bFirst;
  865. // Not to be implemented
  866. private:
  867. enumerator_sequence(class_type const&);
  868. class_type const& operator =(class_type const&);
  869. };
  870. ////////////////////////////////////////////////////////////////////////////
  871. // Compiler compatibility
  872. #ifdef STLSOFT_COMPILER_IS_BORLAND
  873. # undef retrievalQuanta
  874. #endif /* compiler */
  875. ////////////////////////////////////////////////////////////////////////////
  876. // Unit-testing
  877. #ifdef STLSOFT_UNITTEST
  878. # include "./unittest/enumerator_sequence_unittest_.h"
  879. #endif /* STLSOFT_UNITTEST */
  880. /* ////////////////////////////////////////////////////////////////////// */
  881. #ifndef _COMSTL_NO_NAMESPACE
  882. # if defined(_STLSOFT_NO_NAMESPACE) || \
  883. defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
  884. } // namespace comstl
  885. # else
  886. } // namespace stlsoft::comstl_project
  887. } // namespace stlsoft
  888. # endif /* _STLSOFT_NO_NAMESPACE */
  889. #endif /* !_COMSTL_NO_NAMESPACE */
  890. /* ////////////////////////////////////////////////////////////////////// */
  891. #endif /* !COMSTL_INCL_COMSTL_COLLECTIONS_HPP_ENUMERATOR_SEQUENCE */
  892. /* ///////////////////////////// end of file //////////////////////////// */