85 lines
2.7 KiB
C++
85 lines
2.7 KiB
C++
/**
|
|
* @file tuple.h
|
|
* @author Nat Goodspeed
|
|
* @date 2021-10-04
|
|
* @brief A couple tuple utilities
|
|
*
|
|
* $LicenseInfo:firstyear=2021&license=viewerlgpl$
|
|
* Copyright (c) 2021, Linden Research, Inc.
|
|
* $/LicenseInfo$
|
|
*/
|
|
|
|
#if ! defined(LL_TUPLE_H)
|
|
#define LL_TUPLE_H
|
|
|
|
#include <tuple>
|
|
#include <type_traits> // std::remove_reference
|
|
#include <utility> // std::pair
|
|
|
|
/**
|
|
* tuple_cons() behaves like LISP cons: it uses std::tuple_cat() to prepend a
|
|
* new item of arbitrary type to an existing std::tuple.
|
|
*/
|
|
template <typename First, typename... Rest, typename Tuple_=std::tuple<Rest...>>
|
|
auto tuple_cons(First&& first, Tuple_&& rest)
|
|
{
|
|
// All we need to do is make a tuple containing 'first', and let
|
|
// tuple_cat() do the hard part.
|
|
return std::tuple_cat(std::tuple<First>(std::forward<First>(first)),
|
|
std::forward<Tuple_>(rest));
|
|
}
|
|
|
|
/**
|
|
* tuple_car() behaves like LISP car: it extracts the first item from a
|
|
* std::tuple.
|
|
*/
|
|
template <typename... Args, typename Tuple_=std::tuple<Args...>>
|
|
auto tuple_car(Tuple_&& tuple)
|
|
{
|
|
return std::get<0>(std::forward<Tuple_>(tuple));
|
|
}
|
|
|
|
/**
|
|
* tuple_cdr() behaves like LISP cdr: it returns a new tuple containing
|
|
* everything BUT the first item.
|
|
*/
|
|
// derived from https://stackoverflow.com/a/24046437
|
|
template <typename Tuple, std::size_t... Indices>
|
|
auto tuple_cdr_(Tuple&& tuple, const std::index_sequence<Indices...>)
|
|
{
|
|
// Given an index sequence from [0..N-1), extract tuple items [1..N)
|
|
return std::make_tuple(std::get<Indices+1u>(std::forward<Tuple>(tuple))...);
|
|
}
|
|
|
|
template <typename Tuple>
|
|
auto tuple_cdr(Tuple&& tuple)
|
|
{
|
|
return tuple_cdr_(
|
|
std::forward<Tuple>(tuple),
|
|
// Pass helper function an index sequence one item shorter than tuple
|
|
std::make_index_sequence<
|
|
std::tuple_size<
|
|
// tuple_size doesn't like reference types
|
|
typename std::remove_reference<Tuple>::type
|
|
>::value - 1u>
|
|
());
|
|
}
|
|
|
|
/**
|
|
* tuple_split(), the opposite of tuple_cons(), has no direct analog in LISP.
|
|
* It returns a std::pair of tuple_car(), tuple_cdr(). We could call this
|
|
* function tuple_car_cdr(), or tuple_slice() or some such. But tuple_split()
|
|
* feels more descriptive.
|
|
*/
|
|
template <typename... Args, typename Tuple_=std::tuple<Args...>>
|
|
auto tuple_split(Tuple_&& tuple)
|
|
{
|
|
// We're not really worried about forwarding multiple times a tuple that
|
|
// might contain move-only items, because the implementation above only
|
|
// applies std::get() exactly once to each item.
|
|
return std::make_pair(tuple_car(std::forward<Tuple_>(tuple)),
|
|
tuple_cdr(std::forward<Tuple_>(tuple)));
|
|
}
|
|
|
|
#endif /* ! defined(LL_TUPLE_H) */
|