C++17 multiple parameter pack expansion - c++

I am attempting to map function f over tuples t0, t1, etc. to return the tuple
std::tuple<f(std::get<0>(t0),std:get<0>(t1),...),f(std::get<1>(t0),std::get<1>(t1),...),...). I have a version working using car,cdr, and cons but am trying to get a version working using std::index_sequence.
The code:
// Helper
template<typename T>
using make_tuple_index = std::make_index_sequence<std::tuple_size<T>::value>;
// Implementation
template<typename F, typename... Ts, std::size_t... Is>
auto mapx_n_impl(const F& f, std::index_sequence<Is...>, const Ts&... t)
{ return std::make_tuple(f(std::get<Is>(t...))...); }
// Interface
template<typename T,
typename F,
typename Indices = make_tuple_index<T>>
auto map(const T& t, const F& f)
{ return mapx_impl(t, f, Indices{}); }
// Test
auto tup1 = std::make_tuple(1.0, 2.0, 3.0);
auto tup2 = std::make_tuple(0.0, 1.0, 2.0);
auto r = mapx_n([](auto x, auto y) { return x - y; }, tup1, tup2);
The problem is expanding the parameter packs in the implementation return statement. I need it to expand t in the "inner" loop and Is in the "outer" loop. How is the expansion controlled? And, how do I fix my return statement?
UPDATE:
Based on the response from #Yakk and the further elucidation by #max66, I have simplified my code as much as I think possible. The current version integrates a version of the parameter pack expansion helper from #Yakk's answer as well as factoring out the get_element call into a lambda.
// invoke_with_pack
template<std::size_t... Is, typename F>
auto invoke_with_pack(std::index_sequence<Is...>, F&& function)
{ return function(std::integral_constant<std::size_t, Is>{}...); }
// nth
template<natural N, typename... Ts>
using nth = typename std::tuple_element<N, std::tuple<Ts...>>::type;
// make_tuple_index -- Helper template for computing indices
// corresponding to a tuple.
template<typename T>
using make_tuple_index = std::make_index_sequence<std::tuple_size<T>::value>;
// map_n -- Map <function> over <tuples> t0,t1,...
template<typename F,
typename... Ts,
typename Indices = make_tuple_index<nth<0,Ts...>>>
auto map_n(F&& function, Ts&... tuples)
{
auto get_element = [&](auto I) { return function(std::get<I>(tuples)...); };
return invoke_with_pack(Indices{}, [&](auto... Is) {
return std::make_tuple(get_element(Is)...);
});
}
Now on to figuring out how to implement fold_left and fold_right with indexes instead of car,cdr and cons.

Start with this:
namespace utility {
template<std::size_t...Is>
auto index_over( std::index_sequence<Is...> ) {
return [](auto&& f)->decltype(auto) {
return decltype(f)(f)( std::integral_constant<std::size_t, Is>{}... );
};
}
template<std::size_t N>
auto index_upto( std::integral_constant<std::size_t, N> ={} ) {
return index_over( std::make_index_sequence<N>{} );
}
}
that lets us avoid having to write a whole pile of functions just to expand some parameter packs. index_upto<7>()([](auto...Is){ /* here */ }) gives you a context where you have a bunch of compile time integral constants 0 through 6 in a pack.
template<class F, class T0, class...Tuples>
auto map_over_tuples( F&& f, T0&... t0, Tuples&&... tuples ) {
using tuple_size = typename std::tuple_size< std::decay_t<T0> >::type;
auto get_element = [&](auto I){
return f(std::get<I>(std::forward<T0>(t0)), std::get<I>(std::forward<Tuples>(tuples)...));
};
return index_upto<tuple_size{}>()([&](auto...Is){
return std::make_tuple( get_element(Is)... );
});
}
in some compilers, use of I has to be replaced with decltype(I)::value in get_element.

The problem is expanding the parameter packs in the implementation return statement. I need it to expand t in the "inner" loop and Is in the "outer" loop. How is the expansion controlled? And, how do I fix my return statement?
I don't see a simple and elegant way to do this.
It seems to me that you have to decouple the two packs in same way and expand first one then another.
If you see the Yakk solution, you see the inner expansion (t...) through a lambda function with single calling f() in it.
The following is a solution, based on the same principle with a template function, and the use of std::apply to leave the call of f() outside.
Frankly, I think the Yakk solution is more efficient (no need of unuseful tuples creation) so take this example as an oddity
#include <tuple>
#include <iostream>
template <std::size_t I, typename ... Ts>
auto getN (Ts const & ... t)
{ return std::make_tuple(std::get<I>(t)...); }
template<typename F, typename... Ts, std::size_t... Is>
auto mapx_n_impl(const F& f, std::index_sequence<Is...>, const Ts&... t)
{ return std::make_tuple(std::apply(f, getN<Is>(t...))...); }
template <typename F, typename T0, typename ... Ts>
auto mapx_n (F const & f, T0 const & t0, Ts const & ... ts)
{ return mapx_n_impl(f,
std::make_index_sequence<std::tuple_size<T0>::value> {}, t0, ts...); }
int main ()
{
// Test
auto tup1 = std::make_tuple(1.0, 2.0, 3.0);
auto tup2 = std::make_tuple(0.0, 1.0, 2.0);
auto r = mapx_n([](auto x, auto y) { return x - y; }, tup1, tup2);
std::cout << std::get<0U>(r) << std::endl;
std::cout << std::get<1U>(r) << std::endl;
std::cout << std::get<2U>(r) << std::endl;
}

