Find first element in tuple based on parameterized primitive - c++

I have a contrived example of what I would like to achieve.
#include <tuple>
#include <array>
template <int Val>
struct Value
{
static constexpr auto aValue = Val;
double arbitrary_operation() { return aValue * 2.0; };
};
struct Meta
{
auto do_calculation(std::floating_point auto x) { return x * 4.0; }
};
constexpr auto Values = std::tuple{Value<5>{}, Value<6>{}, Value<7>{}};
constexpr auto ValueMetadata = std::array<std::pair<int, Meta>, 3>{{{5, Meta{}}, {6, Meta{}}, {7, Meta{}}}};
void do_work()
{
std::array<double, ValueMetadata.size()> results;
for (auto& m : ValueMetadata)
{
// find first element in Values tuple that corresponds to parameterized integer (m.first == aValue)
auto value = get_first_element_in_values_tuple<m.first>(); // TODO
// perform arbitrary_operation on that Value and aggregate results
auto arbitrary_value = value.arbitrary_operation();
auto result = m.second.do_calculation(arbitrary_value);
// store in results array
}
}
The body of do_work is presenting some difficult - and I'm wondering where this can be performed at compile-time.

So I'm not sure what the templated Value class brings you. Why not make them non-template, then you can just make Values an std::array, and use constexpr algorithms from C++20?
Either way, if you insist on the tuple solution, here's one way you can create a tuple_find_if function with the help of C++20 constexpr algorithms and std::index_sequence:
template<typename Tuple, typename Func, std::size_t ... N>
consteval auto tuple_find_if_impl(Tuple tup, Func func, std::index_sequence<N...>)
{
constexpr auto compares = std::array{func(std::get<N>(tup))...};
constexpr auto index = std::ranges::find(compares, true) - compares.begin();
return std::get<index>(tup);
}
template<typename ... Ts, typename Func>
consteval auto tuple_find_if(std::tuple<Ts...> tup, Func func)
{
return tuple_find_if_impl(tup, func, std::make_index_sequence<sizeof...(Ts)>{});
}
Demo: https://godbolt.org/z/cEfPoPbdc
And here's a recursive version that works with C++17:
template<typename T, typename ... Ts, typename Func, std::size_t ... N>
constexpr auto tuple_find_if_impl(std::tuple<T, Ts...> tup, Func func, std::index_sequence<N...>)
{
if constexpr(func(std::get<0>(tup)))
{
return std::get<0>(tup);
}
else
{
return tuple_find_if_impl(std::make_tuple(std::get<N + 1>(tup)...), func, std::make_index_sequence<sizeof...(Ts) - 1>{});
}
}
template<typename ... Ts, typename Func>
constexpr auto tuple_find_if(std::tuple<Ts...> tup, Func func)
{
return tuple_find_if_impl(tup, func, std::make_index_sequence<sizeof...(Ts) - 1>{});
}
Demo: https://godbolt.org/z/sc5Txronh

You can first convert the tuple to an array and use std::find to find the appropriate index, then use variant to type-erasure the index.
#include <algorithm>
#include <variant>
template<std::size_t N>
constexpr auto VarIndexArray = []<std::size_t... Is>(std::index_sequence<Is...>) {
using VarType = std::variant<std::integral_constant<std::size_t, Is>...>;
return std::array{VarType{std::in_place_index<Is>}...};
}(std::make_index_sequence<N>{});
void do_work() {
std::array<double, ValueMetadata.size()> results;
constexpr auto ValueArray = std::apply(
[](auto... vals) { return std::array{decltype(vals)::aValue...}; }, Values);
constexpr auto IndexArray = VarIndexArray<std::tuple_size_v<decltype(Values)>>;
for (auto& m : ValueMetadata) {
// find first element in Values tuple that corresponds to parameterized integer
auto index = std::ranges::find(ValueArray, m.first) - ValueArray.begin();
auto result = std::visit(
[&m](auto index) {
auto arbitrary_value = std::get<index>(Values).arbitrary_operation();
auto result = m.second.do_calculation(arbitrary_value);
return result;
}, IndexArray[index]);
}
}
Demo

