libcruft-util/tuple/value.hpp

302 lines
8.8 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-2018 Danny Robson <danny@nerdcruft.net>
*/
#pragma once
#include "index.hpp"
#include "../types.hpp"
#include <cstddef>
#include <functional>
#include <tuple>
#include <utility>
namespace cruft::tuple::value {
namespace detail {
// Call the function for the value at the index `S`, then increment
// the index and call ourselves again if we haven't reached the end.
//
// A detail function is used to simplify the case for empty tuples;
// ie, we'd like to keep the index assertion, but a constexpr-if won't
// elide the assertion.
template<
typename FunctionT,
typename TupleT,
std::size_t S = 0
>
void
each (FunctionT &&func, TupleT &&value)
{
using tuple_t = std::decay_t<TupleT>;
static_assert (S < std::tuple_size_v<tuple_t>);
std::invoke (func, std::get<S> (value));
if constexpr (S + 1 < std::tuple_size_v<tuple_t>) {
each<FunctionT,TupleT,S+1> (
std::forward<FunctionT> (func),
std::forward<TupleT> (value)
);
}
}
}
///////////////////////////////////////////////////////////////////////////
/// Call a provided functor of type FunctionT with each value in a
/// provided tuple-like object TupleT
template<
typename FunctionT,
typename TupleT
>
void
each (FunctionT &&func, TupleT &&value)
{
if constexpr (std::tuple_size_v<std::decay_t<TupleT>> > 0) {
return detail::each (
std::forward<FunctionT> (func),
std::forward<TupleT> (value)
);
}
}
///////////////////////////////////////////////////////////////////////////
namespace detail {
template <
typename TupleT,
typename FunctionT,
std::size_t... IndicesV
>
bool
all (TupleT &&val, FunctionT &&pred, std::index_sequence<IndicesV...>)
{
return (pred (std::get<IndicesV> (val)) && ...);
}
}
///------------------------------------------------------------------------
/// Returns true if all members of the tuple satisfy the predicate.
template <typename TupleT, typename FunctionT>
bool
all (TupleT &&val, FunctionT &&pred)
{
return detail::all (
std::forward<TupleT> (val),
std::forward<FunctionT> (pred),
std::make_index_sequence<
std::tuple_size_v<
std::remove_cvref_t<TupleT>
>
> {}
);
}
///------------------------------------------------------------------------
/// Returns true if all members of the tuple are convertible to true.
template <typename TupleT>
bool
all (TupleT &&val)
{
return all (
std::forward<TupleT> (val),
[] (auto const &i) { return bool (i); }
);
}
///------------------------------------------------------------------------
/// Returns true if none of the members of the tuple satisfy the predicate.
template <typename TupleT, typename FunctionT>
bool
none (TupleT &&val, FunctionT &&pred)
{
return all (
std::forward<TupleT> (val),
[&pred] (auto &&arg) { return !pred (arg); }
);
}
///------------------------------------------------------------------------
/// Returns true if none of the members of the tuple are convertible to
/// true.
template <typename TupleT>
bool
none (TupleT &&val)
{
return all (
std::forward<TupleT> (val),
[] (auto const &i) { return !bool (i); }
);
}
///////////////////////////////////////////////////////////////////////////
namespace detail {
template <typename FuncT, typename ArgT, std::size_t ...Indices>
auto
map (std::index_sequence<Indices...>, FuncT &&func, ArgT &&arg)
{
return std::tuple (
std::invoke (func, std::get<Indices> (arg))...
);
}
};
/// returns a tuple of the result of applying the provided function to
/// each value of the supplied tuple.
template <
typename FuncT,
typename ArgT,
typename IndicesV = std::make_index_sequence<
std::tuple_size_v<std::decay_t<ArgT>>
>
>
auto map (FuncT &&func, ArgT &&arg)
{
return detail::map (
IndicesV{},
std::forward<FuncT> (func),
std::forward<ArgT> (arg)
);
}
///////////////////////////////////////////////////////////////////////////
// return a tuple that contains the 'nth' element from each of the supplied
// tuple-like arguments
template <size_t N, typename ...TupleT>
auto
nth (TupleT&&...args)
{
return std::make_tuple (
std::get<N> (args)...
);
}
///////////////////////////////////////////////////////////////////////////
namespace detail {
template <typename ...TupleT, size_t ...Elements>
auto
zip (std::index_sequence<Elements...>, TupleT &&...args)
{
return std::make_tuple (
nth<Elements> (args...)...
);
}
};
/// converts m arguments of n-tuples into one m-tuple of n-tuples by taking
/// successive elements from each argument tuple.
///
/// eg, zip (('a', 1.f), (0, nullptr)) becomes (('a', 0), (1.f, nullptr))
template <typename ...Args >
auto
zip (Args &&...args)
{
static_assert (sizeof...(args) > 0);
// ensure all the types have the same static size
using all_t = std::tuple<std::decay_t<Args>...>;
using first_t = std::tuple_element_t<0,all_t>;
static constexpr auto arity = std::tuple_size_v<first_t>;
static_assert ((
(arity == std::tuple_size_v<std::decay_t<Args>>) && ...
));
return detail::zip (
std::make_index_sequence<arity> {},
std::forward<Args> (args)...
);
}
///////////////////////////////////////////////////////////////////////////
namespace detail {
template <typename TupleT, size_t ...Indices>
auto
reverse (index::indices<Indices...>, TupleT &&val)
{
return std::make_tuple (
std::get<Indices> (val)...
);
}
};
//-------------------------------------------------------------------------
template <typename TupleT>
auto
reverse (TupleT &&val)
{
return detail::reverse (
index::make_reverse_t<
std::tuple_size_v<std::decay_t<TupleT>>
> {},
std::forward<TupleT> (val)
);
}
///////////////////////////////////////////////////////////////////////////
namespace detail {
template <typename FunctionT, typename TupleT, size_t ...Index>
// requires (
// std::is_convertible_v<
// std::tuple_element_t<Index, TupleT>,
// std::common_type<
// std::tuple_element_t<Index, TupleT>...
// >
// > && ...
// )
auto
transform_array (std::index_sequence<Index...>, FunctionT &&func, TupleT &&val)
{
using value_type = std::common_type_t<
std::invoke_result_t<
FunctionT,
std::tuple_element_t<Index, std::remove_cvref_t<TupleT>>
>...
>;
constexpr auto arity = std::tuple_size_v<
std::remove_cvref_t<TupleT>
>;
return std::array<value_type, arity> { {
value_type (std::invoke (func, std::get<Index> (val)))...
} };
}
}
///------------------------------------------------------------------------
/// Transform each element of a tuple with the supplied function and
/// return an array of the return values.
///
/// The function must return types that have a computable common type.
template <typename FunctionT, typename TupleT>
auto
transform_array (FunctionT &&func, TupleT &&val)
{
return detail::transform_array (
std::make_index_sequence<
std::tuple_size_v<
std::remove_cvref_t<TupleT>
>
> {},
std::forward<FunctionT> (func),
std::forward<TupleT > (val )
);
}
}