C++ subtuple from tuple given variadic index sequence - c++

Assume I have a std::tuple, I'd like to write a function which receives a tuple and a variadic sequence outputting a subtuple containing the columns corresponding to those indexes.
Example usecase:
std::tuple<int, char, float, std::string> t{1, 'd', 3.14, "aaa"};
auto subtuple = extract_subtuple(t, 0, 2);
// returns std::tuple<int, float>(1, 3.14)
Is this possible ?
I had a look at this other question but in the end I didn't find what I was looking for.

You can't do it directly because tuple indices should be constant expressions and function parameters are never constant expressions even if corresponding arguments are. You have two main options.
Firstly, you could make indices template parameters:
template<std::size_t... Is, class... Ts>
auto extract_subtuple(const std::tuple<Ts...>& tuple) {
return std::make_tuple(std::get<Is>(tuple)...);
}
auto subtuple = extract_subtuple<0, 1>(t);
Secondly, if you want to make indices function parameters, you could wrap them into types:
template<class... Ts, class... Indices>
auto extract_subtuple(const std::tuple<Ts...>& tuple, Indices...) {
return std::make_tuple(std::get<Indices::value>(tuple)...);
}
template<std::size_t I>
using Idx = std::integral_constant<std::size_t, I>;
auto subtuple = extract_subtuple(t, Idx<0>{}, Idx<1>{});

Related

C++ SFINAE to determine the number of function arguments (C++17)

I created a metafunction using SFINAE to determine the number of arguments of a function at compile time. It works fine with gcc when used with with function objects, but not with lambda closures, I don't understand why. The metafunction is here below
template < typename T >
int val (T &&){return 0;};
template <int N, typename Functor>
struct has_args {
template <typename F , int ... Args>
static auto test(F, decltype(val(std::declval<F>()( Args ... ))), std::integer_sequence<int, Args ...>){
return std::true_type{};
};
template <typename F, typename Val, typename Seq>
static auto test(F, Val, Seq){
return std::false_type{};
};
using type = decltype (test(std::declval<Functor>(), 0, std::make_integer_sequence<int, N>()));
};
and here is how it should behave
struct func{
template<typename T>
int operator()(T){}
};
int main(){
auto lambda0 = [](auto arg){};
static_assert(has_arg<1, func>::type::value==true, "error");
//static_assert(has_arg<1, decltype(lambda0)>::type::value==true, "error"); // Assertion fails!
}
The full code (with few more examples) is in this git repo: https://github.com/crosetto/has_args/blob/main/number_of_arguments.cpp
Does anybody have an explanation of why this doesn't work with lambdas?
As pointed out in the comment by #rafix07, the issue here is that the lambda is returning void, so it's signature is not matched in the first definition of test, and falls back to the other overload. One fix is to apply a comma operator to the argument of val, i.e. changing
static auto test(F, decltype(val(std::declval<F>()( Args ... ))), std::integer_sequence<int, Args ...>)
into
static auto test(F, decltype(val((std::declval<F>()( Args ... ),0))), std::integer_sequence<int, Args ...>)
or, as pointed out by #Jarod42 in the comments, val is unnecessary, and one can write:
static auto test(F, decltype(((std::declval<F>()( Args ... ),void(),0))), std::integer_sequence<int, Args ...>)
Note that the function body gets parsed, and any use of the arguments which wouldn't compile with integers results in a compiler error. I think this cannot be worked around in a generic way (one can use a fake type other than int though, and make it satisfy the required API).

Extract data from heterogeneous list

