libcruft-util/adapter.hpp

233 lines
7.5 KiB
C++

/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright 2015-2022 Danny Robson <danny@nerdcruft.net>
*/
#include "concepts/named.hpp"
#include <cstddef>
#include <iterator>
#include <tuple>
namespace cruft::adapter {
namespace container {
/// An identity proxy for a referenced container.
///
/// Provided for symmetry with `reverse` and others, so as to allow a
/// common template interface.
template <typename ContainerT>
class identity {
public:
identity (ContainerT &_target)
: m_target (_target)
{ ; }
decltype(auto) begin (void) & { return m_target.begin (); }
decltype(auto) end (void) & { return m_target.end (); }
decltype(auto) begin (void) const& { return m_target.begin (); }
decltype(auto) end (void) const& { return m_target.end (); }
decltype(auto) cbegin (void) const& { return m_target.cbegin (); }
decltype(auto) cend (void) const& { return m_target.cend (); }
private:
ContainerT &m_target;
};
/// Creates a reversed proxy for a referenced container.
///
/// The target container must remain an l-value.
///
/// It is the caller's job to ensure the targeted container remains
/// valid for the duration of the proxy's lifetime.
template <typename T>
class reverse {
public:
explicit reverse (T &_target):
m_target (_target)
{ ; }
auto begin (void) & { return m_target.rbegin (); }
auto end (void) & { return m_target.rend (); }
auto begin (void) const& { return m_target.rbegin (); }
auto end (void) const& { return m_target.rend (); }
auto cbegin (void) const& { return m_target.crbegin (); }
auto cend (void) const& { return m_target.crend (); }
private:
T &m_target;
};
//---------------------------------------------------------------------
template <typename Container>
reverse (Container&) -> reverse<Container>;
///////////////////////////////////////////////////////////////////////
/// Adapter's a container to return indices of the container's data
/// rather than the values themselves.
///
/// The targeted container must be an l-value.
///
/// It is the caller's job to ensure the targeted container remains
/// valid for the duration of the proxy's lifetime.
template <typename T>
class indices {
public:
using typename T::size_type;
explicit indices (T &_target):
m_target (_target)
{ ; }
size_type begin (void)& { return 0; }
size_type end (void)& { return m_target.size (); }
private:
T &m_target;
};
template <
typename ContainerT,
typename FunctionT
>
requires (
std::is_invocable_v<
FunctionT,
typename std::iterator_traits<typename ContainerT::iterator>::value_type
>
)
class transform {
public:
using value_type = std::invoke_result_t<
FunctionT,
typename std::iterator_traits<typename ContainerT::iterator>::value_type
>;
using pointer = void;
using reference = void;
using difference_type = typename std::iterator_traits<
typename ContainerT::iterator
>::difference_type;
template <typename _ContainerT, typename _FunctionT>
transform (_ContainerT &&_container, _FunctionT &&_function)
: m_container (std::forward<_ContainerT> (_container))
, m_function (std::forward< _FunctionT> (_function ))
{ ; }
auto begin (void) const { return iterator (m_container.begin (), m_function); }
auto end (void) const { return iterator (m_container.end (), m_function); }
class iterator {
private:
using underlying = typename ContainerT::iterator;
public:
// HACK: We really should be using the underlying iterator's
// category. However it's a massive PITA to write this code
// right now.
using iterator_category = std::forward_iterator_tag; //typename std::iterator_traits<underlying>::iterator_category;
using difference_type = typename std::iterator_traits<underlying>::difference_type;
using value_type = transform::value_type;
using pointer = void;
using reference = void;
iterator (
typename ContainerT::iterator _cursor,
FunctionT const &_function
)
: m_cursor (_cursor)
, m_function (_function)
{ ; }
decltype(auto) operator* ()
{
return m_function (*m_cursor);
}
iterator& operator++ ()&
{
++m_cursor;
return *this;
}
bool operator== (iterator const &rhs) const noexcept
{
return m_cursor == rhs.m_cursor;
}
private:
typename ContainerT::iterator m_cursor;
FunctionT const &m_function;
};
private:
ContainerT m_container;
FunctionT m_function;
};
template <typename ContainerT, typename FunctionT>
transform (ContainerT, FunctionT) -> transform<
std::remove_cvref_t<ContainerT>,
std::remove_cvref_t< FunctionT>
>;
}
namespace iterator {
///////////////////////////////////////////////////////////////////////
/// Adapt's an iterator to return the n-th element of the tuple that
/// corresponds to the underlying iterator::value_type when
/// dereferenced.
template <size_t I, typename IteratorT>
struct scalar {
public:
using iterator_category = typename std::iterator_traits<IteratorT>::iterator_category;
using value_type = typename std::tuple_element<
I,
typename std::iterator_traits<
IteratorT
>::value_type
>::type;
using difference_type = typename std::iterator_traits<IteratorT>::difference_type;
using reference = value_type&;
using const_reference = value_type const&;
explicit scalar (IteratorT _inner):
m_inner (_inner)
{ ; }
const_reference operator* (void) const&
{ return std::get<I> (*m_inner); }
reference operator* (void)&
{ return std::get<I> (*m_inner); }
bool operator== (scalar<I,IteratorT> rhs) { return m_inner == rhs.m_inner; }
bool operator!= (scalar<I,IteratorT> rhs) { return m_inner != rhs.m_inner; }
scalar<I,IteratorT>& operator++ (void)
{ ++m_inner; return *this; }
private:
IteratorT m_inner;
};
}
}