I may be misinterpreting your constraints, but if I understand correctly, you want to find the matching tuple element for each pair in your array, then aggregate the results. Depending on exactly what you want there's certainly better solutions using fold expressions and index sequences, but I believe I have something that works and produces a compile time result:
live example
template <int I, std::size_t TupleIndex>
consteval double find_match() {
// Compare I to the current aValue, if they match, return the val.
if constexpr (I == std::get<TupleIndex>(Values).aValue) {
return std::get<TupleIndex>(Values).val();
} else {
// Otherwise, move onto the next element of the tuple.
return find_match<I, TupleIndex+1>();
}
}
template <std::size_t I>
consteval double accumulate_matches() {
if constexpr (I == ValueMetadata.size()) {
return 0;
} else {
// Change this expression to accumulate with something
// other than a simple summation.
// 0 to start searching at the beginning of the tuple.
// +1 in the recursive call to get to the next element of ValueMetadata
return find_match<ValueMetadata[I].first, 0>()
+ accumulate_matches<I+1>();
}
}
consteval double get_total() {
return accumulate_matches<0>();
}

Related

How to remove an element from a pack in C++?

I'm trying to remove an element from a C++ pack. It's a hard to explain with words so I will just show you what I want in code.
// lets say I have the following function
template<typename... Args>
void foo1(Args... arguments)
{
// does something with the arguments
}
// and another similar function which needs to call foo1 but with a modified pack
template<typename... Args>
void foo2(Args... arguments)
{
// foo2 figures out what arguments should be removed from the "arguments" pack
// and here comes the hard part, after I know the indices of what elements to remove, how do I remove them from the pack?
// then foo2 calls foo1 with the new pack (modified argument list)
foo1(new_arguments...);
}
I want a pure C++ solution without including any files because it should work for kernel mode and you can't include any standard C++ library in kernel mode.
Any ideas how to do it?
EDIT:
The indices are constexpr integer values so I can use them in templates or anything like that.
Here's a C++20 solution without using any standard library headers.
It defines a type trait take which collects a list of N types from the front of a pack, and then uses the list to define a lambda that partitions the arguments of foo2 and drops the Nth index at each recursion until no drop indices are left before delegating to foo1.
namespace detail {
template <class...>
struct list {};
template <int, class, class = list<>>
struct take;
template <class Drop, class Take>
struct take<0, Drop, Take> {
using type = Take;
};
template <int N, class T, class... Drop, class... Take>
requires(N > 0)
struct take<N, list<T, Drop...>, list<Take...>>
: take<N - 1, list<Drop...>, list<Take..., T>> {};
} // namespace detail
template <class... Args>
void foo2(Args... new_arguments) {
foo1(new_arguments...);
}
template <int Index, int... Indices, class... Args>
void foo2(Args... arguments) {
[&]<class... Take>(detail::list<Take...>) {
[](Take... take, auto, auto... rest) {
foo2<(Indices - 1)...>(take..., rest...);
}(arguments...);
}(typename detail::take<Index, detail::list<Args...>>::type{});
}
I found a solution, it's not exactly what I wanted because it uses std::tuple but it's good enough for now. Here is the code:
#include <tuple>
struct remove_element_from_pack
{
private:
template<typename Ty, typename... Args>
__forceinline constexpr static Ty get_first_val(Ty val, Args... other) { return val; }
template<typename Ty, typename... Args>
__forceinline constexpr static auto remove_first_from_tuple(Ty first, Args... rest)
{
return std::tuple<Args...>(rest...);
}
template<typename return_ty, typename... Args>
__forceinline constexpr static return_ty call_func_internal(const void* function, Args... arguments)
{
return ((return_ty(__fastcall*)(Args...))function)(arguments...);
}
public:
template<
typename return_ty,
int current_index,
int remove_current,
int... remove,
typename current_ty,
typename... All,
typename... Processed>
__forceinline static return_ty call_func(const void* function, std::tuple<current_ty, All...> all, std::tuple<Processed...> processed)
{
auto current = std::apply([](auto&&... args)->auto { return get_first_val(args...); }, all);
// if there are no more elements
if constexpr (!sizeof...(All))
{
// if we have to remove the current element
if constexpr (remove_current != -1 && is_in_pack<int, current_index, remove_current, remove...>::value)
{
return std::apply(
[](auto &&... args)->return_ty { return call_func_internal<return_ty>(args...); },
std::tuple_cat(std::make_tuple(function), processed)
);
}
else
{
return std::apply(
[](auto &&... args)->return_ty { return call_func_internal<return_ty>(args...); },
std::tuple_cat(std::make_tuple(function), std::tuple_cat(processed, std::make_tuple(current)))
);
}
}
else
{
auto new_all = std::apply([](auto&&... args)->auto { return remove_first_from_tuple(args...); }, all);
// if we have to remove the current element
if constexpr (remove_current != -1 && is_in_pack<int, current_index, remove_current, remove...>::value)
{
// if there are any elements left to remove
if constexpr (sizeof...(remove) > 0)
{
return std::apply(
[](auto &&... args)->return_ty { return call_func<return_ty, current_index + 1, remove...>(args...); },
std::tuple_cat(std::make_tuple(function), std::make_tuple(new_all), std::make_tuple(processed))
);
}
else
{
return std::apply(
[](auto &&... args)->return_ty { return call_func<return_ty, current_index + 1, -1>(args...); },
std::tuple_cat(std::make_tuple(function), std::make_tuple(new_all), std::make_tuple(processed))
);
}
}
else
{
auto new_processed = std::tuple_cat(processed, std::make_tuple(current));
return std::apply(
[](auto &&... args)->return_ty { return call_func<return_ty, current_index + 1, remove_current, remove...>(args...); },
std::tuple_cat(std::make_tuple(function), std::make_tuple(new_all), std::make_tuple(new_processed))
);
}
}
}
};
Then you can call the function for example like this:
// target function
int __fastcall add2(double a, double b)
{
return a + b;
}
// call
remove_element_from_pack::call_func<int, 0, 0, 3>(
add2, std::tuple<double, double, double, double>(20.0, 30.0, 40.0, 29.0), std::tuple()
);
In this example the 0 and 3rd element will be removed from the pack (first tuple in the function call) which means 20.0 and 29.0 will be excluded and add2 will be called with 30.0 and 40.0.
EDIT:
I forgot to post this part of the code:
template<typename Ty, Ty element, Ty first, Ty... rest_of_pack>
struct is_in_pack
{
private:
__forceinline static constexpr bool get_value_internal()
{
if constexpr (first == element)
return true;
else if constexpr (!sizeof...(rest_of_pack))
return false;
else
return is_in_pack<Ty, element, rest_of_pack...>::value;
}
public:
static constexpr const bool value = get_value_internal();
};