Based on the great solutions I've developed a more general function to transform and fold (reduce) tuples. As you were mentioning fold_left and fold_right in your question, this might be of interest for the discussion.
The basic idea is to apply a second functor to the mapped (a.k.a. transformed) tuple rather than calling std::make_tuple as you did in your solution. This allows many algorithms (e.g. count_if, all_of, any_of etc.) to be implemented easily.
Live example here.
#include <tuple>
#include <functional>
#define FWD(x) std::forward<decltype(x)>(x)
namespace tuple_utils {
template<class UnaryFunc, std::size_t... Idx>
constexpr auto apply_for_each_index(std::index_sequence<Idx...>, UnaryFunc&& f) {
return FWD(f)(std::integral_constant<std::size_t, Idx>{}...);
}
template<typename T>
using make_tuple_index = std::make_index_sequence<std::tuple_size<std::decay_t<T>>::value>;
template<class... Ts>
using first_element_t = typename std::tuple_element<0, std::tuple<Ts...>>::type;
template<class T>
constexpr size_t tuple_size_v = std::tuple_size_v<std::decay_t<T>>;
template<class Map, class Reduce, class... Tuples>
constexpr auto
transform_reduce(Map &&transform_func, Reduce &&reduce_func, Tuples&&... tuples) {
using first_tuple_t = first_element_t<Tuples...>;
constexpr size_t first_tuple_size = tuple_size_v<first_tuple_t>;
static_assert(((tuple_size_v<Tuples> == first_tuple_size) && ...), "all tuples must be of same size!");
auto transform_elements_at = [&](auto Idx){
return FWD(transform_func)(std::get<Idx>(FWD(tuples))...);
};
using Indices = make_tuple_index<first_tuple_t>;
return apply_for_each_index(
Indices{},
[&](auto... Indices) {
return FWD(reduce_func)(transform_elements_at(Indices)...);
}
);
}
}
int main()
{
using tuple_utils::transform_reduce;
auto make_tuple = [](auto&&... xs) { return std::make_tuple(FWD(xs)...); };
auto equal = [](auto&& first, auto&&... rest){return ((FWD(first) == FWD(rest)) && ... ); };
constexpr auto all = [](auto... bs) { return (bs && ...);};
constexpr auto any = [](auto... bs) { return (bs || ...);};
constexpr auto count = [](auto... bs) { return (bs + ...); };
static_assert(transform_reduce(std::equal_to<>(), make_tuple, std::tuple{1,2,3,4}, std::tuple{1,2,7,8}) == std::tuple{true, true, false, false});
static_assert(transform_reduce(equal, all, std::tuple{1,2,3,4}, std::tuple{1,2,7,8}) == false);
static_assert(transform_reduce(equal, all, std::tuple{1,2,3,4}, std::tuple{1,2,3,4}, std::tuple{1,2,3,4}) == true);
static_assert(transform_reduce(equal, any, std::tuple{1,2,3,4}, std::tuple{1,2,7,8}) == true);
static_assert(transform_reduce(equal, count, std::tuple{1,2,3,4}, std::tuple{1,2,7,8}) == 2);
}

Related

How to flatten heterogeneous lists (aka tuples of tuples of ...)

