Compare commits

...

5 Commits

Author SHA1 Message Date
80bb1ac9e4 Avoid including <utility>, added a missing include. 2023-06-22 21:14:03 +03:00
d11499c08c Performance improvement: (#79)
Performance improvement: Add move semantics to by-value parameters (iterator and predicate), to eliminate the cost of copy-construction. Both the Predicate (Normally a std::function, or lambda), and custom iterators can have state, which can be expensive to copy. Profiler identified this as a bottleneck while using boost::adaptors::filtered.
2023-06-22 21:06:19 +03:00
6d02c36718 Add clang-16 CI jobs, switch to clang-15 from stock Ubuntu repos. 2023-06-05 03:17:56 +03:00
85b542e1b6 Extracted is_iterator trait to a separate header.
Also, reworked is_iterator to be more robust against various corner cases
and added dedicated tests for the type trait.
2023-05-21 15:18:54 +03:00
e8fbd92a61 Use BOOST_STATIC_ASSERT instead of BOOST_MPL_ASSERT (#78)
The MPL version is slower to compile and `BOOST_STATIC_ASSERT` is
already used in some places. So unify that.

This also fixes `Wzero-as-null-pointer-constant` warnings, see https://github.com/boostorg/mpl/pull/75
2023-05-07 15:23:16 +03:00
10 changed files with 373 additions and 75 deletions

View File

@ -236,10 +236,6 @@ jobs:
os: ubuntu-22.04
install:
- clang-15
sources:
- "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-15 main"
source_keys:
- "https://apt.llvm.org/llvm-snapshot.gpg.key"
- toolset: clang
compiler: clang++-15
cxxstd: "03,11,14,17,20,2b"
@ -248,15 +244,35 @@ jobs:
- clang-15
- libc++-15-dev
- libc++abi-15-dev
cxxflags: -stdlib=libc++
linkflags: -stdlib=libc++
- toolset: clang
compiler: clang++-16
cxxstd: "03,11,14,17,20,2b"
os: ubuntu-22.04
install:
- clang-16
sources:
- "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-15 main"
- "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-16 main"
source_keys:
- "https://apt.llvm.org/llvm-snapshot.gpg.key"
- toolset: clang
compiler: clang++-16
cxxstd: "03,11,14,17,20,2b"
os: ubuntu-22.04
install:
- clang-16
- libc++-16-dev
- libc++abi-16-dev
sources:
- "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-16 main"
source_keys:
- "https://apt.llvm.org/llvm-snapshot.gpg.key"
cxxflags: -stdlib=libc++
linkflags: -stdlib=libc++
- name: UBSAN
toolset: clang
compiler: clang++-14
compiler: clang++-15
cxxstd: "03,11,14,17,20,2b"
cxxflags: -stdlib=libc++
linkflags: -stdlib=libc++
@ -264,9 +280,9 @@ jobs:
build_variant: debug
os: ubuntu-22.04
install:
- clang-14
- libc++-14-dev
- libc++abi-14-dev
- clang-15
- libc++-15-dev
- libc++abi-15-dev
- toolset: clang
cxxstd: "03,11,14,17,2a"

View File

@ -13,15 +13,14 @@
# include <boost/mpl/if.hpp>
# include <boost/mpl/eval_if.hpp>
# include <boost/mpl/identity.hpp>
# include <boost/mpl/assert.hpp>
# include <boost/static_assert.hpp>
# include <boost/type_traits/is_same.hpp>
# include <boost/type_traits/is_const.hpp>
# include <boost/type_traits/is_reference.hpp>
# include <boost/type_traits/is_convertible.hpp>
# include <boost/type_traits/is_same.hpp>
# include <boost/iterator/detail/config_def.hpp> // try to keep this last
# ifdef BOOST_ITERATOR_REF_CONSTNESS_KILLS_WRITABILITY
@ -139,17 +138,17 @@ struct iterator_category_with_traversal
// Make sure this isn't used to build any categories where
// convertibility to Traversal is redundant. Should just use the
// Category element in that case.
BOOST_MPL_ASSERT_NOT((
is_convertible<
BOOST_STATIC_ASSERT((
!is_convertible<
typename iterator_category_to_traversal<Category>::type
, Traversal
>));
>::value));
BOOST_MPL_ASSERT((is_iterator_category<Category>));
BOOST_MPL_ASSERT_NOT((is_iterator_category<Traversal>));
BOOST_MPL_ASSERT_NOT((is_iterator_traversal<Category>));
BOOST_STATIC_ASSERT(is_iterator_category<Category>::value);
BOOST_STATIC_ASSERT(!is_iterator_category<Traversal>::value);
BOOST_STATIC_ASSERT(!is_iterator_traversal<Category>::value);
# if !BOOST_WORKAROUND(BOOST_MSVC, BOOST_TESTED_AT(1310))
BOOST_MPL_ASSERT((is_iterator_traversal<Traversal>));
BOOST_STATIC_ASSERT(is_iterator_traversal<Traversal>::value);
# endif
};
@ -158,7 +157,7 @@ struct iterator_category_with_traversal
template <class Traversal, class ValueParam, class Reference>
struct facade_iterator_category_impl
{
BOOST_MPL_ASSERT_NOT((is_iterator_category<Traversal>));
BOOST_STATIC_ASSERT(!is_iterator_category<Traversal>::value);
typedef typename iterator_facade_default_category<
Traversal,ValueParam,Reference

View File

@ -12,6 +12,14 @@
#include <boost/type_traits/is_class.hpp>
#include <boost/static_assert.hpp>
#include <boost/config.hpp>
#include <boost/config/workaround.hpp>
#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
#define BOOST_ITERATOR_DETAIL_MOVE(_type, _value) static_cast< _type&& >(_value)
#else
#define BOOST_ITERATOR_DETAIL_MOVE(_type, _value) _value
#endif
namespace boost {
namespace iterators {
@ -54,13 +62,13 @@ namespace iterators {
filter_iterator() { }
filter_iterator(Predicate f, Iterator x, Iterator end_ = Iterator())
: super_t(x), m_predicate(f), m_end(end_)
: super_t(BOOST_ITERATOR_DETAIL_MOVE(Iterator, x)), m_predicate(BOOST_ITERATOR_DETAIL_MOVE(Predicate, f)), m_end(BOOST_ITERATOR_DETAIL_MOVE(Iterator, end_))
{
satisfy_predicate();
}
filter_iterator(Iterator x, Iterator end_ = Iterator())
: super_t(x), m_predicate(), m_end(end_)
: super_t(BOOST_ITERATOR_DETAIL_MOVE(Iterator, x)), m_predicate(), m_end(BOOST_ITERATOR_DETAIL_MOVE(Iterator, end_))
{
// Pro8 is a little too aggressive about instantiating the
// body of this function.
@ -111,7 +119,7 @@ namespace iterators {
inline filter_iterator<Predicate,Iterator>
make_filter_iterator(Predicate f, Iterator x, Iterator end = Iterator())
{
return filter_iterator<Predicate,Iterator>(f,x,end);
return filter_iterator<Predicate,Iterator>(BOOST_ITERATOR_DETAIL_MOVE(Predicate, f), BOOST_ITERATOR_DETAIL_MOVE(Iterator, x), BOOST_ITERATOR_DETAIL_MOVE(Iterator, end));
}
template <class Predicate, class Iterator>
@ -123,7 +131,7 @@ namespace iterators {
>::type x
, Iterator end = Iterator())
{
return filter_iterator<Predicate,Iterator>(x,end);
return filter_iterator<Predicate,Iterator>(BOOST_ITERATOR_DETAIL_MOVE(Iterator, x), BOOST_ITERATOR_DETAIL_MOVE(Iterator, end));
}
} // namespace iterators
@ -133,4 +141,6 @@ using iterators::make_filter_iterator;
} // namespace boost
#undef BOOST_ITERATOR_DETAIL_MOVE
#endif // BOOST_FILTER_ITERATOR_23022003THW_HPP

View File

@ -0,0 +1,148 @@
/*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* https://www.boost.org/LICENSE_1_0.txt)
*
* Copyright (c) 2023 Andrey Semashev
*/
/*!
* \file iterator/is_iterator.hpp
*
* This header contains definition of the \c is_iterator type trait.
*/
#ifndef BOOST_ITERATOR_IS_ITERATOR_HPP_INCLUDED_
#define BOOST_ITERATOR_IS_ITERATOR_HPP_INCLUDED_
#include <cstddef>
#include <boost/config.hpp>
#include <boost/type_traits/integral_constant.hpp>
#include <boost/type_traits/negation.hpp>
#include <boost/type_traits/conjunction.hpp>
#include <boost/type_traits/is_complete.hpp>
#include <boost/type_traits/is_function.hpp>
#if !defined(BOOST_NO_CXX17_ITERATOR_TRAITS)
#include <iterator>
#endif
#ifdef BOOST_HAS_PRAGMA_ONCE
#pragma once
#endif
namespace boost {
namespace iterators {
namespace detail {
// The trait attempts to detect if the T type is an iterator class. Class-type iterators are assumed
// to have the nested type iterator_category. Strictly speaking, this is not required to be the
// case (e.g. a user can specialize iterator_traits for T without defining T::iterator_category).
// Still, this is a good heuristic in practice, and we can't do anything better anyway.
// Since C++17 we can test for iterator_traits<T>::iterator_category presence instead as it is
// required to be only present for iterators.
namespace has_iterator_category_detail {
typedef char yes_type;
struct no_type { char padding[2]; };
template< typename T >
yes_type check(
#if !defined(BOOST_NO_CXX17_ITERATOR_TRAITS)
typename std::iterator_traits< T >::iterator_category*
#else
typename T::iterator_category*
#endif
);
template< typename >
no_type check(...);
} // namespace has_iterator_category_detail
template< typename T >
struct is_iterator_impl :
public boost::integral_constant<
bool,
sizeof(has_iterator_category_detail::check< T >(0)) == sizeof(has_iterator_category_detail::yes_type)
>
{
};
template< typename T >
struct is_iterator_impl< T* > :
public boost::conjunction<
boost::is_complete< T >,
boost::negation< boost::is_function< T > >
>::type
{
};
template< typename T, typename U >
struct is_iterator_impl< T U::* > :
public boost::false_type
{
};
template< typename T >
struct is_iterator_impl< T& > :
public boost::false_type
{
};
template< typename T, std::size_t N >
struct is_iterator_impl< T[N] > :
public boost::false_type
{
};
#if !defined(BOOST_TT_HAS_WORKING_IS_COMPLETE)
template< typename T >
struct is_iterator_impl< T[] > :
public boost::false_type
{
};
template< >
struct is_iterator_impl< void > :
public boost::false_type
{
};
template< >
struct is_iterator_impl< void* > :
public boost::false_type
{
};
#endif // !defined(BOOST_TT_HAS_WORKING_IS_COMPLETE)
} // namespace detail
/*!
* \brief The type trait detects whether the type \c T is an iterator type.
*
* The type trait yields \c true if its argument type \c T, after stripping top level
* cv qualifiers, is one of the following:
*
* - A pointer type, other than a pointer to function, a pointer to a class member,
* or a pointer to an incomplete type, including `void`.
* - A class type for which an iterator category is obtainable. Prior to C++17,
* the iterator category must be defined as a public `T::iterator_category` type.
* Since C++17, the expression `std::iterator_traits< T >::iterator_category` must
* be valid and produce the iterator category type.
*
* Otherwise, the type trait yields \c false.
*/
template< typename T >
struct is_iterator : public detail::is_iterator_impl< T >::type {};
template< typename T >
struct is_iterator< const T > : public detail::is_iterator_impl< T >::type {};
template< typename T >
struct is_iterator< volatile T > : public detail::is_iterator_impl< T >::type {};
template< typename T >
struct is_iterator< const volatile T > : public detail::is_iterator_impl< T >::type {};
} // namespace iterators
using iterators::is_iterator;
} // namespace boost
#endif // BOOST_ITERATOR_IS_ITERATOR_HPP_INCLUDED_

View File

@ -144,8 +144,8 @@ namespace boost_concepts
{
typedef typename std::iterator_traits<Iterator>::difference_type difference_type;
BOOST_MPL_ASSERT((boost::is_integral<difference_type>));
BOOST_MPL_ASSERT_RELATION(std::numeric_limits<difference_type>::is_signed, ==, true);
BOOST_STATIC_ASSERT(boost::is_integral<difference_type>::value);
BOOST_STATIC_ASSERT(std::numeric_limits<difference_type>::is_signed);
BOOST_CONCEPT_ASSERT((
boost::Convertible<

View File

@ -15,13 +15,12 @@
#ifndef BOOST_NEXT_PRIOR_HPP_INCLUDED
#define BOOST_NEXT_PRIOR_HPP_INCLUDED
#include <iterator>
#include <boost/config.hpp>
#include <boost/core/enable_if.hpp>
#include <boost/type_traits/has_plus.hpp>
#include <boost/type_traits/has_plus_assign.hpp>
#include <boost/type_traits/has_minus.hpp>
#include <boost/type_traits/has_minus_assign.hpp>
#include <boost/iterator/is_iterator.hpp>
#include <boost/iterator/advance.hpp>
#include <boost/iterator/reverse_iterator.hpp>
@ -39,46 +38,6 @@ namespace boost {
namespace next_prior_detail {
// The trait attempts to detect if the T type is an iterator. Class-type iterators are assumed
// to have the nested type iterator_category. Strictly speaking, this is not required to be the
// case (e.g. a user can specialize iterator_traits for T without defining T::iterator_category).
// Still, this is a good heuristic in practice, and we can't do anything better anyway.
// Since C++17 we can test for iterator_traits<T>::iterator_category presence instead as it is
// required to be only present for iterators.
template< typename T, typename Void = void >
struct is_iterator_class
{
static BOOST_CONSTEXPR_OR_CONST bool value = false;
};
template< typename T >
struct is_iterator_class<
T,
typename enable_if_has_type<
#if !defined(BOOST_NO_CXX17_ITERATOR_TRAITS)
typename std::iterator_traits< T >::iterator_category
#else
typename T::iterator_category
#endif
>::type
>
{
static BOOST_CONSTEXPR_OR_CONST bool value = true;
};
template< typename T >
struct is_iterator :
public is_iterator_class< T >
{
};
template< typename T >
struct is_iterator< T* >
{
static BOOST_CONSTEXPR_OR_CONST bool value = true;
};
template< typename T, typename Distance, bool HasPlus = has_plus< T, Distance >::value >
struct next_plus_impl;
@ -107,7 +66,7 @@ struct next_plus_assign_impl< T, Distance, true >
}
};
template< typename T, typename Distance, bool IsIterator = is_iterator< T >::value >
template< typename T, typename Distance, bool IsIterator = boost::iterators::is_iterator< T >::value >
struct next_advance_impl :
public next_plus_assign_impl< T, Distance >
{
@ -152,7 +111,7 @@ struct prior_minus_assign_impl< T, Distance, true >
}
};
template< typename T, typename Distance, bool IsIterator = is_iterator< T >::value >
template< typename T, typename Distance, bool IsIterator = boost::iterators::is_iterator< T >::value >
struct prior_advance_impl :
public prior_minus_assign_impl< T, Distance >
{

View File

@ -26,6 +26,8 @@ test-suite iterator
[ run zip_iterator_test_std_tuple.cpp ]
[ run zip_iterator_test_std_pair.cpp ]
[ run is_iterator.cpp ]
# These tests should work for just about everything.
[ compile is_lvalue_iterator.cpp ]
[ compile is_readable_iterator.cpp ]

164
test/is_iterator.cpp Normal file
View File

@ -0,0 +1,164 @@
/*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* https://www.boost.org/LICENSE_1_0.txt)
*
* Copyright (c) 2023 Andrey Semashev
*/
/*!
* \file is_iterator.cpp
*
* This header contains tests for the \c is_iterator type trait.
*/
#include <boost/iterator/is_iterator.hpp>
#include <cstddef>
#include <list>
#include <vector>
#include <string>
#include <iterator>
#include <boost/iterator/iterator_adaptor.hpp>
#include <boost/core/lightweight_test.hpp>
template< typename T >
struct value_iterator
{
typedef std::input_iterator_tag iterator_category;
typedef T value_type;
typedef std::ptrdiff_t difference_type;
typedef T* pointer;
typedef T& reference;
value_type operator*() const;
};
template< typename T >
struct proxy_iterator
{
typedef T value_type;
typedef std::output_iterator_tag iterator_category;
typedef std::ptrdiff_t difference_type;
typedef T* pointer;
typedef T& reference;
struct proxy
{
operator value_type&() const;
proxy& operator=(value_type) const;
};
proxy operator*() const;
};
template< typename T >
struct lvalue_iterator
{
typedef T value_type;
typedef T& reference;
typedef T difference_type;
typedef std::input_iterator_tag iterator_category;
typedef T* pointer;
T& operator*() const;
lvalue_iterator& operator++();
lvalue_iterator operator++(int);
};
template< typename T >
struct constant_lvalue_iterator
{
typedef T value_type;
typedef T const& reference;
typedef T difference_type;
typedef std::input_iterator_tag iterator_category;
typedef T const* pointer;
T const& operator*() const;
constant_lvalue_iterator& operator++();
constant_lvalue_iterator operator++(int);
};
template< typename Iterator >
class adapted_iterator :
public boost::iterators::iterator_adaptor< adapted_iterator< Iterator >, Iterator >
{
friend class iterator_core_access;
private:
typedef boost::iterators::iterator_adaptor< adapted_iterator< Iterator >, Iterator > base_type;
private:
typename base_type::reference dereference() const;
void increment();
void decrement();
void advance(typename base_type::difference_type n);
template< typename OtherIterator >
typename base_type::difference_type distance_to(adapted_iterator< OtherIterator > const& y) const;
};
struct complete {};
struct incomplete;
int main()
{
BOOST_TEST(boost::iterators::is_iterator< int* >::value);
BOOST_TEST(boost::iterators::is_iterator< const int* >::value);
BOOST_TEST(boost::iterators::is_iterator< complete* >::value);
BOOST_TEST(boost::iterators::is_iterator< std::reverse_iterator< int* > >::value);
BOOST_TEST(boost::iterators::is_iterator< std::reverse_iterator< complete* > >::value);
BOOST_TEST(boost::iterators::is_iterator< adapted_iterator< int* > >::value);
BOOST_TEST(boost::iterators::is_iterator< std::string::iterator >::value);
BOOST_TEST(boost::iterators::is_iterator< std::string::const_iterator >::value);
BOOST_TEST(boost::iterators::is_iterator< std::string::reverse_iterator >::value);
BOOST_TEST(boost::iterators::is_iterator< std::string::const_reverse_iterator >::value);
BOOST_TEST(boost::iterators::is_iterator< std::list< int >::iterator >::value);
BOOST_TEST(boost::iterators::is_iterator< std::list< int >::const_iterator >::value);
BOOST_TEST(boost::iterators::is_iterator< std::list< int >::reverse_iterator >::value);
BOOST_TEST(boost::iterators::is_iterator< std::list< int >::const_reverse_iterator >::value);
BOOST_TEST(boost::iterators::is_iterator< std::vector< int >::iterator >::value);
BOOST_TEST(boost::iterators::is_iterator< std::vector< int >::const_iterator >::value);
BOOST_TEST(boost::iterators::is_iterator< std::vector< int >::reverse_iterator >::value);
BOOST_TEST(boost::iterators::is_iterator< std::vector< int >::const_reverse_iterator >::value);
BOOST_TEST(boost::iterators::is_iterator< std::insert_iterator< std::vector< int > > >::value);
BOOST_TEST(boost::iterators::is_iterator< std::back_insert_iterator< std::vector< int > > >::value);
BOOST_TEST(boost::iterators::is_iterator< std::front_insert_iterator< std::vector< int > > >::value);
BOOST_TEST(boost::iterators::is_iterator< std::istream_iterator< int > >::value);
BOOST_TEST(boost::iterators::is_iterator< std::ostream_iterator< int > >::value);
BOOST_TEST(boost::iterators::is_iterator< std::istreambuf_iterator< char > >::value);
BOOST_TEST(boost::iterators::is_iterator< std::ostreambuf_iterator< char > >::value);
BOOST_TEST(!boost::iterators::is_iterator< int >::value);
BOOST_TEST(!boost::iterators::is_iterator< complete >::value);
BOOST_TEST(!boost::iterators::is_iterator< void >::value);
BOOST_TEST(!boost::iterators::is_iterator< const void >::value);
BOOST_TEST(!boost::iterators::is_iterator< void* >::value);
#if defined(BOOST_TT_HAS_WORKING_IS_COMPLETE)
BOOST_TEST(!boost::iterators::is_iterator< incomplete >::value);
BOOST_TEST(!boost::iterators::is_iterator< incomplete* >::value);
#endif
BOOST_TEST(!boost::iterators::is_iterator< int (int) >::value);
BOOST_TEST(!boost::iterators::is_iterator< int (*)(int) >::value);
BOOST_TEST(!boost::iterators::is_iterator< int complete::* >::value);
BOOST_TEST(!boost::iterators::is_iterator< int (complete::*)(int) >::value);
BOOST_TEST(!boost::iterators::is_iterator< int (complete::*)(int) const >::value);
#if defined(__cpp_noexcept_function_type) && (__cpp_noexcept_function_type >= 201510L)
BOOST_TEST(!boost::iterators::is_iterator< int (*)(int) noexcept >::value);
BOOST_TEST(!boost::iterators::is_iterator< int (complete::*)(int) noexcept >::value);
BOOST_TEST(!boost::iterators::is_iterator< int (complete::*)(int) const noexcept >::value);
#endif
BOOST_TEST(!boost::iterators::is_iterator< int[] >::value);
BOOST_TEST(!boost::iterators::is_iterator< int[10] >::value);
BOOST_TEST(!boost::iterators::is_iterator< int*[] >::value);
BOOST_TEST(!boost::iterators::is_iterator< int*[10] >::value);
BOOST_TEST(!boost::iterators::is_iterator< int& >::value);
BOOST_TEST(!boost::iterators::is_iterator< int*& >::value);
BOOST_TEST(!boost::iterators::is_iterator< int (&)(int) >::value);
BOOST_TEST(!boost::iterators::is_iterator< int (&)[10] >::value);
return boost::report_errors();
}

View File

@ -147,7 +147,7 @@ struct iterator_with_proxy_reference
template <class T, class U>
void same_type(U const&)
{ BOOST_MPL_ASSERT((boost::is_same<T,U>)); }
{ BOOST_STATIC_ASSERT((boost::is_same<T,U>::value)); }
template <class I, class A>
struct abstract_iterator

View File

@ -5,15 +5,15 @@
#ifndef STATIC_ASSERT_SAME_DWA2003530_HPP
# define STATIC_ASSERT_SAME_DWA2003530_HPP
#include <boost/mpl/assert.hpp>
# include <boost/type_traits/is_same.hpp>
#include <boost/static_assert.hpp>
#include <boost/type_traits/is_same.hpp>
#define STATIC_ASSERT_SAME( T1,T2 ) BOOST_MPL_ASSERT((::boost::is_same< T1, T2 >))
#define STATIC_ASSERT_SAME( T1,T2 ) BOOST_STATIC_ASSERT((::boost::is_same< T1, T2 >::value))
template <class T1, class T2>
struct static_assert_same
{
BOOST_MPL_ASSERT((::boost::is_same< T1, T2 >));
STATIC_ASSERT_SAME(T1, T2);
enum { value = 1 };
};