Specifying parameter pack length with concepts [duplicate] - c++

Why I get a compilation error if uncomment line #1 and comment line #2?
Demo: https://godbolt.org/z/KW6dhsrKd
#include <utility>
template <typename, std::size_t> concept prefix = true;
template<std::size_t>
struct dummy { template<typename T> constexpr dummy(T){}; };
template <auto N>
consteval auto nth_element(auto... args)
{
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
//return [](prefix<Is> auto..., auto arg, auto...) { //compile error // #1
return [](dummy<Is> ..., auto arg, auto...) { // OK // #2
return arg;
}(args...);
}(std::make_index_sequence<N>());
}
int main()
{
static_assert(nth_element<0>(1, 2, 3) == 1);
static_assert(nth_element<1>(1, 2, 3) == 2);
static_assert(nth_element<2>(1, 2, 3) == 3);
return 0;
}

From [dcl.fct]/22:
An abbreviated function template is equivalent to a function template ([temp.fct]) whose template-parameter-list includes one invented type template-parameter for each generic parameter type placeholder of the function declaration, in order of appearance. [..] The invented type template-parameter is a template parameter pack if the corresponding parameter-declaration declares a function parameter pack.
Note that prefix<Is> auto... is both a pack and a pack expansion simultaneously.
So the transformed template would look like
[] <prefix<Is>... Args> (Args..., auto arg, auto...) {
return arg;
}(args...)
Which is equivalent to
[] <typename... Args> requires(prefix<Args, Is> && ...) (Args..., auto arg, auto...) {
return arg;
}(args...)
(This still generates the same error message, too.)
Where we can see that 1) Args is in a non-deduced context and will always remain empty, and 2) Is and Args would have to agree in length for the constraint to be satisfiable.
The solution given in the comments is not ideal IMO, as creating a tuple is likely relatively expensive (especially at compile time). I think your solution + proper forwarding is the best for now (until we get proper pack subscripts). I.e. something like this:
template<std::size_t>
struct dummy { constexpr dummy(auto&&){}; };
template <std::size_t N, typename... Args>
constexpr decltype(auto) nth_element(Args&&... args) {
return [&]<std::size_t... Is>(std::index_sequence<Is...>) -> decltype(auto) {
return [](dummy<Is> ..., auto&& arg, auto&&...) -> decltype(auto) { // OK // #2
return std::forward<decltype(arg)>(arg);
}(std::forward<Args>(args)...);
}(std::make_index_sequence<N>());
}

Related

c++ parameter pack expansion with concepts

Why I get a compilation error if uncomment line #1 and comment line #2?
Demo: https://godbolt.org/z/KW6dhsrKd
#include <utility>
template <typename, std::size_t> concept prefix = true;
template<std::size_t>
struct dummy { template<typename T> constexpr dummy(T){}; };
template <auto N>
consteval auto nth_element(auto... args)
{
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
//return [](prefix<Is> auto..., auto arg, auto...) { //compile error // #1
return [](dummy<Is> ..., auto arg, auto...) { // OK // #2
return arg;
}(args...);
}(std::make_index_sequence<N>());
}
int main()
{
static_assert(nth_element<0>(1, 2, 3) == 1);
static_assert(nth_element<1>(1, 2, 3) == 2);
static_assert(nth_element<2>(1, 2, 3) == 3);
return 0;
}
From [dcl.fct]/22:
An abbreviated function template is equivalent to a function template ([temp.fct]) whose template-parameter-list includes one invented type template-parameter for each generic parameter type placeholder of the function declaration, in order of appearance. [..] The invented type template-parameter is a template parameter pack if the corresponding parameter-declaration declares a function parameter pack.
Note that prefix<Is> auto... is both a pack and a pack expansion simultaneously.
So the transformed template would look like
[] <prefix<Is>... Args> (Args..., auto arg, auto...) {
return arg;
}(args...)
Which is equivalent to
[] <typename... Args> requires(prefix<Args, Is> && ...) (Args..., auto arg, auto...) {
return arg;
}(args...)
(This still generates the same error message, too.)
Where we can see that 1) Args is in a non-deduced context and will always remain empty, and 2) Is and Args would have to agree in length for the constraint to be satisfiable.
The solution given in the comments is not ideal IMO, as creating a tuple is likely relatively expensive (especially at compile time). I think your solution + proper forwarding is the best for now (until we get proper pack subscripts). I.e. something like this:
template<std::size_t>
struct dummy { constexpr dummy(auto&&){}; };
template <std::size_t N, typename... Args>
constexpr decltype(auto) nth_element(Args&&... args) {
return [&]<std::size_t... Is>(std::index_sequence<Is...>) -> decltype(auto) {
return [](dummy<Is> ..., auto&& arg, auto&&...) -> decltype(auto) { // OK // #2
return std::forward<decltype(arg)>(arg);
}(std::forward<Args>(args)...);
}(std::make_index_sequence<N>());
}

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