I am attempting to employ C++17 fold expressions and the C++14 indices trick to flatten an arbitrary input consisting of tuples and non-tuples.
The expected result should at least conform to these requirements:
constexpr auto bare = 42;
constexpr auto single = std::tuple{bare};
constexpr auto nested_simple = std::tuple{single};
constexpr auto multiple = std::tuple{bare, bare};
constexpr auto nested_multiple = std::tuple{multiple};
constexpr auto multiply_nested = std::tuple{multiple, multiple};
static_assert(flatten(bare) == bare);
static_assert(flatten(single) == bare);
static_assert(flatten(nested_simple) == bare);
static_assert(flatten(multiple) == multiple);
static_assert(flatten(nested_multiple) == multiple);
static_assert(flatten(multiply_nested) == std::tuple{bare, bare, bare, bare});
I have relatively simple code to handle all but the last case:
template<typename T>
constexpr decltype(auto) flatten(T&& t)
{
return std::forward<T>(t);
}
template<typename T>
constexpr decltype(auto) flatten(std::tuple<T> t)
{
return std::get<0>(t);
}
template<typename... Ts>
constexpr decltype(auto) flatten_multi(Ts&&... ts)
{
return std::make_tuple(flatten(ts)...);
}
template<typename... Ts, std::size_t... Indices>
constexpr decltype(auto) flatten_impl(std::tuple<Ts...> ts, const std::index_sequence<Indices...>&)
{
return flatten_multi(std::get<Indices>(ts)...);
}
template<typename... Ts>
constexpr decltype(auto) flatten(std::tuple<Ts...> ts)
{
return flatten_impl(ts, std::make_index_sequence<sizeof...(Ts)>());
}
Live demo here. Obviously, it doesn't handle multiply nested items well.
The more advanced form to handle the multiply_nested case I haven't found. I tried applying operator>> to be able to use fold expressions, but haven't been able to get anything that compiles. My last attempt can be found here. The core idea is to use operator>> in a fold expression to combine elements 2 by 2, each time unwrapping the previous result.
It seems to me I should be able to use something like std::tuple_cat, but it shouted at me quite loudly for reasons I couldn't decipher completely.
So my question is this: what am I missing? How can I unwrap an arbitrarily deeply arbitrarily nested tuple-like input?
I propose to SFINAE on presence of tuple
// Simple traits
template <typename T> struct is_tuple : std::false_type{};
template <typename... Ts> struct is_tuple<std::tuple<Ts...>> : std::true_type{};
// utility to ensure return type is a tuple
template<typename T>
constexpr decltype(auto) as_tuple(T t) { return std::make_tuple(t); }
template<typename ...Ts>
constexpr decltype(auto) as_tuple(std::tuple<Ts...> t) { return t; }
// Simple case
template<typename T>
constexpr decltype(auto) flatten(T t)
{
return t;
}
// Possibly recursive tuple
template<typename T>
constexpr decltype(auto) flatten(std::tuple<T> t)
{
return flatten(std::get<0>(t));
}
// No more recursion, (sizeof...Ts != 1) with above overload
template<typename ...Ts, std::enable_if_t<!(is_tuple<Ts>::value || ...), bool> = false>
constexpr decltype(auto) flatten(std::tuple<Ts...> t)
{
return t;
}
// Handle recursion
template<typename ...Ts, std::enable_if_t<(is_tuple<Ts>::value || ...), bool> = false>
constexpr decltype(auto) flatten(std::tuple<Ts...> t)
{
return std::apply([](auto...ts)
{
return flatten(std::tuple_cat(as_tuple(flatten(ts))...));
}, t);
}
Demo
namespace flattenns {
struct flat_t {};
template<std::size_t... Is, class...As>
constexpr auto flatten( std::index_sequence<Is...>, flat_t, std::tuple<As...> as ) {
return std::tuple_cat( flatten(flat_t{}, std::get<Is>(as))... );
}
template<class...As, class...Ts>
constexpr auto flatten( flat_t, std::tuple<As...> as ) {
return flatten( std::make_index_sequence<sizeof...(As)>{}, flat_t{}, as );
}
template<class T>
constexpr std::tuple<T> flatten( flat_t, T t ) { return {t}; }
template<class...Ts>
constexpr auto flatten( flat_t, Ts... ts ) {
return std::tuple_cat( flatten(flat_t{}, ts)... );
}
constexpr std::tuple<> flatten( flat_t ) { return {}; }
}
template<class...Ts>
constexpr auto sane_flatten( Ts...ts ) {
return flattenns::flatten(flattenns::flat_t{}, ts...);
}
// to take std::tuple<int>(7) -> 7
namespace insanens {
template<class...Ts>
constexpr auto unpack_single( std::tuple<Ts...> t ) {return t;}
template<class T>
constexpr auto unpack_single( std::tuple<T> t ) {return std::get<0>(t);}
}
template<class...Ts>
constexpr auto insane_flatten( Ts...ts ) {
return insanens::unpack_single( sane_flatten(ts...) );
}
template<class...Ts>
constexpr auto flatten( Ts...ts ) {
return insane_flatten(ts...);
}
As noted above, flatten( std::tuple<int>(7) ) should NOT BE 7. That is insanity.
But as you want it, I add it as a post-processing step.
Your operation is otherwise relatively sane. You are recursively applying [[x],[y]] to [x,y]. The final unboxing is not sane. By splitting it off, the code becomes easy, which is also evidence why it is insane.
Live example.
In case you are wondering, the flat_t tag type exists in order to (a) split the index sequence from a possible argument (which could be done by having a different function name) and (b) enable ADL lookup so every implementation of flatten can see all of the other ones.
Here is another version that has two design goals:
avoid construction of temporary tuples and avoid std::tuple_cat
explicitly determine the types in the final tuple
For avoiding temporary tuples and std::tuple_cat, it is useful to predict the final size of the output tuple. Let us define a helper called get_rank:
#include <cstddef>
#include <tuple>
#include <type_traits>
template<class T>
struct Type {// tag type
using type = T;
};
template<class T>
constexpr std::size_t get_rank(Type<T>) {
static_assert(!std::is_const<T>{} && !std::is_volatile<T>{}, "avoid surprises");
return 1;
}
template<class... Ts>
constexpr std::size_t get_rank(Type< std::tuple<Ts...> >) {
return (0 + ... + get_rank(Type<Ts>{}));
}
The flatten function can utilize get_rank in order to create an index sequence for the elements of the output tuple. This sequence is passed to flatten_impl together with the forwarded input tuple and a type tag. Let us explicitly provide lvalue and rvalue overloads for the interface function, but use perfect forwarding internally:
#include <cstddef>
#include <tuple>
#include <utility>
// to be implemented
#include "tuple_element_at_rankpos_t.hpp"
#include "get_at_rankpos.hpp"
template<std::size_t... rank_positions, class Tuple, class... Ts>
constexpr auto flatten_impl(
std::index_sequence<rank_positions...>,
Tuple&& tuple,
Type< std::tuple<Ts...> > tuple_tag
) {
return std::tuple<
tuple_element_at_rankpos_t< rank_positions, std::tuple<Ts...> >...
>{
get_at_rankpos<rank_positions>(std::forward<Tuple>(tuple), tuple_tag)...
};
}
template<class... Ts>
constexpr auto flatten(const std::tuple<Ts...>& tuple) {
using TupleTag = Type< std::tuple<Ts...> >;
constexpr std::size_t rank = get_rank(TupleTag{});
return flatten_impl(
std::make_index_sequence<rank>{}, tuple, TupleTag{}
);
}
template<class... Ts>
constexpr auto flatten(std::tuple<Ts...>& tuple) {
using TupleTag = Type< std::tuple<Ts...> >;
constexpr std::size_t rank = get_rank(TupleTag{});
return flatten_impl(
std::make_index_sequence<rank>{}, tuple, TupleTag{}
);
}
template<class... Ts>
constexpr auto flatten(std::tuple<Ts...>&& tuple) {
using TupleTag = Type< std::tuple<Ts...> >;
constexpr std::size_t rank = get_rank(TupleTag{});
return flatten_impl(
std::make_index_sequence<rank>{}, std::move(tuple), TupleTag{}
);
}
At this point, we need two more building blocks:
tuple_element_at_rankpos_t (like std::tuple_element_t, but for nested tuples) and
get_at_rankpos (like std::get, but for nested tuples).
Either building block shall find the type/value of an element in the nested input tuple based on the element's position in the flattened output tuple. At each nesting level, these building blocks need to extract the index for the current nesting depth from the rankpos. This common index computation can be moved to an extract_index helper. The first building block may look like this:
#include <cassert>
#include <cstddef>
#include <array>
#include <tuple>
#include <utility>
template<class... Ts>
constexpr auto extract_index(
std::size_t rankpos, Type< std::tuple<Ts...> >
) {
static_assert(sizeof...(Ts) >= 1, "do not extract from empty tuples");
constexpr auto ranks = std::array{get_rank(Type<Ts>{})...};
std::size_t index = 0;
std::size_t nested_rankpos = rankpos;
while(nested_rankpos >= ranks[index]) {
nested_rankpos -= ranks[index++];
assert(index < sizeof...(Ts));
}
return std::pair{index, nested_rankpos};
}
////////////////////////////////////////////////////////////////////////////////
template<std::size_t rankpos, class T>
constexpr auto tuple_element_at_rankpos_tag(
Type<T> /* element_tag */
) {
static_assert(rankpos == 0);
return Type<T>{};
}
template<std::size_t rankpos, class... Ts>
constexpr auto tuple_element_at_rankpos_tag(
Type< std::tuple<Ts...> > tuple_tag
) {
// constexpr auto [index, nested_rankpos] = extract_index(rankpos, tuple_tag);
constexpr std::pair pair = extract_index(rankpos, tuple_tag);
constexpr std::size_t index = pair.first;
constexpr std::size_t nested_rankpos = pair.second;
using NestedType = std::tuple_element_t< index, std::tuple<Ts...> >;
return tuple_element_at_rankpos_tag<nested_rankpos>(
Type<NestedType>{}
);
}
template<std::size_t rankpos, class Tuple>
using tuple_element_at_rankpos_t = typename decltype(
tuple_element_at_rankpos_tag<rankpos>(Type<Tuple>{})
)::type;
The second building block is a repetition of the same glue code as above. In addition to the type we need to handle the values (lvalue, const lvalue, rvalue). Using perfect forwarding we may write:
template<std::size_t rankpos, class Element, class T>
constexpr decltype(auto) get_at_rankpos(
Element&& element,
Type<T> /* element_tag */
) {
static_assert(rankpos == 0);
return std::forward<Element>(element);
}
template<std::size_t rankpos, class Tuple, class... Ts>
constexpr decltype(auto) get_at_rankpos(
Tuple&& tuple,
Type< std::tuple<Ts...> > tuple_tag
) {
// constexpr auto [index, nested_rankpos] = extract_index(rankpos, tuple_tag);
constexpr std::pair pair = extract_index(rankpos, tuple_tag);
constexpr std::size_t index = pair.first;
constexpr std::size_t nested_rankpos = pair.second;
using NestedType = std::tuple_element_t< index, std::tuple<Ts...> >;
return get_at_rankpos<nested_rankpos>(
std::get<index>(std::forward<Tuple>(tuple)),
Type<NestedType>{}
);
}
Something perhaps a little more straightforward, although more verbose: partial class template specialization + if constexpr:
The basic approach is to specialize the following base class:
template<class... T>
struct flatten
{};
To account for our three cases:
A bare value
A tuple of one thing
A tuple of more than one thing
Case #1, the base case, is fairly straightforward, just return what we get:
//base case: something that isn't another tuple
template<class T>
struct flatten<T>
{
template<class U>
constexpr decltype(auto) operator()(U&& _value){
return std::forward<U>(_value);
}
};
Case #2 is also pretty straightforward, just recurse on itself until we reach Case #1
// recursive case 1 : plain old tuple of one item
template<class T>
struct flatten<std::tuple<T>>
{
template<class U>
constexpr decltype(auto) operator()(U&& _tup){
return flatten<std::remove_cvref_t<T>>{}(std::get<0>(_tup));
}
};
Case #3 is long because of the possible sub-cases, but each block is pretty readable. We
Flatten the first element (possibly recurses)
Flatten the rest of the elements (possible recurses)
And then we have four cases to consider:
We have two tuples (e.g., tuple<int, int>, tuple<int, int>)
We have a tuple and a value (e.g., tuple<int, int>, int)
We have a value and a tuple (e.g., int, tuple<int, int>)
We have two values (e.g., int, int)
We just need one helper function that allows us to strip the head off a tuple and return the rest of it.
// helper for getting tuple elements except the first one
template<template<class...> class Tup, class... T, size_t... indices>
constexpr auto get_rest_of_tuple(const Tup<T...>& _tup, std::index_sequence<indices...>){
return std::make_tuple(std::get<indices + 1>(_tup)...);
}
and some helper traits:
// some type traits to use for if constexpr
template<class T>
struct is_tuple : std::false_type{};
template<class... T>
struct is_tuple<std::tuple<T...>> : std::true_type{};
template<class T>
constexpr bool is_tuple_v = is_tuple<T>::value;
Finally the impl:
// recursive case 2: tuple of more than one item
template<class First, class Second, class... Rest>
struct flatten<std::tuple<First, Second, Rest...>>
{
template<class Tup>
constexpr decltype(auto) operator()(Tup&& _tup){
auto flattened_first = flatten<std::remove_cvref_t<First>>{}(std::get<0>(_tup));
auto restTuple = get_rest_of_tuple(_tup, std::make_index_sequence<sizeof...(Rest)+1>{});
auto flattened_rest = flatten<std::remove_cvref_t<decltype(restTuple)>>{}(restTuple);
// both are tuples
if constexpr(is_tuple_v<decltype(flattened_first)> && is_tuple_v<decltype(flattened_rest)>)
{
return std::tuple_cat(flattened_first, flattened_rest);
}
// only second is tuple
if constexpr(!is_tuple_v<decltype(flattened_first)> && is_tuple_v<decltype(flattened_rest)>)
{
return std::tuple_cat(std::make_tuple(flattened_first), flattened_rest);
}
//only first is tuple
if constexpr(is_tuple_v<decltype(flattened_first)> && !is_tuple_v<decltype(flattened_rest)>)
{
return std::tuple_cat(flattened_first, std::make_tuple(flattened_rest));
}
// neither are tuples
if constexpr(!is_tuple_v<decltype(flattened_first)> && !is_tuple_v<decltype(flattened_rest)>)
{
return std::tuple_cat(std::make_tuple(flattened_first), std::make_tuple(flattened_rest));
}
}
};
} // namespace detail
Finally, we use trampolining to hide all these details from the end user by shoving them into a details namespace and exposing the following function to call into them:
template<class T>
constexpr decltype(auto) flatten(T&& _value){
return detail::flatten<std::remove_cvref_t<T>>{}(std::forward<T>(_value));
}
Demo
(includes some additional tests for correctness)
While the impl of Case #3 above is pretty straightforward it is both verbose and a bit inefficient (the compiler evaluates each of those if constexpr statements when it should only evaluate one, but I didn't want to string along else branches because of the nesting).
We can pretty vastly simplify Case #3 by diverting to two helper functions that detect whether the argument is a tuple of not and return the right thing:
template<class U, std::enable_if_t<!is_tuple_v<U>, int> = 0>
constexpr decltype(auto) flatten_help(U&& _val){
return std::make_tuple(_val);
}
template<class... T>
constexpr decltype(auto) flatten_help(const std::tuple<T...>& _tup){
return _tup;
}
// recursive case 2: tuple of more than one item
template<class First, class Second, class... Rest>
struct flatten<std::tuple<First, Second, Rest...>>
{
template<class Tup>
constexpr decltype(auto) operator()(Tup&& _tup){
auto flattened_first = flatten<std::remove_cvref_t<First>>{}(std::get<0>(_tup));
auto restTuple = get_rest_of_tuple(_tup, std::make_index_sequence<sizeof...(Rest)+1>{});
auto flattened_rest = flatten<std::remove_cvref_t<decltype(restTuple)>>{}(restTuple);
return std::tuple_cat(flatten_help(flattened_first), flatten_help(flattened_rest));
}
};
Demo 2

