/** * @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) */