I was looking at this answer about the advantages of auto in template parameter.
Please consider the following heterogeneous list:
template <auto ... vs> struct HeterogenousValueList {};
using MyList1 = HeterogenousValueList<42, 'X', 13u>;
Now I've declared a type named MyList1. How can I extract the stored data from this type (i.e., 42, 'x' or 13u)?
To extract data from a template parameter pack, we usually do pattern matching in template.
Firstly, we create a class template At but without contents. Its template parameters are supposed to be an index, and an instance of HeterogenousValueList. This class template will be used like a function to access information in the list.
template <int Index, class ValueList>
struct At;
Next, we create a specialization of At. This is where pattern matching is used. Through pattern matching, the first element of the list will become u. The rest of the list will be vs. If the index is 0, u can be accessed through the static member value. Note that vs can be an empty list, so this also covers the case that u being the last of the list.
template <auto u, auto... vs>
struct At<0, HeterogenousValueList<u, vs...>>
{
static constexpr auto value = u;
};
What if the index is not 0? We shift the list and decrement the index by 1, and pass them into At again. In other words, this is a template recursion.
template <int Index, auto u, auto... vs>
struct At<Index, HeterogenousValueList<u, vs...>>
{
static constexpr auto value = At<Index - 1, HeterogenousValueList<vs...>>::value;
};
Now, we can try to use it: https://godbolt.org/g/51dpH8
int main()
{
volatile auto value0 = At<0, MyList1>::value;
volatile auto value1 = At<1, MyList1>::value;
volatile auto value2 = At<2, MyList1>::value;
// volatile auto value3 = At<-1, MyList1>::value;
// volatile auto value4 = At<3, MyList1>::value;
}
I use volatile variable so that the compiler does not optimize the effect away and you can see the effect in the assembly listing.
And one more great thing: the compiler checks the bound! We usually don't have bound check for run-time array for run-time efficiency reason. But this is a compile-time list. The compiler can do it for us!
Actually, there is a simpler implementation. This time, we use constexpr-if in a function template. But the idea of pattern matching and template recursion remain the same.
template <int Index, auto u, auto... vs>
auto at(HeterogenousValueList<u, vs...>)
{
if constexpr (Index == 0)
return u;
else
return at<Index - 1>(HeterogenousValueList<vs...>{});
}
And this time, when we use it, we need to instantiate MyList1 into an object.
https://godbolt.org/g/CA3VHj
int main()
{
volatile auto value0 = at<0>(MyList1{});
volatile auto value1 = at<1>(MyList1{});
volatile auto value2 = at<2>(MyList1{});
// volatile auto value3 = at<-1, MyList1>::value;
// volatile auto value4 = at<3, MyList1>::value;
}
As you mention "playing" and "learning it by manipulation", you may be interested in several alternatives.
First solution: convert to std::tuple and then std::get
The std::tuple, which is a container for heterogeneous values, can be utilized to access the elements of your heterogeneous value list. This is a simple two-step process:
convert HeterogenousValueList<42, 'X', 13u>{} to std::tuple<int, char, unsigned>{42, 'X', 13u}
access the tuple's values at the desired positions by std::get
Complete C++17 example:
#include <type_traits>
#include <tuple>
template<auto... vs>
struct HeterogenousValueList {};
template<int i, auto... vs>
constexpr auto get(HeterogenousValueList<vs...>) {
constexpr std::tuple tuple{vs...};// class-template argument deduction
static_assert(std::is_same<
decltype(tuple), const std::tuple<int, char, unsigned>
>{});
return std::get<i>(tuple);
}
int main() {
using MyList1 = HeterogenousValueList<42, 'X', 13u>;
constexpr auto at1 = get<1>(MyList1{});
static_assert(at1 == 'X');
static_assert(std::is_same<decltype(at1), const char>{});
}
Second solution: wrap in types for std::tuple_element_t
Another useful idiom is to wrap non-type template parameters in a single empty type. In your example, this allows to make use of std::tuple_element_t, which can yield the nth type of a variadic pack:
#include <type_traits>
#include <tuple>
template<auto... vs>
struct HeterogenousValueList {};
template<auto v_>
struct SingleValue {// similar to std::integral_constant, but uses auto
static constexpr auto v = v_;
};
template<int i, auto... vs>
constexpr auto get(HeterogenousValueList<vs...>) {
using TupleOfSingleValues = std::tuple<SingleValue<vs>...>;
using SingleValueAtPosition = std::tuple_element_t<i, TupleOfSingleValues>;
return SingleValueAtPosition::v;
// return std::tuple_element_t<i, std::tuple<SingleValue<vs>...>>::v;// same
}
// same `main` as first solution
Third solution: Implement your own logic
There are several ways to implement your own version for "getting the nth type/element". One aspect in this business is the compile-time performance: particularly the recursive strategies are said to cause long compilation times.
My favorite non-recursive strategy is the one which is also used in Boost.Hana. If you prefer a video explanation you can watch two minutes of the talk "Metaprogramming for the brave" by Louis Dionne (Boost.Hana author) starting at 01 h 12 min. The idea is to use multiple inheritance. In your example, HeterogenousList<42, 'X', 13u> can have the base classes IndexedValue<0, 42>, IndexedValue<1, 'X'>, and IndexedValue<2, 13u>. Then you can pass the HeterogenousList<42, 'X', 13u> to a templated get function that takes a, say, const IndexedValue<1, [[DEDUCED]]>& as argument:
#include <type_traits>
#include <utility>
template<std::size_t i, auto v_>
struct IndexedValue {
static constexpr auto v = v_;
};
template<class Is, auto... vs>
struct IndexedHeterogenousList;
template<std::size_t... is, auto... vs>
struct IndexedHeterogenousList<
std::index_sequence<is...>,// partial specialization to extract the `is`
vs...
> : IndexedValue<is, vs>...// multiple "variadic" inheritance
{};
template<auto... vs>
struct HeterogenousValueList
: IndexedHeterogenousList<std::make_index_sequence<sizeof...(vs)>, vs...>
{};
template<std::size_t i, auto v>// `i` must be given; `v` is deduced
constexpr auto get(const IndexedValue<i, v>& iv) {// one base matches
return v;
}
// same `main` as first solution