Remove last item from function parameter pack

I am writing a method to extract values from arbitrarily nested structs. I am almost there, but would like to also provide an option to convert the value retrieved (by default no conversion). Since parameter packs can't be followed by another template parameter, I have to fudge this a bit. The below works except for the indicated line:
#include <iostream>
#include <type_traits>
typedef struct {
int a;
int b;
} bar;
typedef struct {
int c;
bar d;
} baz;
template <typename T, typename S, typename... Ss>
auto inline getField(const T& obj, S field1, Ss... fields)
{
if constexpr (!sizeof...(fields))
return obj.*field1;
else
return getField(obj.*field1, fields...);
}
template <typename Obj, typename Out, class ...C, typename... T>
auto inline getFieldC(const Obj& obj, Out, T C::*... field)
{
return static_cast<Out>(getField(obj, field...));
}
template<class T> struct tag_t { using type = T; };
template<class...Ts>
using last = typename std::tuple_element_t< sizeof...(Ts) - 1, std::tuple<tag_t<Ts>...> >::type;
template <typename Obj, typename... T>
auto getMyFieldWrapper(const Obj& obj, T... field)
{
if constexpr (std::is_member_object_pointer_v<last<Obj, T...>>)
return getField(obj, field...);
else
return getFieldC(obj, last<Obj, T...>{}, field...); // <- this doesn't compile, need a way to pass all but last element of field
}
int main()
{
baz myObj;
std::cout << getMyFieldWrapper(myObj, &baz::c); // works
std::cout << getMyFieldWrapper(myObj, &baz::d, &bar::b); // works
std::cout << getMyFieldWrapper(myObj, &baz::d, &bar::b, 0.); // doesn't work
}
How do I implement the indicated line? I'm using the latest MSVC, and am happy to make full use of C++17 to keep things short and simple.
Usually more helpful to invert the flow. First, write a higher-order function that forwards an index sequence:
template <typename F, size_t... Is>
auto indices_impl(F f, std::index_sequence<Is...>) {
return f(std::integral_constant<size_t, Is>()...);
}
template <size_t N, typename F>
auto indices(F f) {
return indices_impl(f, std::make_index_sequence<N>());
}
That is just generally useful in lots of places.
In this case, we use it to write a higher-order function to drop the last element in a pack:
template <typename F, typename... Ts>
auto drop_last(F f, Ts... ts) {
return indices<sizeof...(Ts)-1>([&](auto... Is){
auto tuple = std::make_tuple(ts...);
return f(std::get<Is>(tuple)...);
});
}
And then you can use that:
return drop_last([&](auto... elems){
return getMyField(obj, last<Obj, T...>{}, elems...);
}, field...);
References omitted for brevity.
Of course, if you want to combine both and just rotate, you can do:
// Given f and some args t0, t1, ..., tn, calls f(tn, t0, t1, ..., tn-1)
template <typename F, typename... Ts>
auto rotate_right(F f, Ts... ts) {
auto tuple = std::make_tuple(ts...);
return indices<sizeof...(Ts)-1>([&](auto... Is){
return f(
std::get<sizeof...(Ts)-1>(tuple),
std::get<Is>(tuple)...);
});
}
used as:
return rotate_right([&](auto... elems){
return getMyField(obj, elems...);
}, field...);
How do I implement the indicated line?
Not sure to understand what do you want but... it seems to me that you can make it calling an intermediate function
template <std::size_t ... Is, typename ... Ts>
auto noLastArg (std::index_sequence<Is...> const &,
std::tuple<Ts...> const & tpl)
{ return getMyField(std::get<Is>(tpl)...); }
you can rewrite your function as follows
template <typename Obj, typename ... T>
auto getMyFieldWrapper (Obj const & obj, T ... field)
{
if constexpr (std::is_member_object_pointer<last<Obj, T...>>::value )
return getMyField(obj, field...);
else
return noLastArg(std::make_index_sequence<sizeof...(T)>{},
std::make_tuple(obj, field...));
}
The idea is pack the arguments for getMyField in a std::tuple of sizeof...(T)+1u elements (+1 because there is also obj) and call getMyField() unpacking the first sizeof...(T) of them.
But isn't clear, to me, if you want also last<Obj, T...>{}.
In this case, the call to noLastArg() become
return noLastArg(std::make_index_sequence<sizeof...(T)+1u>{},
std::make_tuple(obj, last<Obj, T...>{}, field...));