recursion in variadic template function of different argument types

consider the following piece of code
template <int INDEX>
void foo() { } // termination version
template <int INDEX, typename Arg, typename... Args>
void foo(Arg head, Args... args) {
if (INDEX == 0) {
cout << head << endl;
}
else {
foo <INDEX-1 > (args...);
}
}
int main() {
foo<1> (1, 3.1415);
return 0;
}
the code compiles and outputs 3.1415 as expected.
however, the following simple code compiles fine but always outputs 1. do you have any fix for this?
template <int INDEX>
void foo() { } // termination version
template <int INDEX, typename Arg, typename... Args>
Arg foo(Arg head, Args... args) {
if (INDEX == 0) {
return head;
}
else {
foo <INDEX-1 > (args...);
}
}
int main() {
cout<<foo<1> (1, 3.1415,"Test!");
return 0;
}
in other words, how can I recursively call a variadic templated function with different argument types?
1 Problems with your approach
1.1 Missing return in foo<1>
Make sure you understand how a return from a nested call works. Your foo<1> calls foo<0> which returns its (foo<0>'s) first argument back to foo<1>. But your foo<1> does not care about foo<0>'s return because it called foo<0> like this:
else {
foo<i-1>(args...);// `i-1` becomes `0`
}
The compiler knows you have a problem here: Which value should foo<1> return after it got the return from foo<0> (which has been ignored)? It has to return a value of the same type as its first argument, but it never returns before reaching its closing }.
As pointed out in the comments, you should turn on compiler warnings to detect problems like these. In this case, -Wall (GCC documentation on warning options) is sufficient for GCC and clang to warn you (online demo), but there are more warnings available. If your filename reads main.cpp and the closing } is found line 23, column 1, the compiler warning could read
main.cpp: In function ‘Arg foo(Arg, Args ...) [with int INDEX = 1; Arg = int; Args = {double, const char*}]’:
main.cpp:23:1: warning: control reaches end of non-void function [-Wreturn-type]
}
^
1.2 Return type must be known at compile time
You might attempt to fix your code by passing the return value from foo<0> up the stack:
else {
return foo<i-1>(args...);// NOTE: type of return value depends on `foo<i-1>`
}
However, that fails because foo<1> has been declared to return a value of the same type as its first argument:
template<int i, class Arg, class... Args>
Arg foo(Arg, Args... args) {// <--------- NOTE: must return a value of type `Arg`
2 Fix for your own recursive implementation
2.1 C++17 and above
With C++17 you can use auto as return type together with constexpr if to implement the recursion as follows:
template<size_t i, class T0, class... Ts>
auto foo(T0 v0, Ts... vs) {
static_assert(i < 1u + sizeof...(Ts));
if constexpr(0u == i) return v0;// <------ NOTE: must be `if constexpr` (C++17)
else return foo<i-1u>(vs...);
}
2.2 C++14 and above
With C++14 you can also use auto as return type, but constexpr if is not available. The workaround is a well-known idiom and uses specialization of a class templates that "implements" the recursion logic:
template<int i>
struct foo_impl {
static_assert(i > 0, "the case `i == 0` requires a specialization");
template<class T0, class... Ts>
static auto get(T0, Ts... vs) {
return foo_impl<i-1>::get(vs...);
}
};
template<>
struct foo_impl<0> {
template<class T0, class... Ts>
static auto get(T0 v0, Ts...) {
return v0;
}
};
template<int i, class... Ts>
auto foo(Ts... vs) {
static_assert(i >= 0 && i < sizeof...(Ts), "index range: [0, size)");
return foo_impl<i>::get(vs...);// forward to "implementation"
}
2.3 C++11 and above
With C++11 you would need to specify trailing return types which is a bit tedious. See max66's answer for details.
3 Final recommendations
Enable and analyze compiler warnings (-Wall is an absolute minimum).
Once you are familiar with these techniques, do not implement this yourself. Instead, learn and use standard solutions like std::tuple.
Use compile-time recursion with caution. It may significantly increase your compilation time.
I don't think it's possible (in C++11 and C++14, at least) develop a foo() of this type because you don't know the correct return type.
If you don't want use std::tuple, I suggest to develop a type traits to extract the n-th type and manage foo() via SFINAE.
The following is a possible solution
#include <iostream>
#include <type_traits>
template <std::size_t, typename...>
struct indexType
{ using type = int; }; // the type of the foo() without argument
template <std::size_t I, typename I0, typename ... Is>
struct indexType<I, I0, Is...>
{ using type = typename indexType<I-1U, Is...>::type; };
template <typename I0, typename ... Is>
struct indexType<0U, I0, Is...>
{ using type = I0; };
template <std::size_t I, typename ... Args>
using indexType_t = typename indexType<I, Args...>::type;
template <std::size_t>
int foo ()
{ return 0; } // termination version: a return type is needed
template <std::size_t I, typename Arg, typename... Args>
auto foo (Arg const & head, Args const & ...)
-> typename std::enable_if<I == 0U, Arg>::type
{ return head; }
template <std::size_t I, typename Arg, typename... Args>
auto foo (Arg const &, Args const & ... args)
-> typename std::enable_if<I != 0U, indexType_t<I-1U, Args...>>::type
{ return foo<I-1U>(args...); }
int main ()
{
std::cout << foo<1U> (1, 3.1415, std::string("Test!")) << std::endl;
std::cout << foo<2U> (1, 3.1415, std::string("Test!")) << std::endl;
std::cout << foo<3U> (1, 3.1415, std::string("Test!")) << std::endl;
}

Ambiguous call to variadic template function with no parameters?

When running this:
template <typename T>
struct CodeByType
{
static const int32_t Value = 7;
};
template <>
struct CodeByType<int>
{
static const int32_t Value = 1;
};
template <typename Arg, typename... Args>
int32_t Sum()
{
// The compiler complains on this line
return Sum<Arg>() + Sum<Args...>();
}
template <typename Arg>
int32_t Sum()
{
return CodeByType<Arg>::Value;
}
int main()
{
auto sum = Sum<int, char, double>();
}
I'm getting:
Error C2668 'Sum': ambiguous call to overloaded function
Can someone please explain why and how to overcome it?
This looks awfully similar to the below code, which does compile, so I suppose it has something to do with Sum not accepting any actual parameters.
template <typename T>
T adder(T first) {
return first;
}
template<typename T, typename... Args>
T adder(T first, Args... rest) {
return first + adder(rest...);
}
int main()
{
auto sum = adder(1, 7);
}
If you reduce your code to just:
Sum<int>();
You get a more helpful error message:
31 : <source>:31:16: error: call to 'Sum' is ambiguous
auto sum = Sum<int>();
^~~~~~~~
17 : <source>:17:9: note: candidate function [with Arg = int, Args = <>]
int32_t Sum()
^
24 : <source>:24:9: note: candidate function [with Arg = int]
int32_t Sum()
^
1 error generated.
So it is clearer that there is an overload ambiguity between the first overload with Args = <> and the second one. Both are viable.
One would might think as specialization for a solution:
template <typename Arg>
int32_t Sum<Arg>()
{
return CodeByType<Arg>::Value;
}
which would indeed solve the issue, had it been allowed by the standard. Partial function specializations are not allowed.
C++17 solution:
This is the most elegant solution:
constexpr if to the rescue:
template <typename Arg, typename... Args>
int32_t Sum()
{
if constexpr(sizeof...(Args) == 0)
return CodeByType<Arg>::Value;
else
return Sum<Arg>() + Sum<Args...>();
}
C++14 solution
We use SFINAE to enable/disable the function we want. Please note the function definition order had to be reversed.
template <typename Arg, typename... Args>
auto Sum() -> std::enable_if_t<(sizeof...(Args) == 0), int32_t>
{
return CodeByType<Arg>::Value;
}
template <typename Arg, typename... Args>
auto Sum() -> std::enable_if_t<(sizeof...(Args) > 0), int32_t>
{
return Sum<Arg>() + Sum<Args...>();
}
C++11 solution
just replace std::enable_if_t<> with typename std::enable_if<>::type
In c++17, it would simply be
template <typename... Args>
int32_t Sum()
{
return (CodeByType<Args>::Value + ...); // Fold expression
}
In C++11, you may do:
template <typename... Args>
int32_t Sum()
{
int32_t res = 0;
const int32_t dummy[] = {0, (res += CodeByType<Args>::Value)...};
static_cast<void>(dummy); silent warning about unused variable
return res;
}
My memories of the template mechanism are old but if I recall correctly, their information is erased at a certain point in the compilation process.
My guess is that in the second case, the functions get distinguished not by the difference in the template types, but by the difference in the arguments.
In your case, you have no arguments, so stripped of the template information the two overloaded versions are equal and it cannot distinguish between them when you call it.

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