accessing std::tuple element by constexpr in type

I would like to access to a tuple element at compile time by a value constexpr in the type
#include <iostream>
#include <tuple>
#include <utility>
struct A {
static constexpr int id = 1;
void work() {
std::cout << "A" << std::endl;
}
};
struct B {
static constexpr int id = 2;
void work() {
std::cout << "B" << std::endl;
}
};
int main() {
A a;
B b;
std::tuple<A,B> t = std::make_tuple(a,b);
static constexpr int search_id = 2;
auto& item = std::get< ? ( T::id == search_id ) ? >(t);
item.work();
return 0;
}
I guess using std::apply and test would be a runtime search...
I'm using c++20
Instead of std::get a single element, you can use std::apply to iterate over the elements of the tuple and perform operations based on the element type
A a;
B b;
auto t = std::make_tuple(a, b);
static constexpr int search_id = 2;
std::apply([](auto&... items) {
([]<class T>(T& item) {
if constexpr (T::id == search_id)
item.work();
}(items), ...);
}, t);
Demo
If you really want to get a single tuple element with a specific id value, you can still use std::apply to expand the id of all elements and find the offset of the value equal to search_id as the template parameter of std::get
auto& item = std::apply([&t]<class... Args>(const Args&... items) -> auto& {
constexpr auto id = [] {
std::array ids{Args::id...};
return ids.end() - std::ranges::find(ids, search_id);
}();
return std::get<id>(t);
}, t);
item.work();
You can create constrexpr function to get index:
template <typename... Ts>
constexpr std::size_t get_index(int id)
{
constexpr int ids[] = {Ts::id...};
const auto it = std::find(std::begin(ids), std::end(ids), id);
// Handle absent id.
if (it == std::end(ids)) {
throw std::runtime("Invalid id");
}
// You can also possibly handle duplicate ids.
return std::distance(std::begin(ids), it);
}
template <int id, typename... Ts>
constexpr auto& get_item(std::tuple<Ts...>& t)
{
return std::get<get_index<Ts...>(id)>(t);
}
template <int id, typename... Ts>
constexpr const auto& get_item(const std::tuple<Ts...>& t)
{
return std::get<get_index<Ts...>(id)>(t);
}
and then
auto& item = get_item<search_id>(t);
This is a prime candidate for std::disjunction, which can be used to perform a compile-time linear search; you just need a helper type to act as the predicate:
namespace detail {
template<typename T, auto Id, auto I, typename U = std::tuple_element_t<I, T>>
struct get_by_id_pred : std::bool_constant<std::remove_cvref_t<U>::id == Id> {
static constexpr auto index = I;
};
}
template<int Id>
constexpr auto&& get_by_id(auto&& t) noexcept {
using tuple_t = std::remove_cvref_t<decltype(t)>;
return [&]<auto ...Is>(std::index_sequence<Is...>) -> auto&& {
using res = std::disjunction<detail::get_by_id_pred<tuple_t, Id, Is>...>;
static_assert(res::value, "id not found");
return std::get<res::index>(decltype(t)(t));
}(std::make_index_sequence<std::tuple_size_v<tuple_t>>{});
}
...
auto& item = get_by_id<search_id>(t);
Online Demo