Iteratively filtering arguments matching a predicate at compile-time

Context
Firstly, some context: I'm using an empty struct called nothing to emulate something similar to "regular void" in order to prettify some interfaces that rely on chaining multiple function objects together.
struct nothing { };
Example usage:
when_all([]{ return 0; }, []{ }, []{ return 'a'; })
.then([](int, char){ }); // result of lambda in the middle ignored
In the above example, what's actually happening is that I'm packaging all the results of the function objects passed to when_all in an std::tuple, converting void to nothing (in this example: std::tuple<int, nothing, char>), then I'm using a helper function called apply_ignoring_nothing that invokes a function object by unpacking an std::tuple, ignoring the elements that are nothing.
auto f_then = [](int, char){ };
auto args = std::tuple{0, nothing{}, 'a'};
apply_ignoring_nothing(f_then, args); // compiles
apply_ignoring_nothing is implemented in terms of call_ignoring_nothing.
Question
I have a function call_ignoring_nothing with the following signature:
template <typename F, typename... Ts>
constexpr decltype(auto) call_ignoring_nothing(F&& f, Ts&&... xs);
This function will invoke f by perfectly-forwarding all xs... for which the compile-time is_nothing_v<T> returns false.
is_nothing_v is defined as follows:
template <typename T>
inline constexpr bool is_nothing_v = std::is_same_v<std::decay_t<T>, nothing>;
The way I implemented call_ignoring_nothing is recursively. The base case only takes f and simply invokes it:
#define FWD(x) ::std::forward<decltype(x)>(x)
template <typename F>
constexpr decltype(auto) call_ignoring_nothing(F&& f)
{
return returning_nothing_instead_of_void(FWD(f));
}
The recursive case takes f, x, and xs..., and conditionally binds x as one of f's arguments if !is_nothing_v<decltype(f)> through a lambda. It then recurses over call_ignoring_nothing passing the newly-created lambda as f:
template <typename F, typename T, typename... Ts>
constexpr decltype(auto) call_ignoring_nothing(F&& f, T&& x, Ts&&... xs)
{
return call_ignoring_nothing(
[&](auto&&... ys) -> decltype(auto) {
if constexpr(is_nothing_v<T>)
{
return FWD(f)(FWD(ys)...);
}
else
{
return FWD(f)(FWD(x), FWD(ys)...);
}
},
FWD(xs)...);
}
I would like to implement call_ignoring_nothing in an iterative manner, possibly making use of pack expansion to filter out the arguments without recursion.
Is it possible to implement call_ignoring_nothing without recursion? I couldn't think of any technique that allows arguments to be filtered out during pack expansion.
Not so different from the Griwes suggestion but... I suppose you can use std::apply(), std::tuple_cat(), std::get() and tuples that are empty or with value according the value of is_nothing_v.
I mean... something like [edit: improved with a suggestion from T.C. and an example from the OP itself (Vittorio Romeo)]
template <bool B, typename ... Ts>
constexpr auto pick_if (Ts && ... xs)
{
if constexpr ( B )
return std::forward_as_tuple(std::forward<Ts>(xs)...);
else
return std::tuple{};
}
template <typename F, typename ... Ts>
constexpr decltype(auto) call_ignoring_nothing (F && f, Ts && ... xs)
{
return std::apply(f,
std::tuple_cat(pick_if<!is_nothing_v<Ts>>(std::forward<Ts>(xs))...)
);
}
The following is a working example
#include <tuple>
#include <iostream>
#include <type_traits>
struct nothing { };
template <typename T>
constexpr bool is_nothing_v = std::is_same<std::decay_t<T>, nothing>::value;
template <bool B, typename ... Ts>
constexpr auto pick_if (Ts && ... xs)
{
if constexpr ( B )
return std::forward_as_tuple(std::forward<Ts>(xs)...);
else
return std::tuple{};
}
template <typename F, typename ... Ts>
constexpr decltype(auto) call_ignoring_nothing (F && f, Ts && ... xs)
{
return std::apply(f,
std::tuple_cat(pick_if<!is_nothing_v<Ts>>(std::forward<Ts>(xs))...)
);
}
float foo (int a, float b) { return a + b; }
int main ()
{
std::cout << call_ignoring_nothing(foo, nothing{}, 12, nothing{},
2.3f, nothing{}); // print 14.3
}
live example on wandbox
Here's another take that doesn't depend on tuple_cat. First calculate the positions at which a pack of bools is true via a "normal" constexpr function template:
template<class... Bools>
constexpr int count(Bools... bs)
{
return (bool(bs) + ...);
}
template<bool... bs>
constexpr std::array<std::size_t, count(bs...)> indices()
{
std::array<std::size_t, count(bs...)> ret = {};
std::size_t i = 0, j = 0;
for(bool b : {bs...}) {
if(b) {
ret[j] = i;
++j;
}
++i;
}
return ret;
}
Then convert the result to a index_sequence:
template<bool...bs, std::size_t...Is>
constexpr auto indices_as_sequence_helper(std::index_sequence<Is...>)
{
return std::index_sequence<indices<bs...>()[Is]...>{};
}
template<bool...bs>
constexpr auto indices_as_sequence()
{
return indices_as_sequence_helper<bs...>(std::make_index_sequence<count(bs...)>());
}
Then it's a simple matter of forward_as_tuple + get with the index_sequence:
template <typename F, typename... Ts, std::size_t... Is>
constexpr decltype(auto) call_some(std::index_sequence<Is...>, F&& f, Ts&&... xs)
{
return std::forward<F>(f)(
std::get<Is>(std::forward_as_tuple(std::forward<Ts>(xs)...))...);
}
template <typename F, typename... Ts>
constexpr decltype(auto) call_ignoring_nothing(F&& f, Ts&&... xs)
{
return call_some(indices_as_sequence<!is_nothing_v<Ts>...>(),
std::forward<F>(f), std::forward<Ts>(xs)...);
}

