Skip to content

Commit

Permalink
refs #121 Инструменты для приведения разнотипных диапазонов к общему …
Browse files Browse the repository at this point in the history
…типу
  • Loading branch information
izvolov committed Jul 24, 2020
1 parent 03b3fc6 commit c969f30
Show file tree
Hide file tree
Showing 9 changed files with 257 additions and 0 deletions.
68 changes: 68 additions & 0 deletions include/burst/iterator/detail/uniform_range_tuple_please.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#ifndef BURST_ITERATOR_DETAIL_UNIFORM_RANGE_TUPLE_PLEASE_HPP
#define BURST_ITERATOR_DETAIL_UNIFORM_RANGE_TUPLE_PLEASE_HPP

#include <burst/range/to_any_range.hpp>
#include <burst/tuple/by_all.hpp>
#include <burst/type_traits/are_same.hpp>
#include <burst/type_traits/minimum_category.hpp>
#include <burst/type_traits/range_category.hpp>

#include <boost/iterator/iterator_categories.hpp>

#include <tuple>
#include <type_traits>

namespace burst
{
namespace detail
{
/*!
\brief
Перегрузка для случая, когда все переданные диапазоны имеют один и тот же тип
\see uniform_range_tuple_please
*/
template <typename... Ranges>
auto uniform_range_tuple_please_impl (std::tuple<Ranges &...> ranges, std::true_type)
{
static_assert(are_same_v<Ranges...>, "");
return ranges;
}

/*!
\brief
Перегрузка для случая, когда переданные диапазоны разнотипны
\see uniform_range_tuple_please
*/
template <typename... Ranges>
auto uniform_range_tuple_please_impl (std::tuple<Ranges &...> ranges, std::false_type)
{
static_assert(not are_same_v<Ranges...>, "");
using iterator_category = minimum_category_t<range_category_t<Ranges>...>;
using boost_traversal =
typename boost::iterator_category_to_traversal<iterator_category>::type;
return by_all(to_any_range<boost_traversal>, ranges);
}

/*!
\brief
Сделать из кортежа возможно разнотипных диапазонов кортеж заведомо однотипных
диапазонов
\returns
Если диапазоны изначально однотипны, то возвращается изначальный кортеж, в
противном случае создаётся кортеж, в котором каждый из диапазонов приведён к
boost::any_range.
\see are_same
*/
template <typename... Ranges>
auto uniform_range_tuple_please (std::tuple<Ranges &...> ranges)
{
return uniform_range_tuple_please_impl(ranges, are_same<Ranges...>{});
}
}
}

#endif // BURST_ITERATOR_DETAIL_UNIFORM_RANGE_TUPLE_PLEASE_HPP
47 changes: 47 additions & 0 deletions include/burst/range/to_any_range.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#ifndef BURST_RANGE_TO_ANY_RANGE_HPP
#define BURST_RANGE_TO_ANY_RANGE_HPP

#include <burst/type_traits/range_reference.hpp>
#include <burst/type_traits/range_value.hpp>

#include <boost/range/any_range.hpp>

namespace burst
{
/*!
\brief
Стирает настоящий тип диапазона
\details
Создаёт диапазон, абстрагированный от типа контейнера. Полученный диапазон имеет те же
самые характеристики, что и исходный диапазон, но имеет некий "универсальный" тип, в
котором имеет значение только категория итератора, его значение и т.п.
\tparam Traversal
\parblock
Способ обхода итератора. Нечто типа бустового эквивалента категории итератора.
Его можно получить из обычной std-категории с помощью специальной метафункции:
\code{.cpp}
using boost_traversal =
typename boost::iterator_category_to_traversal<iterator_category>::type;
\endcode
\endparblock
*/
template <typename Traversal>
struct to_any_range_t
{
template <typename Range>
constexpr auto operator () (Range && range) const
{
using value_type = range_value_t<Range>;
using reference_type = range_reference_t<Range>;
return boost::any_range<value_type, Traversal, reference_type>(range);
}
};

template <typename Traversal>
constexpr auto to_any_range = to_any_range_t<Traversal>{};
} // namespace burst

#endif // BURST_RANGE_TO_ANY_RANGE_HPP
24 changes: 24 additions & 0 deletions include/burst/type_traits/are_same.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#ifndef BURST_TYPE_TRAITS_ARE_SAME_HPP
#define BURST_TYPE_TRAITS_ARE_SAME_HPP

#include <burst/type_traits/type_list.hpp>

#include <type_traits>

namespace burst
{
/*!
\brief
Проверяет "одинаковость" типов
\returns
Истину, когда все переданные типы одинаковы, и ложь в противном случае.
*/
template <typename T, typename... Ts>
struct are_same: std::is_same<type_list<T, Ts...>, type_list<Ts..., T>> {};

template <typename... Ts>
constexpr const auto are_same_v = are_same<Ts...>::value;
} // namespace burst

#endif // BURST_TYPE_TRAITS_ARE_SAME_HPP
42 changes: 42 additions & 0 deletions include/burst/type_traits/minimum_category.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#ifndef BURST_TYPE_TRAITS_MINIMUM_CATEGORY_HPP
#define BURST_TYPE_TRAITS_MINIMUM_CATEGORY_HPP