Function to generate a tuple given a size N and a type T

While trying to reply to this question, I found my self in the need of creating a bunch of parameters for a variadic function on the fly where:
the number of the parameters is not given
the types are all the same, but unknown (even if they must be default constructible)
At runtime, the standard containers and a for loop can be used to do that.
Anyway, I'd like to generate a set of parameters at compile time, so as to be able to forward them to a variadic function.
Because of that, a std::tuple seemed the obvious solution.
Here arose the question: given a size N and a default constructible type T at compile time, how can I write a function to generate a tuple of the given size?
I'm looking for something like this:
auto tup = gen<MyType, N>();
On SO is a notable example of a recursive generator based structs but I was struggling with a function based solution and I've not been able to find it anywhere.
A correctly written forwarding function (a la std::apply) should work with std::array<T, N> and anything else that implements the std::tuple_size/std::get interface. That said,
template<size_t, class T>
using T_ = T;
template<class T, size_t... Is>
auto gen(std::index_sequence<Is...>) { return std::tuple<T_<Is, T>...>{}; }
template<class T, size_t N>
auto gen() { return gen<T>(std::make_index_sequence<N>{}); }
Here is a possible implementation of such a function:
#include<utility>
#include<tuple>
template<typename T>
constexpr auto
params(std::index_sequence<0>) {
return std::tuple<T>{};
}
template<typename T, std::size_t I, std::size_t... O>
constexpr auto
params(std::index_sequence<I, O...>) {
auto tup = std::tuple<T>{ T{} };
auto seq = std::make_index_sequence<sizeof...(O)>{};
return std::tuple_cat(tup, params<T>(seq));
}
template<typename T, std::size_t N>
constexpr auto
gen(std::integral_constant<std::size_t, N>) {
return params<T>(std::make_index_sequence<N>{});
}
int main() {
auto tup = gen<int>(std::integral_constant<std::size_t, 3>{});
static_assert(std::tuple_size<decltype(tup)>::value == 3, "!");
}
For the sake of simplicity, I've used int as a type.
With a small effort, user defined types can be used and the constraint of having them default constructible can be relaxed.

Distribute argument parameter pack to invoke two functors