Is it possible to return a variadic lambda from a function template?

I have the following piece of code (c++11):
template <typename F,
typename FirstT,
typename... FIn>
auto min_on(F f, FirstT first, FIn... v) -> typename std::common_type<FirstT, FIn...>::type
{
using rettype = typename std::common_type<FirstT, FIn...>::type;
using f_rettype = decltype(f(first));
rettype result = first;
f_rettype result_trans = f(first);
f_rettype v_trans;
(void)std::initializer_list<int>{
((v_trans = f(v), v_trans < result_trans)
? (result = static_cast<rettype>(v), result_trans = v_trans, 0)
: 0)...};
return result;
}
Which basically returns the argument result that produced the minimum value for expression f(result). This can be called like this:
auto mod7 = [](int x)
{
return x % 7;
};
auto minimum = min_on(mod7, 2, 8, 17, 5);
assert( minimum == 8); // since 8%7 = 1 -> minimum value for all arguments passed
Now I would like to use this in a 'curried' way so that I can get a variadic lambda from min_on and then call it with arguments (that I might receive later), like so:
auto mod7 = [](int x)
{
return x % 7;
};
auto f_min = min_on(mod7);
auto minimum = f_min(2, 8, 17, 5);
// or
auto minimum = min_on(mod7)(2, 8, 17, 5);
Is this even possible?
In C++11, the following works if you’re willing to manually create the function object:
template <typename F>
struct min_on_t {
min_on_t(F f) : f(f) {}
template <typename T, typename... Ts>
auto operator ()(T x, Ts... xs) -> typename std::common_type<T, Ts...>::type
{
// Magic happens here.
return f(x);
}
private: F f;
};
template <typename F>
auto min_on(F f) -> min_on_t<F>
{
return min_on_t<F>{f};
}
And then call it:
auto minimum = min_on(mod7)(2, 8, 17, 5);
To use lambdas in C++14, you need to omit the trailing return type because you cannot specify the type of the lambda without assigning it to a variable first, because a lambda expression cannot occur in an unevaluated context.
template <typename F>
auto min_on(F f)
{
return [f](auto x, auto... xs) {
using rettype = std::common_type_t<decltype(x), decltype(xs)...>;
using f_rettype = decltype(f(x));
rettype result = x;
f_rettype result_trans = f(x);
(void)std::initializer_list<int>{
(f(xs) < result_trans
? (result = static_cast<rettype>(xs), result_trans = f(xs), 0)
: 0)...};
return result;
};
}
Not sure on C++11, but in C++14, you could create a lambda to wrap your function in:
auto min_on_t = [](auto f) {
return [=](auto ... params) {
return min_on(f, params...);
};
};
auto min_t = min_on_t(mod7);
auto minimum = min_t(2, 8, 17, 5);
Live on Coliru
In C++14 this is easy.
template<class F>
auto min_on( F&& f ) {
return [f=std::forward<F>(f)](auto&& arg0, auto&&...args) {
// call your function here, using decltype(args)(args) to perfect forward
};
}
Many compilers got auto return type deduction and arguments in lambdas working prior to full C++14 support. So a nominal C++11 compiler might be able to compile this:
auto min_on = [](auto&& f) {
return [f=decltype(f)(f)](auto&& arg0, auto&&...args) {
// call your function here, using decltype(args)(args) to perfect forward
};
}
in C++11:
struct min_on_helper {
template<class...Args>
auto operator()(Args&&...args)
-> decltype( min_on_impl(std::declval<Args>()...) )
{
return min_on_impl(std::forward<Args>(args)...);
}
};
is boilerplate. This lets us pass the entire overload set of min_on_impl around as one object.
template<class F, class T>
struct bind_1st_t {
F f;
T t;
template<class...Args>
typename std::result_of<F&(T&, Args...)>::type operator()(Args&&...args)&{
return f( t, std::forward<Args>(args)... );
}
template<class...Args>
typename std::result_of<F const&(T const&, Args...)>::type operator()(Args&&...args)const&{
return f( t, std::forward<Args>(args)... );
}
template<class...Args>
typename std::result_of<F(T, Args...)>::type operator()(Args&&...args)&&{
return std::move(f)( std::move(t), std::forward<Args>(args)... );
}
};
template<class F, class T>
bind_1st_t< typename std::decay<F>::type, typename std::decay<T>::type >
bind_1st( F&& f, T&& t ) {
return {std::forward<F>(f), std::forward<T>(t)};
}
gives us bind_1st.
template<class T>
auto min_on( T&& t )
-> decltype( bind_1st( min_on_helper{}, std::declval<T>() ) )
{
return bind_1st(min_on_helper{}, std::forward<T>(t));
}
is modular and solves your problem: both min_on_helper and bind_1st can be tested independently.
You can also replace bind_1st with a call to std::bind, but in my experience the quirks of std::bind make me extremely cautious about recommending that to anyone.