#include <iterator>
#include <type_traits>

namespace burst
{
/*!
\brief
Вычисляет меньшую категорию итератора из заданных
\details
Категории стандартной библиотеки по возрастанию:
1. input_iterator_tag
2. forward_iterator_tag
3. bidirectional_iterator_tag
4. random_access_iterator_tag
5. contiguous_iterator_tag [C++20]
*/
template <typename... Categories>
struct minimum_category;

template <typename... Categories>
using minimum_category_t = typename minimum_category<Categories...>::type;

template <typename Category, typename... Categories>
struct minimum_category<Category, Categories...>
{
static_assert(std::is_convertible<Category, std::input_iterator_tag>::value, "");
using type = std::common_type_t<Category, minimum_category_t<Categories...>>;
};

template <typename Category>
struct minimum_category<Category>
{
static_assert(std::is_convertible<Category, std::input_iterator_tag>::value, "");
using type = Category;
};
} // namespace burst

#endif // BURST_TYPE_TRAITS_MINIMUM_CATEGORY_HPP
12 changes: 12 additions & 0 deletions include/burst/type_traits/range_category.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#ifndef BURST_TYPE_TRAITS_RANGE_CATEGORY_HPP
#define BURST_TYPE_TRAITS_RANGE_CATEGORY_HPP

#include <boost/range/category.hpp>

namespace burst
{
template <typename Range>
using range_category_t = typename boost::range_category<Range>::type;
} // namespace burst

#endif // BURST_TYPE_TRAITS_RANGE_CATEGORY_HPP
10 changes: 10 additions & 0 deletions include/burst/type_traits/type_list.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#ifndef BURST_TYPE_TRAITS_TYPE_LIST_HPP
#define BURST_TYPE_TRAITS_TYPE_LIST_HPP

namespace burst
{
template <typename... Ts>
struct type_list {};
} // namespace burst

#endif // BURST_TYPE_TRAITS_TYPE_LIST_HPP
1 change: 1 addition & 0 deletions test/burst/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ add_subdirectory(integer)
add_subdirectory(iterator)
add_subdirectory(range)
add_subdirectory(tuple)
add_subdirectory(type_traits)
3 changes: 3 additions & 0 deletions test/burst/type_traits/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
target_sources(burst-unit-tests PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/minimum_category.cpp
)
50 changes: 50 additions & 0 deletions test/burst/type_traits/minimum_category.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#include <burst/type_traits/minimum_category.hpp>

#include <doctest/doctest.h>

#include <type_traits>
#include <iterator>

TEST_SUITE("minimum_category")
{
TEST_CASE("Выбирает минимальную категорию итератора из набора категорий")
{
using input = std::input_iterator_tag;
using forward = std::forward_iterator_tag;
using bidir = std::bidirectional_iterator_tag;
using random = std::random_access_iterator_tag;

CHECK(std::is_same<burst::minimum_category_t<input, input>, input>::value);
CHECK(std::is_same<burst::minimum_category_t<input, forward>, input>::value);
CHECK(std::is_same<burst::minimum_category_t<input, bidir>, input>::value);
CHECK(std::is_same<burst::minimum_category_t<input, random>, input>::value);

CHECK(std::is_same<burst::minimum_category_t<forward, input>, input>::value);
CHECK(std::is_same<burst::minimum_category_t<forward, forward>, forward>::value);
CHECK(std::is_same<burst::minimum_category_t<forward, bidir>, forward>::value);
CHECK(std::is_same<burst::minimum_category_t<forward, random>, forward>::value);

CHECK(std::is_same<burst::minimum_category_t<bidir, input>, input>::value);
CHECK(std::is_same<burst::minimum_category_t<bidir, forward>, forward>::value);
CHECK(std::is_same<burst::minimum_category_t<bidir, bidir>, bidir>::value);
CHECK(std::is_same<burst::minimum_category_t<bidir, random>, bidir>::value);

CHECK(std::is_same<burst::minimum_category_t<random, input>, input>::value);
CHECK(std::is_same<burst::minimum_category_t<random, forward>, forward>::value);
CHECK(std::is_same<burst::minimum_category_t<random, bidir>, bidir>::value);
CHECK(std::is_same<burst::minimum_category_t<random, random>, random>::value);
}

TEST_CASE("Принимает произвольные наборы категорий")
{
using input = std::input_iterator_tag;
using forward = std::forward_iterator_tag;
using bidir = std::bidirectional_iterator_tag;
using random = std::random_access_iterator_tag;

CHECK(std::is_same<burst::minimum_category_t<input, forward, bidir, random>, input>::value);
CHECK(std::is_same<burst::minimum_category_t<forward, random, bidir>, forward>::value);
CHECK(std::is_same<burst::minimum_category_t<bidir, random, bidir, random>, bidir>::value);
CHECK(std::is_same<burst::minimum_category_t<random, random, random>, random>::value);
}
}

0 comments on commit c969f30

Please sign in to comment.