// Copyright Catch2 Authors // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE.txt or copy at // https://www.boost.org/LICENSE_1_0.txt) // SPDX-License-Identifier: BSL-1.0 #ifndef CATCH_TEST_HELPERS_RANGE_TEST_HELPERS_HPP_INCLUDED #define CATCH_TEST_HELPERS_RANGE_TEST_HELPERS_HPP_INCLUDED #include #include #include #include #include namespace unrelated { template class needs_ADL_begin { std::vector m_elements; public: using iterator = typename std::vector::iterator; using const_iterator = typename std::vector::const_iterator; needs_ADL_begin( std::initializer_list init ): m_elements( init ) {} const_iterator Begin() const { return m_elements.begin(); } const_iterator End() const { return m_elements.end(); } friend const_iterator begin( needs_ADL_begin const& lhs ) { return lhs.Begin(); } friend const_iterator end( needs_ADL_begin const& rhs ) { return rhs.End(); } }; struct ADL_empty { bool Empty() const { return true; } friend bool empty( ADL_empty e ) { return e.Empty(); } }; struct ADL_size { size_t sz() const { return 12; } friend size_t size( ADL_size s ) { return s.sz(); } }; } // namespace unrelated #if defined( __clang__ ) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wunused-function" #endif template class has_different_begin_end_types { // Using std::vector leads to annoying issues when T is bool // so we just use list because the perf is not critical and ugh. std::list m_elements; // Different type for the "end" iterator struct iterator_end {}; // Fake-ish forward iterator that only compares to a different type class iterator { using underlying_iter = typename std::list::const_iterator; underlying_iter m_start; underlying_iter m_end; public: iterator( underlying_iter start, underlying_iter end ): m_start( start ), m_end( end ) {} using iterator_category = std::forward_iterator_tag; using difference_type = std::ptrdiff_t; using value_type = T; using const_reference = T const&; using pointer = T const*; friend bool operator==( iterator iter, iterator_end ) { return iter.m_start == iter.m_end; } friend bool operator==(iterator lhs, iterator rhs) { return lhs.m_start == rhs.m_start && lhs.m_end == rhs.m_end; } friend bool operator!=( iterator iter, iterator_end ) { return iter.m_start != iter.m_end; } friend bool operator!=( iterator lhs, iterator rhs ) { return !( lhs == rhs ); } iterator& operator++() { ++m_start; return *this; } iterator operator++( int ) { auto tmp( *this ); ++m_start; return tmp; } const_reference operator*() const { return *m_start; } pointer operator->() const { return m_start; } }; public: explicit has_different_begin_end_types( std::initializer_list init ): m_elements( init ) {} iterator begin() const { return { m_elements.begin(), m_elements.end() }; } iterator_end end() const { return {}; } }; #if defined( __clang__ ) # pragma clang diagnostic pop #endif template struct with_mocked_iterator_access { std::vector m_elements; // use plain arrays to have nicer printouts with CHECK(...) mutable std::unique_ptr m_derefed; // We want to check which elements were dereferenced when iterating, so // we can check whether iterator-using code traverses range correctly template class basic_iterator { template using constify_t = std::conditional_t, U>; constify_t* m_origin; size_t m_origin_idx; public: using iterator_category = std::forward_iterator_tag; using difference_type = std::ptrdiff_t; using value_type = constify_t; using const_reference = typename std::vector::const_reference; using reference = typename std::vector::reference; using pointer = typename std::vector::pointer; basic_iterator( constify_t* origin, std::size_t origin_idx ): m_origin{ origin }, m_origin_idx{ origin_idx } {} friend bool operator==( basic_iterator lhs, basic_iterator rhs ) { return lhs.m_origin == rhs.m_origin && lhs.m_origin_idx == rhs.m_origin_idx; } friend bool operator!=( basic_iterator lhs, basic_iterator rhs ) { return !( lhs == rhs ); } basic_iterator& operator++() { ++m_origin_idx; return *this; } basic_iterator operator++( int ) { auto tmp( *this ); ++( *this ); return tmp; } const_reference operator*() const { assert( m_origin_idx < m_origin->m_elements.size() && "Attempted to deref invalid position" ); m_origin->m_derefed[m_origin_idx] = true; return m_origin->m_elements[m_origin_idx]; } pointer operator->() const { assert( m_origin_idx < m_origin->m_elements.size() && "Attempted to deref invalid position" ); return &m_origin->m_elements[m_origin_idx]; } }; using iterator = basic_iterator; using const_iterator = basic_iterator; with_mocked_iterator_access( std::initializer_list init ): m_elements( init ), m_derefed( std::make_unique( m_elements.size() ) ) {} const_iterator begin() const { return { this, 0 }; } const_iterator end() const { return { this, m_elements.size() }; } iterator begin() { return { this, 0 }; } iterator end() { return { this, m_elements.size() }; } }; namespace Catch { // make sure with_mocked_iterator_access is not considered a range by Catch, // so that below StringMaker is used instead of the default one for ranges template struct is_range> : std::false_type {}; template struct StringMaker> { static std::string convert( with_mocked_iterator_access const& access ) { // We have to avoid the type's iterators, because we check // their use in tests return ::Catch::Detail::stringify( access.m_elements ); } }; } // namespace Catch #endif // CATCH_TEST_HELPERS_RANGE_TEST_HELPERS_HPP_INCLUDED