C++ variadic template partial specialization for non-type arguments

I have a map_n template that applies an N-arity function to each set of elements from N input tuples to produce a new output tuple. All input tuples must be of same length (which I should probably check with static assert).
The code works correctly, except I have not been able to write the recursion termination condition partial specialization in a generic way as shown in the following code snippet.
#include <tuple>
#include <cassert>
namespace impl {
// car, cdr, cons implementation
//
template<unsigned... XS>
struct sequence {
template<unsigned X>
using cons = sequence<X, XS...>;
};
template<unsigned start, unsigned end>
struct range {
static_assert(start < end, "Range: start > end");
using type = typename range<start + 1, end>::type::template cons<start>;
};
template<unsigned start>
struct range<start, start> {
using type = sequence<>;
};
template<typename T, unsigned... N>
auto select(const T& t, sequence<N...>) {
return std::make_tuple(std::get<N>(t)...);
}
} // end namespace impl
// car, cdr, cons
//
// empty list
//
constexpr const std::tuple<> empty;
// car
//
template<typename T>
auto car(const T& t) { return std::get<0>(t); }
// cdr
//
template<typename T, typename R = typename impl::range<1, std::tuple_size<T>::value>::type>
auto cdr(const T& t) {
return impl::select(t, R());
}
// cons
//
template<typename X, typename... XS>
auto cons(X x, const std::tuple<XS...>& t) {
return std::tuple_cat(std::make_tuple(x), t);
}
namespace impl {
// map_n implementation
template<typename F, typename... Ts>
struct map_n_impl {
static auto map(const F& f, const Ts&... t) {
return cons(
f(car(t)...),
map_n_impl<F, decltype(cdr(t))...>::map(f, cdr(t)...)
);
}
};
// NOTE: Need a more general specialization here
//
template<typename F>
struct map_n_impl<F, std::tuple<>, std::tuple<>> {
static std::tuple<> map(const F&, const std::tuple<>&, const std::tuple<>&)
{
return std::make_tuple();
}
};
} // end namespace impl
// map_n
//
template<typename F, typename... Ts>
auto map_n(const F& f, const Ts&... t) {
return impl::map_n_impl<F, Ts...>::map(f, t...);
}
int main(int, const char **) {
{
auto tup1 = std::make_tuple(1.0, 2.0, 3.0);
auto tup2 = std::make_tuple(0.0, 1.0, 2.0);
auto r = map_n([](auto x, auto y) { return x - y; }, tup1, tup2);
assert(std::get<0>(r) == 1.0);
assert(std::get<1>(r) == 1.0);
assert(std::get<2>(r) == 1.0);
}
// {
// auto tup1 = std::make_tuple(1.0, 2.0, 3.0);
// auto tup2 = std::make_tuple(0.0, 1.0, 2.0);
// auto tup3 = std::make_tuple(4.0, 5.0, 6.0);
// auto r = map_n([](auto x, auto y, auto z) { return x - y + z; }, tup1, tup2, tupe3);
// assert(std::get<0>(r) == 5.0);
// assert(std::get<1>(r) == 6.0);
// assert(std::get<2>(r) == 7.0);
// }
return 0;
}
It's a lot easier than what you're going for. You don't need map_n_impl at all. If we're going to stick with the functional, recursive approach - we need two overloads of map_n: one for all the tuples are non-empty and one for all the tuples are empty. We'll use Columbo's bool_pack trick to figure out if they're all empty or not:
template <bool... >
struct bool_pack;
template <bool... b>
using all_true = std::is_same<bool_pack<true, b...>, bool_pack<b..., true>>;
template <class... T>
using all_empty = all_true<std::is_same<T, std::tuple<>>::value...>;
And then just use that to SFINAE the two disjoint conditions:
template<typename F, typename... Ts,
std::enable_if_t<!all_empty<Ts...>::value, int*> = nullptr>
auto map_n(const F& f, const Ts&... t) {
return cons(
f(car(t)...),
map_n(f, cdr(t)...)
);
}
template<typename F, typename... Ts,
std::enable_if_t<all_empty<Ts...>::value, int*> = nullptr>
auto map_n(const F& , const Ts&... t) {
return std::make_tuple(t...);
}
Note that tuple isn't really the best way to do cons/car/cdr in C++ - it's not very cdr-able. More appropriate would be nested pairs.
You could also built up the whole tuple in one go with the index sequence trick. It's a little annoying here because we need to unpack two parameter packs differently, hence the extra call lambda. There may be a better way to do this:
template <size_t... Is, class F, class... Ts>
auto map_n_impl(std::index_sequence<Is...>, const F& f, const Ts&... ts) {
auto call = [&](auto idx){
return f(std::get<idx>(ts)...);
};
return std::make_tuple(
call(std::integral_constant<size_t, Is>{})...
);
}
template <class F, class T0, class... Ts>
auto map_n(const F& f, const T0& t0, const Ts&... ts) {
return map_n_impl(
std::make_index_sequence<std::tuple_size<T0>::value>{},
f,
t0,
ts...);
}