Replace N-th element of a std::tuple

What is the shortest / best way to replace the n-th element of a tuple with a value (which may or may not have a different type)? Solutions including c++20 are fine.
[EDIT: I would prefer something not requiring other libraries, but I'm still interested what solutions are possible with e.g. boost].
I.e.:
#include <cassert>
#include <tuple>
template<std::size_t N, ... >
auto replace_tuple_element( ... ) // <- Looking for a suitable implementation
struct Foo {
int value;
};
int main()
{
auto t1 = std::tuple{ 0, 1, 2, 3 };
auto t2 = replace_tuple_element<2>( t1, Foo{10} );
assert( std::get<0>(t2) == std::get<0>(t1));
assert( std::get<1>(t2) == std::get<1>(t1));
assert( std::get<2>(t2).value == 10);
assert( std::get<3>(t2) == std::get<3>(t1));
}
Note: Just replacing the n-th type in a typelist has e.g. be discussed here: How do I replace a tuple element at compile time?.
But I also want to replace the value and hope that there are simpler/more elegant solutions now in c++20 than back when that question was asked.
One solution I found for c++20 is this:
#include <cassert>
#include <tuple>
#include <type_traits>
template<std::size_t N, class TupleT, class NewT>
constexpr auto replace_tuple_element( const TupleT& t, const NewT& n )
{
constexpr auto tail_size = std::tuple_size<TupleT>::value - N - 1;
return [&]<std::size_t... I_head, std::size_t... I_tail>
( std::index_sequence<I_head...>, std::index_sequence<I_tail...> )
{
return std::tuple{
std::get<I_head>( t )...,
n,
std::get<I_tail + N + 1>( t )...
};
}(
std::make_index_sequence<N>{},
std::make_index_sequence<tail_size>{}
);
}
struct Foo {
int value;
};
int main()
{
auto t1 = std::tuple{ 0, 1, 2, 3 };
auto t2 = replace_tuple_element<2>( t1, Foo{10} );
assert( std::get<0>(t2) == std::get<0>(t1));
assert( std::get<1>(t2) == std::get<1>(t1));
assert( std::get<2>(t2).value == 10);
assert( std::get<3>(t2) == std::get<3>(t1));
}
What I like about the solution is that it is a single, self containied function. I wonder if there is something even shorter and/or more readable though.
Possible solution:
template<std::size_t i>
using index = std::integral_constant<std::size_t, i>;
template<std::size_t N, class Tuple, typename S>
auto replace_tuple_element(Tuple&& tuple, S&& s) {
auto get_element = [&tuple, &s]<std::size_t i>(Index<i>) {
if constexpr (i == N)
return std::forward<S>(s);
else
return std::get<i>(std::forward<Tuple>(tuple));
};
using T = std::remove_reference_t<Tuple>;
return [&get_element]<std::size_t... is>(std::index_sequence<is...>) {
return std::make_tuple(get_element(index<is>{})...);
}(std::make_index_sequence<std::tuple_size_v<T>>{});
}
Note this decays all element types, removing references and const.
This amendment partially addresses this issue:
template<std::size_t N, class Tuple, typename S>
auto replace_tuple_element(Tuple&& tuple, S&& s) {
using T = std::remove_reference_t<Tuple>;
auto get_element = [&tuple, &s]<std::size_t i>(index<i>) {
if constexpr (i == N)
return std::forward<S>(s);
else
if constexpr (std::is_lvalue_reference_v<std::tuple_element_t<i, T>>)
return std::ref(std::get<i>(std::forward<Tuple>(tuple)));
else
return std::get<i>(std::forward<Tuple>(tuple));
};
return [&get_element]<std::size_t... is>(std::index_sequence<is...>) {
return std::make_tuple(get_element(index<is>{})...);
}(std::make_index_sequence<std::tuple_size_v<T>>{});
}
Now replace_tuple_element also follows the convention of std::make_tuple that converts std::reference_wrapper arguments into references. It does preserve reference types, but drops top-level constness.
struct Foo {
Foo(int i) : value(i) {}
int value;
};
int main() {
int i = 1;
int j = 2;
auto t1 = std::make_tuple(std::make_unique<Foo>(0), std::ref(i), std::cref(j), 4);
static_assert(std::is_same_v<decltype(t1),
std::tuple<std::unique_ptr<Foo>, int&, const int&, int>>);
auto t2 = replace_tuple_element<1>(std::move(t1), std::make_unique<Foo>(5));
static_assert(std::is_same_v<decltype(t2),
std::tuple<std::unique_ptr<Foo>, std::unique_ptr<Foo>, const int&, int>>);
auto t3 = replace_tuple_element<0>(std::move(t2), std::cref(i));
static_assert(std::is_same_v<decltype(t3),
std::tuple<const int&, std::unique_ptr<Foo>, const int&, int>>);
auto t4 = replace_tuple_element<2>(std::move(t3), i);
static_assert(std::is_same_v<decltype(t4),
std::tuple<const int&, std::unique_ptr<Foo>, int, int>>);
}
Full demo with run-time asserts
This should do it:
template<std::size_t N, class U, class T>
auto replace_tuple_element(T&& t, U&& u) {
return [&]<std::size_t... I>(std::index_sequence<I...>) {
return std::tuple<std::conditional_t<I == N, U, std::tuple_element_t<I, std::decay_t<T>>>...>{
[&]() -> decltype(auto) {
if constexpr (I == N) return std::forward<U>(u);
else return static_cast<std::tuple_element_t<I, std::decay_t<T>>>(std::get<I>(t));
}()...};
}(std::make_index_sequence<std::tuple_size_v<std::decay_t<T>>>{});
}
You can remove some of the casts, forwards etc. if you're only concerned with value semantics.
The only thing new here is lambda template parameters to infer the indexing argument.
If we want to both preserve all the types exactly as they are, and also do the same kind of reference unwrapping thing that the standard library typically does, then we need to make a small change to what the other implementations are here.
unwrap_ref_decay will do a decay_t on the type, and then turn reference_wrapper<T> into T&. And using Boost.Mp11 for a few things that just make everything nicer:
template <size_t N, typename OldTuple, typename NewType>
constexpr auto replace_tuple_element(OldTuple&& tuple, NewType&& elem)
{
using Old = std::remove_cvref_t<OldTuple>;
using R = mp_replace_at_c<Old, N, std::unwrap_ref_decay_t<NewType>>;
static constexpr auto Size = mp_size<Old>::value;
auto get_nth = [&](auto I) -> decltype(auto) {
if constexpr (I == N) return std::forward<NewType>(elem);
else return std::get<I>(std::forward<OldTuple>(tuple));
};
return [&]<size_t... Is>(std::index_sequence<Is...>) {
return R(get_nth(mp_size_t<Is>())...);
}(std::make_index_sequence<Size>());
}
This implementation means that given:
std::tuple<int const, int const> x(1, 2);
int i = 42;
auto y = replace_tuple_element<1>(x, std::ref(i));
y is a tuple<int const, int&>.
This is a good use case for a counterpart of tuple_cat that, instead of concatenating tuples, gives you a slices of a tuple. Unfortunately, this doesn't exist in the standard library, so we'll have to write it ourselves:
template <std::size_t Begin, std::size_t End, typename Tuple>
constexpr auto tuple_slice(Tuple&& t)
{
return [&]<std::size_t... Ids> (std::index_sequence<Ids...>)
{
return std::tuple<std::tuple_element_t<Ids, std::remove_reference_t<Tuple>>...>
{std::get<Begin + Ids>(std::forward<Tuple>(t))...};
} (std::make_index_sequence<End - Begin>{});
}
Just like tuple_cat, this preserve the exact same types of the original tuple.
With tuple_cat and tuple_slice, the implementation of replace_tuple_element feels quite elegant:
template <std::size_t N, typename Tuple, typename T>
constexpr auto replace_tuple_element(Tuple&& tuple, T&& t)
{
constexpr auto Size = std::tuple_size_v<std::remove_reference_t<Tuple>>;
return std::tuple_cat(
tuple_slice<0, N>(std::forward<Tuple>(tuple)),
std::make_tuple(std::forward<T>(t)),
tuple_slice<N + 1, Size>(std::forward<Tuple>(tuple))
);
}
Using make_tuple preserves the behavior of turning reference_wrapper<T> into T&. Demo

C++17 multiple parameter pack expansion

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);
}