I'm trying to invoke two functional objects through one given argument pack (typename Args... args), an integer parameter is provided to mark the border where i need to split the pack to invoke both functional objects correctly.
Consider the following example:
Args... = <int, int, std::vector<int>, std::vector<int>>
unsigned Bounds = 2;
functor Foo (left) and Bar (right)
// Foo is invoked with <int, int>
// Bar is invoked with <std::vector<int>, std::vector<int>>
// An evaluator template class is invoked to merge the result of both,
// for example with an add (operator+) operation
My idea was to create two integer sequences and use std::get to invoke both functional objects at once with those two integer sequences:
// Sequence creator
template<unsigned Position, unsigned Count, unsigned... Pack>
struct make_sequence
: std::conditional<
Count == 0,
std::common_type<sequence<Pack...>>,
make_sequence<Position + 1, Count - 1, Pack..., Position>
>::type { };
// Create a sequence from inclusive from to exclusive to
template<unsigned InclusiveFrom, unsigned ExclusiveTo>
using make_sequence_from_to_t = typename make_sequence<
InclusiveFrom,
(ExclusiveTo <= InclusiveFrom) ? 0U : (ExclusiveTo - InclusiveFrom)
>::type;
template<typename LeftType, typename RightType, unsigned Bounds, typename Evaluator>
class distribute_functor
{
LeftType left_;
RightType right_;
template<unsigned... LeftSeq, unsigned... RightSeq, typename... Args>
auto internal_invoke(sequence<LeftSeq...>, sequence<RightSeq...>, Args... args)
{
return Evaluator::evaluate(left_(std::get<LeftSeq>(args)...),
// ~~~~~~~~~~~~~~~^^^^^^^~~^^^^~~~~~
// error C3528: 'LeftSeq': the number of
// elements in this pack expansion does not
// match the number of elements in 'args'
right_(std::get<RightSeq>(args)...));
}
public:
template<typename Left, typename Right>
distribute_functor(Left left, Right right)
: left_(std::forward<Left>(left)), right_(std::forward<Right>(right)) { }
template<typename... Args>
auto operator() (Args... args)
{
return internal_invoke(make_sequence_from_to_t<0, Bounds>{},
make_sequence_from_to_t<Bounds, sizeof...(Args)>{},
std::forward<Args>(args)...);
}
};
However the VisualStudio 14 compiler complains about a mismatch between the count of parameters in the arguments pack and in the sequence:
error C3528: 'LeftSeq': the number of elements in this pack expansion does not match the number of elements in 'args'
There is still the way to use std::tuple for the functor invocation which i don't prefer.
Is there another or better way to partial invoke two functional objects in one step from one argument pack?
std::get cannot be used this way.
You should write internal_invoke like this:
template<unsigned... LeftSeq, unsigned... RightSeq, typename ArgsAsTuple>
auto internal_invoke(sequence<LeftSeq...>, sequence<RightSeq...>,ArgsAsTuple&& args) const
{
return Evaluator::evaluate(left_(std::get<LeftSeq>(args)...),
right_(std::get<RightSeq>(args)...));
}
And invoke it with forward_as_tuple:
return internal_invoke(make_sequence_from_to_t<0, Bounds>{},
make_sequence_from_to_t<Bounds, sizeof...(Args)>{},
std::forward_as_tuple(args...));
Explanation:
Two paramter packs of different arity must be expanded separately. When you write std::get<LeftSeq>(args)..., you try to expand together packs of different arity. This cannot be done. You should have wrote std::get<LeftSeq>(args... /* 1st expand/) ... /* 2nd expand */. This is syntactically correct but does not match std::get API. std::forward_as_tuple is there to help you and has been written precisely for those types of use cases.
Edit:
If you want to avoid the tuple, then you must write your own version of std::get to match your need, provided you expand the parameters correctly as I explained above.

Using an array of lambda arguments

I'm playing around with C++14 lambdas (well just lambdas in general really) and I have a function (pipeline) I'm trying to write. The premise is that it'll take a unit lambda and an array of unary lambdas that it'll then run on the unit and produce a new unit to send into the next in the pipeline until you get through the last lambda and return the final unit. my current code is:
auto pipeline = [](auto u, auto callbacks[]){
for(int i = 0; i<sizeof(callbacks)/sizeof(callbacks[0]);i++){
u = bind(u,callbacks[i]);
}
return u;
};
The current issue is that clang is kicking back on the array saying:
testFuture.cpp:183:111: error: no matching function for call to object of type 'func::<lambda at ./func.hpp:30:19>'
cout<<"pipeline(unit(10),{addf(4),curry(mul,2)}):"<<bind(unit(bind(unit(10))(addf(4))))(curry(mul,2))<<"|"<<pipeline(unit(10),{{addf(4),curry(mul,2)}})()<<endl;
^~~~~~~~
./func.hpp:30:19: note: candidate template ignored: couldn't infer template argument '$auto-0-1'
auto pipeline = [](auto u, auto callbacks[]){
^
1 error generated.
Is this simply not possible with lambdas? Do I need to whip out std::function? Am I just going about this the wrong way?
Taking a step back, you’re attempting to perform a (left) fold over a sequence of values. Because in C++ each closure has a unique type, you want to make that fold over a tuple, not an array. As an added benefit, we will be more general and accept functors with any return type, which we couldn’t if e.g. we used std::function to emulate arrow types.
#include <utility> // std::forward, std::integer_sequence
#include <type_traits> // std::remove_reference_t
#include <tuple> // tuple protocol, e.g. std::get, std::tuple_size
namespace detail {
template<typename Functor, typename Zero, typename Tuple, typename Int>
Zero foldl(Functor&&, Zero&& zero, Tuple&&, std::integer_sequence<Int>)
{ return std::forward<Zero>(zero); }
template<typename Functor, typename Zero, typename Tuple, typename Int, Int Index, Int... Indices>
decltype(auto) foldl(Functor&& functor, Zero&& zero, Tuple&& tuple, std::integer_sequence<Int, Index, Indices...>)
{ return detail::foldl(
functor
, functor(std::forward<Zero>(zero), std::get<Index>(std::forward<Tuple>(tuple)))
, std::forward<Tuple>(tuple)
, std::integer_sequence<Int, Indices...> {}); }
} // detail
template<typename Functor, typename Zero, typename Tuple>
decltype(auto) foldl(Functor&& functor, Zero&& zero, Tuple&& tuple)
{
return detail::foldl(
std::forward<Functor>(functor)
, std::forward<Zero>(zero)
, std::forward<Tuple>(tuple)
, std::make_index_sequence<std::tuple_size<std::remove_reference_t<Tuple>>::value>()
);
}
You haven’t told us what bind is supposed to achieve, but here’s an example involving reverse composition of functions. (Included is a limited implementation of integer_sequence and friends as they don’t appear to be available on Coliru — type traits aliases are missing as well.)