coord/ops: add elementwise apply

This commit is contained in:
Danny Robson 2018-03-13 14:34:58 +11:00
parent 16c6a6d627
commit 3e9e9bff5a

View File

@ -25,6 +25,7 @@
#include "../debug.hpp"
#include "../maths.hpp"
#include "../tuple.hpp"
#include "../preprocessor.hpp"
#include "../types/bits.hpp"
@ -473,45 +474,77 @@ namespace util {
///////////////////////////////////////////////////////////////////////////
// logic operators
namespace detail {
/// invoke a function elementwise to the arguments elementwise.
///
/// \tparam ArgsT a tuple containing the (coord) arguments for the func
template <
typename RetT,
typename ArgT,
typename FuncT,
std::size_t ...Indices,
typename = std::enable_if_t<
is_coord_v<ArgT> && is_coord_v<RetT>, void
>
typename ArgsT,
std::size_t ...Indices
>
constexpr auto
apply (const std::index_sequence<Indices...>,
FuncT &&func,
const ArgT &a,
const ArgT &b)
ArgsT args) noexcept
{
using part_t = std::tuple_element_t<0,ArgsT>;
using value_t = typename part_t::value_type;
return RetT {
std::invoke (func, a[Indices], b[Indices])...
std::apply (
func,
::util::tuple::convert (
static_cast<
const value_t& (&)(const part_t&)
> (
get<Indices,RetT>
), args
)
)...
};
}
}
//-------------------------------------------------------------------------
// invokes a function elementwise using elementwise parameters from the
// supplied arguments.
//
// equivalent to this pseduocode:
// for (int i: indices (ReturnT))
// res[i] = func (args[i]...);
// return res;
//
// forwards the arguments as a tuple to a helper function that has access
// to indices as a template parameter.
template <
typename RetT,
std::size_t S,
typename T,
template <std::size_t,typename> class ArgT,
typename ReturnT,
typename FuncT,
typename ...ArgT,
typename = std::enable_if_t<
is_coord_v<RetT> && is_coord_v<ArgT>, void
// return type and arguments must be coordinates
(is_coord_v<ReturnT> && ... && is_coord_v<std::decay_t<ArgT>>) &&
// all types must be the same arity
((ReturnT::elements == std::decay_t<ArgT>::elements) && ...) &&
// all the arguments must be the same type
(std::is_same_v<
std::tuple_element_t<0,std::tuple<std::decay_t<ArgT>...>>,
std::decay_t<ArgT>
> && ...),
void
>,
typename Indices = std::make_index_sequence<S>
typename Indices = std::make_index_sequence<ReturnT::elements>
>
constexpr auto
apply (FuncT &&func, const ArgT<S,T> &a, const ArgT<S,T> &b)
invoke (FuncT &&func, ArgT &&...args) noexcept
{
return detail::apply (Indices{}, std::forward<FuncT> (func), a, b);
return detail::apply<ReturnT> (
Indices{},
std::forward<FuncT> (func),
std::tuple (std::forward<ArgT> (args)...)
);
}