Filling a std::tuple

I have a overloaded function which looks like:
template<typename T>
T getColumn(size_t i);
template<>
std::string getColumn<std::string>(size_t i) {
if(i == 0)
return "first";
else
return "other";
}
template<>
int getColumn<int>(size_t i) {
return i*10;
}
// ...
Now I want to implement the function
template<typename... Values>
std::tuple<Values...> getColumns();
Which creates a tuple (for the return value) and calls getColumn for every element of the tuple (saving the return value in that element), where i is the position of the element. The code which generates the return value of getColumn is simplified (in reality it gets the value from a database).
But I have no idea how to do that.
My best try was with boost::fusion::for_each but I wasn't able to hand i down to getColumn.
Another try was with the iterators from boost::fusion, but that also didn't work:
namespace fusion = boost::fusion;
tuple<Values...> t;
auto it = fusion::begin(t);
while(it != fusion::end(t)) {
getColumn(distance(fusion::begin(t), it), fusion::deref(it));
it = fusion::next(it); // error: assignment not allowed
}
How can I call getColumn for every Type from Values... with the correct value for i and save the results in a std::tuple?
You need to map each element of a parameter pack to its index within the pack - this is the typical use case for the "index sequence trick":
template <int... I> struct index_sequence {};
template <int N, int... I>
struct make_index_sequence : make_index_sequence<N-1,N-1,I...> {};
template <int... I>
struct make_index_sequence<0, I...> : index_sequence<I...> {};
template<typename... Values, int... I>
auto getColumns(index_sequence<I...>) ->
decltype(std::make_tuple(getColumn<Values>(I)...)) {
return std::make_tuple(getColumn<Values>(I)...);
}
template<typename... Values>
auto getColumns() ->
decltype(getColumns<Values...>(make_index_sequence<sizeof...(Values)>())) {
return getColumns<Values...>(make_index_sequence<sizeof...(Values)>());
}
Live demo at Coliru.
Maybe with auto:
template<typename... Values>
auto getColumns() -> decltype(std::make_tuple(getColumns<Values>()...))
{
return std::make_tuple(getColumns<Values>()...);
}
In C++14, you'll be able to omit the -> decltype... part, since it will be deduced from the function body.