Count the number of arguments in a lambda - c++

I need to know the exact number of arguments that a lambda has. I do not care for their types, I just need a count.
auto lambda0 = [&]() { ... };
auto lambda1 = [&](int32_t a) { ... };
auto lambda2 = [&](int32_t a, auto b) { ... };
lambda_details<decltype(lambda0)>::argument_count; // Equals 0
lambda_details<decltype(lambda1)>::argument_count; // Equals 1
lambda_details<decltype(lambda2)>::argument_count; // Equals 2
Detecting variadic lambdas would also be nice so that I can deal with that edge case as well.
auto lambda_variadic = [&](auto... args){ ... };
lambda_details<decltype(lambda_variadic)>::is_variadic; // Equals true
How can I get this information?

You can create an object that can go into any parameter by overloading conversion operator. From there just test if the lambda is callable with a given number of such arguments, counting down from some arbitrary large number. If the lambda happens to be callable on the first try (with given arbitrary large number of arguments), we can assume it is variadic:
#include <iostream>
#include <utility>
#include <type_traits>
struct any_argument {
template <typename T>
operator T&&() const;
};
template <typename Lambda, typename Is, typename = void>
struct can_accept_impl
: std::false_type
{};
template <typename Lambda, std::size_t ...Is>
struct can_accept_impl<Lambda, std::index_sequence<Is...>,
decltype(std::declval<Lambda>()(((void)Is, any_argument{})...), void())>
: std::true_type
{};
template <typename Lambda, std::size_t N>
struct can_accept
: can_accept_impl<Lambda, std::make_index_sequence<N>>
{};
template <typename Lambda, std::size_t Max, std::size_t N, typename = void>
struct lambda_details_impl
: lambda_details_impl<Lambda, Max, N - 1>
{};
template <typename Lambda, std::size_t Max, std::size_t N>
struct lambda_details_impl<Lambda, Max, N, std::enable_if_t<can_accept<Lambda, N>::value>>
{
static constexpr bool is_variadic = (N == Max);
static constexpr std::size_t argument_count = N;
};
template <typename Lambda, std::size_t Max = 50>
struct lambda_details
: lambda_details_impl<Lambda, Max, Max>
{};
int main()
{
auto lambda0 = []() {};
auto lambda1 = [](int a) {};
auto lambda2 = [](int a, auto b) {};
auto lambda3 = [](int a, auto b, char = 'a') {};
auto lambda4 = [](int a, auto b, char = 'a', auto...) {};
std::cout << lambda_details<decltype(lambda0)>::is_variadic << " " << lambda_details<decltype(lambda0)>::argument_count << "\n"; // 0 0
std::cout << lambda_details<decltype(lambda1)>::is_variadic << " " << lambda_details<decltype(lambda1)>::argument_count << "\n"; // 0 1
std::cout << lambda_details<decltype(lambda2)>::is_variadic << " " << lambda_details<decltype(lambda2)>::argument_count << "\n"; // 0 2
std::cout << lambda_details<decltype(lambda3)>::is_variadic << " " << lambda_details<decltype(lambda3)>::argument_count << "\n"; // 0 3
std::cout << lambda_details<decltype(lambda4)>::is_variadic << " " << lambda_details<decltype(lambda4)>::argument_count << "\n"; // 1 50
}

I have solved it using a modified version of #yuri kilochek's answer.
Instead of starting from 50 arguments and counting down, we start at zero and count up. When we get a match we know the minimum amount of arguments required to call the lambda. We then keep searching up until a sane maximum to see if there is a maximum amount of arguments (this can happen when you have default arguments).
If the argument count limit is reached, we assume the lambda to be variadic.
This implementation reduces the amount of template instantiations for non variadic lambdas significantly. It also gives us the minimum amount of arguments for all lambdas, and the maximum amount of arguments for any non-variadic lambdas.
Again, big thanks to Yuri Kilochek for laying the foundation for this elegant solution. Check his answer for more details about the implementation.
struct any_argument
{
template <typename T>
operator T && () const;
};
template <typename Lambda, typename Is, typename = void>
struct can_accept_impl : std::false_type
{};
template <typename Lambda, std::size_t ...Is>
struct can_accept_impl <Lambda, std::index_sequence<Is...>, decltype(std::declval<Lambda>()(((void)Is, any_argument{})...), void())> : std::true_type
{};
template <typename Lambda, std::size_t N>
struct can_accept : can_accept_impl<Lambda, std::make_index_sequence<N>>
{};
template <typename Lambda, std::size_t N, size_t Max, typename = void>
struct lambda_details_maximum
{
static constexpr size_t maximum_argument_count = N - 1;
static constexpr bool is_variadic = false;
};
template <typename Lambda, std::size_t N, size_t Max>
struct lambda_details_maximum<Lambda, N, Max, std::enable_if_t<can_accept<Lambda, N>::value && (N <= Max)>> : lambda_details_maximum<Lambda, N + 1, Max>
{};
template <typename Lambda, std::size_t N, size_t Max>
struct lambda_details_maximum<Lambda, N, Max, std::enable_if_t<can_accept<Lambda, N>::value && (N > Max)>>
{
static constexpr bool is_variadic = true;
};
template <typename Lambda, std::size_t N, size_t Max, typename = void>
struct lambda_details_minimum : lambda_details_minimum<Lambda, N + 1, Max>
{
static_assert(N <= Max, "Argument limit reached");
};
template <typename Lambda, std::size_t N, size_t Max>
struct lambda_details_minimum<Lambda, N, Max, std::enable_if_t<can_accept<Lambda, N>::value>> : lambda_details_maximum<Lambda, N, Max>
{
static constexpr size_t minimum_argument_count = N;
};
template <typename Lambda, size_t Max = 50>
struct lambda_details : lambda_details_minimum<Lambda, 0, Max>
{};
Another important thing to note is that any_argument doesn't automatically play nice with operators. You will have to overload every single one if you want it to work with auto arguments that are operated upon (e.g. [](auto a) { return a * 2; }). It will end up looking more like this:
struct any_argument
{
template <typename T> operator T && () const;
any_argument& operator ++();
any_argument& operator ++(int);
any_argument& operator --();
any_argument& operator --(int);
template <typename T> friend any_argument operator + (const any_argument&, const T&);
template <typename T> friend any_argument operator + (const T&, const any_argument&);
template <typename T> friend any_argument operator - (const any_argument&, const T&);
template <typename T> friend any_argument operator - (const T&, const any_argument&);
template <typename T> friend any_argument operator * (const any_argument&, const T&);
template <typename T> friend any_argument operator * (const T&, const any_argument&);
template <typename T> friend any_argument operator / (const any_argument&, const T&);
template <typename T> friend any_argument operator / (const T&, const any_argument&);
// And every other operator in existence
};

I don't know a way to count all argument of a generic-lambda [edit: but yuri kilochek know how to do it: see his answer for a great solution].
For non-generic lambdas, as suggested by Igor Tandetnik, you can detect the types (return and arguments) of the pointer to operator() and count the arguments.
Something as follows
// count arguments helper
template <typename R, typename T, typename ... Args>
constexpr std::size_t cah (R(T::*)(Args...) const)
{ return sizeof...(Args); }
// count arguments helper
template <typename R, typename T, typename ... Args>
constexpr std::size_t cah (R(T::*)(Args...))
{ return sizeof...(Args); }
template <typename L>
constexpr auto countArguments (L)
{ return cah(&L::operator()); }
But, unfortunately, this doesn't works when you introduce an auto argument because, with an auto argument, you transform operator() in a template function.
About detecting a variadic lambda, you can detect a function with only a variadic list of arguments (let me call it "pure variadic"), as your lambda_variadic, trying to call it with zero and with (by example) 50 argument of a given type.
I mean something as follows
template <typename T, std::size_t>
struct getType
{ using type = T; };
template <typename T, std::size_t N>
using getType_t = typename getType<T, N>::type;
// isPureVariadic arguments helper
template <typename T>
constexpr std::false_type ipvh (...);
// isPureVariadic arguments helper
template <typename T, typename F, std::size_t ... Is>
constexpr auto ipvh (F f, std::index_sequence<Is...>)
-> decltype( f(std::declval<getType_t<T, Is>>()...), std::true_type{} );
template <typename F>
constexpr bool isPureVariadic (F f)
{ return
decltype(ipvh<int>(f, std::make_index_sequence<0u>{}))::value
&& decltype(ipvh<int>(f, std::make_index_sequence<50u>{}))::value; }
but this isn't perfect because gives false positives and false negatives.
A problem is that when you check it with a "not pure variadic lambda" as
auto lambda_variadic2 = [&](std::string, auto... args){ ... };
that is variadic but the first argument doesn't accept a int, isn't detected as "pure variadic"; unfortunately the following lambda
auto lambda_variadic3 = [&](long, auto... args){ ... };
is detected as "pure variadic" because the first argument accept a int.
To avoid this problem, you can modify the function to check the call with 50 arguments of two incompatible types; by example
template <typename F>
constexpr bool isPureVariadic (F f)
{ return
decltype(ipvh<int>(f, std::make_index_sequence<0u>{}))::value
&& decltype(ipvh<int>(f, std::make_index_sequence<50u>{}))::value
&& decltype(ipvh<std::string>(f, std::make_index_sequence<50u>{}))::value; }
Another problem is that are detected as "pure virtual" also non-variadic generic-lambda functions receiving a number of arguments higher that the checked number (50, in the example).
And remain the problem that this solution doesn't detect lambda_variadic2 (a non-pure variadic lambda) as variadic.
The following is a full compiling example with the best I can imagine about your question
#include <iostream>
#include <utility>
#include <type_traits>
// count arguments helper
template <typename R, typename T, typename ... Args>
constexpr std::size_t cah (R(T::*)(Args...) const)
{ return sizeof...(Args); }
// count arguments helper
template <typename R, typename T, typename ... Args>
constexpr std::size_t cah (R(T::*)(Args...))
{ return sizeof...(Args); }
template <typename L>
constexpr auto countArguments (L)
{ return cah(&L::operator()); }
template <typename T, std::size_t>
struct getType
{ using type = T; };
template <typename T, std::size_t N>
using getType_t = typename getType<T, N>::type;
// isPureVariadic arguments helper
template <typename T>
constexpr std::false_type ipvh (...);
// isPureVariadic arguments helper
template <typename T, typename F, std::size_t ... Is>
constexpr auto ipvh (F f, std::index_sequence<Is...>)
-> decltype( f(std::declval<getType_t<T, Is>>()...), std::true_type{} );
template <typename F>
constexpr bool isPureVariadic (F f)
{ return
decltype(ipvh<int>(f, std::make_index_sequence<0u>{}))::value
&& decltype(ipvh<int>(f, std::make_index_sequence<50u>{}))::value; }
int main() {
auto lambda0 = [&]() {};
auto lambda1 = [&](int) {};
auto lambda2 = [&](int, auto) {};
auto lambda3 = [&](auto...) {};
std::cout << countArguments(lambda0) << std::endl;
std::cout << countArguments(lambda1) << std::endl;
// std::cout << countArguments(lambda2) << std::endl; // compilation error
// std::cout << countArguments(lambda3) << std::endl; // compilation error
std::cout << isPureVariadic(lambda0) << std::endl;
std::cout << isPureVariadic(lambda1) << std::endl;
std::cout << isPureVariadic(lambda2) << std::endl;
std::cout << isPureVariadic(lambda3) << std::endl;
}

Related

Getting the number of arguments to a function at compile-time using variadic templates with argument type check in c++

Say I have a class:
template<typename... Types>
class Example
{
public:
using types = std::tuple<Types...>;
template<size_t N> using dim_type = std::tuple_element_t<N, types>;
};
And I want to implement a member function that depends on the tuple element as follows, with the goal of having access to the number of arguments during compilation:
template<size_t N>
inline constexpr void do_stuff(const dim_type<N>& elems...)
{
constexpr size_t size_stuff = sizeof...(elems); // <-- this is the end goal
};
Problem is that this implementation of do_stuff() won't do. And ultimately I would like the function to work like this:
Example<long,std::string> ex;
ex.do_stuff<0>(3);
ex.do_stuff<0>(5,6);
ex.do_stuff<1>("a","b","c","d");
ex.do_stuff<0>("a","b","c","d"); // <-- this line should not compile
ex.do_stuff<1>(1,"b"); // <-- nor should this one
And inside do_stuff I should know at compile-time how many arguments are passed.
One possible answer, but I'm having an issue with it:
I figured that a proper implementation of this would require the use of variadic templates, however, I am having a problem getting the std::enable_if part to work:
template<size_t N, typename... Args,
std::enable_if_t<static_and<std::is_convertible_v<Args, dim_type<N>>...>::value>* = nullptr>
inline constexpr void do_stuff(const Args&... elems)
{
constexpr size_t size_stuff = sizeof...(elems); // <-- this is the end goal
};
where static_and is:
template<bool Head, bool... Tail>
struct static_and {
static constexpr bool value = Head && static_and<Tail...>::value;
};
template<bool Bool> struct static_and<Bool> {
static constexpr bool value = Bool;
};
It seems to me that the right way (a possible right way) is the one based on static_and: a variadic type list Args of argument that is SFINAE checked to be convertible to the right type.
I propose the following version of static_and
template <bool ...>
struct static_and : public std::false_type
{ };
template <>
struct static_and<> : public std::true_type
{ };
template <bool ... Bs>
struct static_and<true, Bs...> : public static_and<Bs...>
{ };
and do_stuff() become
template <std::size_t I, typename ... Ts>
inline constexpr auto do_stuff (Ts const & ... elems)
-> std::enable_if_t<
static_and<std::is_convertible<Ts, type_n<I>>::value...>::value>
{
// now the number of elems is sizeof...(elems)
// or sizeof...(Ts)
std::cout << sizeof...(Ts) << std::endl;
}
The following is a full compiling example (with compilation errors when appropriate)
#include <tuple>
#include <iostream>
#include <type_traits>
template <bool ...>
struct static_and : public std::false_type
{ };
template <>
struct static_and<> : public std::true_type
{ };
template <bool ... Bs>
struct static_and<true, Bs...> : public static_and<Bs...>
{ };
template <typename ... Types>
struct foo
{
using types = std::tuple<Types...>;
static constexpr std::size_t num_types { sizeof...(Types) };
template <std::size_t I>
using type_n = std::tuple_element_t<I, types>;
template <std::size_t I, typename ... Ts>
inline constexpr auto do_stuff (Ts const & ... elems)
-> std::enable_if_t<
static_and<std::is_convertible<Ts, type_n<I>>::value...>::value>
{
// now the number of elems is sizeof...(elems)
// or sizeof...(Ts)
std::cout << sizeof...(Ts) << std::endl;
}
};
int main ()
{
foo<long, std::string> ex;
ex.do_stuff<0>(3); // compile; print 1
ex.do_stuff<0>(5, 6); // compile; print 2
ex.do_stuff<1>("a", "b", "c", "d"); // compile; print 4
// ex.do_stuff<0>("a", "b", "c", "d"); // compilation error
// ex.do_stuff<1>(1, "b"); // compilation error
}
If you can use C++17, instead of static_and you can simply use template folding
template <std::size_t I, typename ... Ts>
inline constexpr auto do_stuff (Ts const & ... elems)
-> std::enable_if_t<(... && std::is_convertible<Ts, type_n<I>>::value)>
{
// now the number of elems is sizeof...(elems)
// or sizeof...(Ts)
std::cout << sizeof...(Ts) << std::endl;
}
If, in C++14, you prefer a constexpr static_and() function instead of a struct, you can write it as follows
template <bool ... Bs>
constexpr bool static_and ()
{
using unused = bool[];
bool ret { true };
(void) unused { true, ret &= Bs... };
return ret;
}
So do_stuff() become
template <std::size_t I, typename ... Ts>
inline constexpr auto do_stuff (Ts const & ... elems)
-> std::enable_if_t<
static_and<std::is_convertible<Ts, type_n<I>>::value...>()>
{
// now the number of elems is sizeof...(elems)
// or sizeof...(Ts)
std::cout << sizeof...(Ts) << std::endl;
}

How to call a specific method only if class has one?

I have a tuple of objects of different classes. I want to iterate over the tuple and call a certain method only if those class has one.
For example (pseudo-code):
struct A { int get( ) { return 5; }; };
struct B { };
struct C { int get( ) { return 10; }; };
int i = 0;
tuple<A, B, C> t;
for ( auto t_element : t )
{
if constexpr ( has_get_method( decltype(t_element) ) )
{
i += t_element.get( );
}
}
I already know how to iterate over the tuple and check if a class has some method using sfinae but how do I skip object that do not have the required method?
EDIT:
If you found this old question in the future, please know that this can be done much easier now using concepts from C++20 standard.
Just write a sfinae'd function and a catchall in case the previous one fails. You don't have to use if constexpr for that and you can't use it actually in C++14 (that is how you tagged the question).
Here is a minimal, working example:
#include <tuple>
#include <iostream>
auto value(...) { return 0; }
template <typename T>
auto value(T &t) -> decltype(t.get()) {
return t.get();
}
struct A { int get() { return 5; }; };
struct B {};
struct C { int get() { return 10; }; };
int main() {
int i = 0;
std::tuple<A, B, C> t;
i += value(std::get<0>(t));
i += value(std::get<1>(t));
i += value(std::get<2>(t));
std::cout << i << std::endl;
}
See it up and running on wandbox.
If you have any argument you want to use to test it, you can use std::forward as:
template <typename T, typename... Args>
auto value(T &t, Args&&... args)
-> decltype(t.get(std::forward<Args>(args)...))
{ return t.get(std::forward<Args>(args)...); }
Then invoke it as:
i += value(std::get<0>(t), params);
You can create a type-traits, to check if a class has a get() method, by declaring a couple of functions (no need to define them)
template <typename>
constexpr std::false_type withGetH (long);
template <typename T>
constexpr auto withGetH (int)
-> decltype( std::declval<T>().get(), std::true_type{} );
template <typename T>
using withGet = decltype( withGetH<T>(0) );
The following is a fully working c++17 example
#include <tuple>
#include <iostream>
#include <type_traits>
template <typename>
constexpr std::false_type withGetH (long);
template <typename T>
constexpr auto withGetH (int)
-> decltype( std::declval<T>().get(), std::true_type{} );
template <typename T>
using withGet = decltype( withGetH<T>(0) );
struct A { int get( ) { return 5; }; };
struct B { };
struct C { int get( ) { return 10; }; };
template <typename T>
int addGet (T & t)
{
int ret { 0 };
if constexpr ( withGet<T>{} )
ret += t.get();
return ret;
}
int main ()
{
int i = 0;
std::tuple<A, B, C> t;
i += addGet(std::get<0>(t));
i += addGet(std::get<1>(t));
i += addGet(std::get<2>(t));
std::cout << i << std::endl;
}
If you can't use if constexpr, you can write (in c++11/14) getAdd() as follows, using tag dispatching
template <typename T>
int addGet (T & t, std::true_type const &)
{ return t.get(); }
template <typename T>
int addGet (T & t, std::false_type const &)
{ return 0; }
template <typename T>
int addGet (T & t)
{ return addGet(t, withGet<T>{}); }
--EDIT--
The OP ask
my code needs to check for a templated method with parameters. Is it possible to modify your solution so that instead of int get() it could check for something like template<class T, class U> void process(T& t, U& u)?
I suppose it's possible.
You can create a type-traits withProcess2 (where 2 is the number of arguments) that receive three template type parameters: the class and the two template types
template <typename, typename, typename>
constexpr std::false_type withProcess2H (long);
template <typename T, typename U, typename V>
constexpr auto withProcess2H (int)
-> decltype( std::declval<T>().process(std::declval<U>(),
std::declval<V>()),
std::true_type{} );
template <typename T, typename U, typename V>
using withProcess2 = decltype( withProcess2H<T, U, V>(0) );
The following is a fully working example (c++17, but now you know how to make it c++14) with A and C with a process() with 2 template parameters and B with a process() with only 1 template parameter.
#include <iostream>
#include <type_traits>
template <typename, typename, typename>
constexpr std::false_type withProcess2H (long);
template <typename T, typename U, typename V>
constexpr auto withProcess2H (int)
-> decltype( std::declval<T>().process(std::declval<U>(),
std::declval<V>()),
std::true_type{} );
template <typename T, typename U, typename V>
using withProcess2 = decltype( withProcess2H<T, U, V>(0) );
struct A
{
template <typename T, typename U>
void process(T const &, U const &)
{ std::cout << "A::process(T, U)" << std::endl; }
};
struct B
{
template <typename T>
void process(T const &)
{ std::cout << "B::process(T)" << std::endl; }
};
struct C
{
template <typename T, typename U>
void process(T &, U &)
{ std::cout << "C::process(T, U)" << std::endl; }
};
template <typename T>
void callProcess (T & t)
{
static int i0 { 0 };
static long l0 { 0L };
if constexpr ( withProcess2<T, int &, long &>{} )
t.process(i0, l0);
}
int main ()
{
std::tuple<A, B, C> t;
callProcess(std::get<0>(t)); // print A::process(T, U)
callProcess(std::get<1>(t)); // no print at all
callProcess(std::get<2>(t)); // print C::process(T, U)
}

Searching through a tuple for arguments of a function

Consider this
int foo (int a, char c, bool b) {std::cout << a << ' ' << c << ' ' << b << '\n'; return 8;}
double bar (int a, char c, bool b, int d) {std::cout << a << ' ' << c << ' ' << b << ' ' << d << '\n'; return 2.5;}
char baz (bool a, bool b) {std::cout << a << ' ' << b << '\n'; return 'a';}
int main() {
const auto tuple = std::make_tuple(5, true, 'a', 3.5, false, 1000, 't', 2, true, 5.8);
const std::tuple<int, double, char> t = searchArguments (tuple, foo, bar, baz);
}
So the arguments for foo are first searched (from tuple). Searching from left to right, the first int found is 5, the first char found is a, and the first bool found is true. So then foo(5,a,true) is called. Similarly for bar and baz. Except bar takes 2 ints, and we don't want it to take 5 twice, but rather 5 and then 1000. Similarly, baz is to take (true, false) for its arguments instead of (true, true).
My current solution below unfortunately outputs precisely what I just said should not be outputted:
foo(5,a,true) // OK
bar(5,a,true,5) // Nope, we want bar(5,a,true,1000)
baz(true,true) // Nope, we want baz(true,false)
I realize that one possible (ugly) way to fix my current solution:
#include <iostream>
#include <tuple>
#include <utility>
// C++17 std::apply
template <typename F, typename Tuple, size_t... Is>
auto apply_impl (F&& f, Tuple&& tuple, const std::index_sequence<Is...>&) {
return (std::forward<F>(f))(std::get<Is>(std::forward<Tuple>(tuple))...);
}
template <typename F, typename Tuple>
auto apply (F&& f, Tuple&& tuple) { // Invoke the Callable object f with a tuple of arguments.
return apply_impl(std::forward<F>(f), std::forward<Tuple>(tuple), std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>());
}
// FunctionTraits
template <typename> struct FunctionTraits;
template <typename R, typename... Args>
struct FunctionTraits<R(Args...)> : std::integral_constant<std::size_t, sizeof...(Args)> {
using args_type = std::tuple<Args...>;
using return_type = R;
};
template <typename R, typename... Args>
struct FunctionTraits<R(*)(Args...)> : FunctionTraits<R(Args...)> {};
template <typename R, typename... Args>
struct FunctionTraits<R(&)(Args...)> : FunctionTraits<R(Args...)> {};
// etc... for other callable types.
namespace getFirstDetail {
template <typename T, typename Tuple, std::size_t N, bool>
struct SearchTuple : SearchTuple<T, Tuple, N+1, std::is_same<std::tuple_element_t<N+1, Tuple>, T>::value> {};
template <typename T, typename Tuple, std::size_t N>
struct SearchTuple<T, Tuple, N, true> {
static T search (const Tuple& tuple) {return std::get<N>(tuple);}
};
}
// Get the first element of a tuple whose type is T. Note that using std::get<T> will not suffice since this fails to compile if the tuple has more than one element of type T.
// It is the client's responsiblity to ensure that such an element in the tuple exists (else there will be a crash).
template <typename T, typename Tuple>
T getFirst (const Tuple& tuple) {
return getFirstDetail::SearchTuple<T, Tuple, -1, false>::search(tuple);
}
namespace searchArgumentsDetail {
template <typename> struct Search;
template <typename... Args>
struct Search<std::tuple<Args...>> {
template <typename R, typename Tuple, typename F>
static R execute (const Tuple& tuple, F f) {return apply(f, std::make_tuple(getFirst<Args>(tuple)...));}
};
}
template <typename Tuple>
std::tuple<> searchArguments (const Tuple&) {return std::tuple<>();}
// Gathers the first possible elements from 'tuple' that 'f' can accept (reading from left to right) and carries out the function. Then it is repeated for the remaining functions fs...
template <typename Tuple, typename F, typename... Fs>
auto searchArguments (const Tuple& tuple, F f, Fs... fs) {
using ArgsType = typename FunctionTraits<F>::args_type;
using ReturnType = typename FunctionTraits<F>::return_type;
const auto singleTuple = std::make_tuple (searchArgumentsDetail::Search<ArgsType>::template execute<ReturnType>(tuple, f));
return std::tuple_cat (singleTuple, searchArguments (tuple, fs...));
}
// Testing
int foo (int a, char c, bool b) {std::cout << a << ' ' << c << ' ' << b << '\n'; return 8;}
double bar (int a, char c, bool b, int d) {std::cout << a << ' ' << c << ' ' << b << ' ' << d << '\n'; return 2.5;}
char baz (bool a, bool b) {std::cout << a << ' ' << b << '\n'; return 'a';}
int main() {
const auto tuple = std::make_tuple(5, true, 'a', 3.5, false, 1000, 't', 2, true, 5.8);
std::cout << std::boolalpha;
const std::tuple<int, double, char> t = searchArguments (tuple, foo, bar, baz);
std::cout << std::get<0>(t) << ' ' << std::get<1>(t) << ' ' << std::get<2>(t) << '\n'; // 8 2.5 a
std::cin.get();
}
is to remove each element used from the tuple and pass the smaller tuple to the next recursion, thus guaranteeing that those repeat arguments don't occur. But that's a real mess (and probably unnecessarily inefficient). Furthermore, when calling up the next function, we need to restart with the original tuple again, and thus the original tuple must be passed around as well as each truncated tuple. I just want to ask if there is a better, much more elegant solution than this before I leap into this nightmarish task (if it even works at all).
Update: A new idea I thought of (if simply trying to fix my current solution), is to modify my getFirst function to getN<N...>, where N = 1 means get the first, N = 2 means get the second, etc...? But then there is the responsibility of updating the latest N value.
#include <utility>
#include <type_traits>
#include <tuple>
namespace detail {
template <std::size_t, int, typename, typename, typename=void>
constexpr std::size_t find = -1;
template <std::size_t I, int dir, typename U, typename Ts>
constexpr auto find<I, dir, U, Ts, std::enable_if_t<(I < std::tuple_size<Ts>{})>>
= std::is_same<std::tuple_element_t<I, Ts>, U>{}? I : find<I+dir, dir, U, Ts>;
template <typename, typename ISeq, std::size_t, typename>
struct obtain_indices {using type = ISeq;};
template <typename Ts, std::size_t... Is, std::size_t u, typename Us>
struct obtain_indices<Ts, std::integer_sequence<
std::enable_if_t<(u < std::tuple_size<Us>{}), std::size_t>, Is...>, u, Us> {
static constexpr std::array<std::size_t, sizeof...(Is)> indices = {Is...};
using C = std::tuple_element_t<u, Us>;
static constexpr auto previous = find<u-1, -1, C, Us>;
using type = typename obtain_indices<Ts, std::index_sequence<Is...,
find<previous != -1? indices[previous]+1 : 0, 1, C, Ts>>, u+1, Us>::type;
};
// General overload once indices have been determined
template <typename Tup, typename F, std::size_t... Is>
constexpr decltype(auto) invoke(F&& f, Tup&& t,
std::index_sequence<Is...>) {
return std::forward<F>(f)(std::get<Is>(std::forward<Tup>(t))...);
}
} // end namespace detail
// For function pointers
template <typename Tup, typename R, typename... Args>
constexpr decltype(auto) invoke(R(*f)(Args...), Tup&& t) {
return detail::invoke(f, std::forward<Tup>(t),
typename detail::obtain_indices<std::decay_t<Tup>,
std::index_sequence<>, 0, std::tuple<std::decay_t<Args>...>>::type{});
}
From your example:
#include <iostream>
double bar (int a, char c, bool b, int d) {
std::cout << a << ' ' << c << ' ' << b << ' ' << d << '\n';
return 2.5;
}
int main() {
const auto tuple = std::make_tuple(5, true, 'a', 3.5,
false, 1000, 't', 2, true, 5.8);
invoke(bar, tuple);
}
Demo.
Here is an alternative I've found to Columbo's method. Instead of searching backwards through the args tuple to determine if T was searched before already, store Pair<T,index+1> in a pack. Then the search begins at position 0 only if T is not found among the Pairs in that pack, else at the index+1 position. I don't know which method is more efficient though.
#include <iostream>
#include <utility>
#include <type_traits>
#include <tuple>
template <typename, std::size_t> struct Pair;
template <typename Tuple, typename F, std::size_t... Is>
constexpr decltype(auto) partial_apply (Tuple&& tuple, F&& f, std::index_sequence<Is...>) {
return std::forward<F>(f)(std::get<Is>(std::forward<Tuple>(tuple))...);
}
// FunctionTraits
template <typename> struct FunctionTraits;
template <typename R, typename... Args>
struct FunctionTraits<R(Args...)> : std::integral_constant<std::size_t, sizeof...(Args)> {
using args_type = std::tuple<std::decay_t<Args>...>;
using return_type = R;
};
template <typename R, typename... Args>
struct FunctionTraits<R(*)(Args...)> : FunctionTraits<R(Args...)> {};
template <typename R, typename... Args>
struct FunctionTraits<R(&)(Args...)> : FunctionTraits<R(Args...)> {};
// etc... for other callable types.
template <typename Tuple, typename T, std::size_t Start, typename = void>
struct Find : std::integral_constant<std::size_t, -1> {};
template <typename Tuple, typename T, std::size_t Start>
struct Find<Tuple, T, Start, std::enable_if_t<(Start < std::tuple_size<Tuple>::value)>> {
static constexpr size_t value = std::is_same<T, std::tuple_element_t<Start, Tuple>>::value ? Start : Find<Tuple, T, Start+1>::value;
};
template <typename T, typename... Pairs> struct SearchPairs;
template <typename T>
struct SearchPairs<T> : std::integral_constant<std::size_t, 0> {};
template <typename T, typename First, typename... Rest>
struct SearchPairs<T, First, Rest...> : SearchPairs<T, Rest...> {};
template <typename T, std::size_t I, typename... Rest>
struct SearchPairs<T, Pair<T,I>, Rest...> : std::integral_constant<std::size_t, I> {};
template <typename Tuple, typename ArgsTuple, std::size_t Start, typename Indices, typename LastIndices, typename = void>
struct ObtainIndices {
using type = Indices;
};
template <typename Tuple, typename ArgsTuple, std::size_t Start, std::size_t... Is, typename... Pairs>
struct ObtainIndices<Tuple, ArgsTuple, Start, std::index_sequence<Is...>, std::tuple<Pairs...>,
std::enable_if_t<(Start < std::tuple_size<ArgsTuple>::value)> > {
using T = std::tuple_element_t<Start, ArgsTuple>;
static constexpr std::size_t start = SearchPairs<T, Pairs...>::value, // Searching through Pairs..., and will be 0 only if T is not found among the pairs. Else we start after where the last T was found in Tuple.
index = Find<Tuple, T, start>::value;
using type = typename ObtainIndices<Tuple, ArgsTuple, Start+1,
std::index_sequence<Is..., index>, std::tuple<Pair<T, index+1>, Pairs...>>::type;
// 'index+1' because we start searching for T again (if ever) after position 'index'. Also, we must place Pair<T, index+1> before the Pairs... pack rather than after it because if a Pair with T already exists, that Pair must not be used again.
};
template <typename Tuple, typename F>
constexpr decltype(auto) searchArguments (Tuple&& t, F&& f) {
using IndexSequence = typename ObtainIndices<std::decay_t<Tuple>, typename FunctionTraits<std::decay_t<F>>::args_type, 0, std::index_sequence<>, std::tuple<>>::type;
return partial_apply(std::forward<Tuple>(t), std::forward<F>(f), IndexSequence{});
}
// Testing
int foo (int a, char c, bool b, int d, bool e, int f) {std::cout << "foo(" << a << ", " << c << ", " << b << ", " << d << ", " << e << ", " << f << ")\n"; return 8;}
int main() {
const auto tuple = std::make_tuple(3.14, "bye", 5, true, 'a', 3.5, 20, false, 1000, 't', true, 5.8);
std::cout << std::boolalpha;
const int a = searchArguments(tuple, foo); // foo(5, a, true, 20, false, 1000)
std::cout << a << '\n'; // 8
}

Why are variadic non-type templates not allowed as function arguments? [duplicate]

I saw a blog post which used non-type variadic templates (currently not supported by gcc, only by clang).
template <class T, size_t... Dimensions>
struct MultiDimArray { /* ... */ };
The example in the post compiles fine but I failed to get it to work with function templates.
Can anyone help figuring out the correct syntax (if such exists)?
int max(int n) { return n; } // end condition
template <int... N> // replacing int... with typename... works
int max(int n, N... rest) // !! error: unknown type name 'N'
{
int tmp = max(rest...);
return n < tmp? tmp : n;
}
#include <iostream>
int main()
{
std::cout << max(3, 1, 4, 2, 5, 0) << std::endl;
}
This will print out all elements,
get max could be implemented similarly
template <int N>
void foo(){
cout << N << endl;
}
template <int N, int M, int ... Rest>
void foo(){
cout << N << endl;
foo<M, Rest...>();
}
int main(){
foo<1, 5, 7>();
return 0;
}
You are simply confusing type names and non-type names. What you want simply doesn’t work.
You can probably use variadic non-type templates in functions, but not as (non-template) arguments:
template <int N, int... Rest>
int max()
{
int tmp = max<Rest...>();
return N < tmp ? tmp : N;
}
std::cout << max<3, 1, 4, 2, 5, 0>() << std::endl;
… although I haven’t tested this and I’m not sure how this should work given that you need to have a partial specialisation as the base case. You could solve this by dispatching to a partially specialised struct:
template <int N, int... Rest>
struct max_t {
static int const value = max_t<Rest...>::value > N ? max_t<Rest...>::value : N;
};
template <int N>
struct max_t<N> {
static int const value = N;
};
template <int... NS>
int max()
{
return max_t<NS...>::value;
}
This will work.
Here are two ways of defining a variadic function template only accepting int parameters. The first one generates a hard-error when instantiated, the second uses SFINAE:
template<typename... T>
struct and_: std::true_type {};
template<typename First, typename... Rest>
struct and_
: std::integral_constant<
bool
, First::value && and_<Rest...>::value
> {};
template<typename... T>
void
foo(T... t)
{
static_assert(
and_<std::is_same<T, int>...>::value
, "Invalid parameter was passed" );
// ...
}
template<
typename... T
, typename = typename std::enable_if<
and_<std::is_same<T, int>...>::value
>::type
>
void
foo(T... t)
{
// ...
}
As you can see, non-type template parameters aren't used here.
Luc Danton's solution doesn't behave correctly with parameters which are not of type int, but can be implicitly converted to an int. Here's one which does:
template<typename T, typename U> struct first_type { typedef T type; };
template<typename T> int max_checked(T n) { return n; }
template<typename T1, typename T2, typename ...Ts>
int max_checked(T1 n1, T2 n2, Ts ...ns)
{
int maxRest = max_checked(n2, ns...);
return n1 > maxRest ? n1 : maxRest;
}
template<typename ...T> auto max(T &&...t) ->
decltype(max_checked<typename first_type<int, T>::type...>(t...))
{
return max_checked<typename first_type<int, T>::type...>(t...);
}
struct S { operator int() { return 3; } };
int v = max(1, 2.0, S()); // v = 3.
Here, max forwards all arguments unchanged to max_checked, which takes the same number of arguments of type int (provided by performing a pack-expansion on the first_type template). The decltype(...) return type is used to apply SFINAE if any argument can't be converted to int.
Here's how you could achieve variadic args in your max example such that it can take any number of arithmetic arguments:
template<typename T, typename ... Ts>
struct are_arithmetic{
enum {
value = std::is_arithmetic<T>::value && are_arithmetic<Ts...>::value
};
};
template<typename T>
struct are_arithmetic<T>{
enum {
value = std::is_arithmetic<T>::value
};
};
template<typename Arg, typename = typename std::enable_if<std::is_arithmetic<Arg>::value>::type>
Arg max(Arg arg){
return arg;
}
template<typename Arg, typename Arg1, typename ... Args, typename = typename std::enable_if<are_arithmetic<Arg, Arg1, Args...>::value>::type>
auto max(Arg arg, Arg1 arg1, Args ... args){
auto max_rest = max(arg1, args...);
return arg > max_rest ? arg : max_rest;
}
int main(){
auto res = max(1.0, 2, 3.0f, 5, 7l);
}
This is good because it can take any numeric type and will return the maximum number by its original type rather than just int, too.

accessing first n variadic function arguments

I have the following code :
template<size_t sz,typename T=float> class Vec{
T v[sz];
Vec(const T& val,const T&... nv){
//how do i assign `sz` number of first arguments into `this->v` array
}
}
I want to create constructor, that receive generic number of constructor argument, and assign the first sz number of arguments into member variable of v
what I want to do, is to be able doing like this: Vec<3> var(1.0,2.0,3.0);
This is possible, but complicated. Here is some code that does it. It may be possible to eliminate the holder type, but I leave that as an exercise for the reader. This has been tested with g++ 4.6.
#include <iostream>
#include <typeinfo>
template<size_t ... Indices> struct indices_holder
{};
template<size_t index_to_add,typename Indices=indices_holder<> >
struct make_indices_impl;
template<size_t index_to_add,size_t...existing_indices>
struct make_indices_impl<index_to_add,indices_holder<existing_indices...> >
{
typedef typename make_indices_impl<
index_to_add-1,
indices_holder<index_to_add-1,existing_indices...> >::type type;
};
template<size_t... existing_indices>
struct make_indices_impl<0,indices_holder<existing_indices...> >
{
typedef indices_holder<existing_indices...> type;
};
template<size_t max_index>
typename make_indices_impl<max_index>::type make_indices()
{
return typename make_indices_impl<max_index>::type();
}
template<unsigned index,typename ... U>
struct select_nth_type;
template<unsigned index,typename T,typename ... U>
struct select_nth_type<index,T,U...>
{
typedef typename select_nth_type<index-1,U...>::type type;
static type&& forward(T&&,U&&... u)
{
return select_nth_type<index-1,U...>::forward(static_cast<U&&>(u)...);
}
};
template<typename T,typename ... U>
struct select_nth_type<0,T,U...>
{
typedef T type;
static type&& forward(T&&t,U&&...)
{
return static_cast<T&&>(t);
}
};
template<unsigned index,typename ... U>
typename select_nth_type<index,U...>::type&& forward_nth(U&&... u)
{
return static_cast<typename select_nth_type<index,U...>::type&&>(
select_nth_type<index,U...>::forward(
static_cast<U&&>(u)...));
}
template<size_t sz,typename T=float> struct Vec{
struct holder
{
T data[sz];
};
holder v;
template<typename ... U>
struct assign_helper
{
template<size_t... Indices>
static holder create_array(indices_holder<Indices...>,Vec* self,U&&... u)
{
holder res={{static_cast<T>(forward_nth<Indices>(u...))...}};
return res;
}
};
template<typename ... U>
Vec(U&&... u):
v(assign_helper<U...>::create_array(make_indices<sz>(),this,static_cast<U&&>(u)...))
{}
};
int main()
{
Vec<3> v(1.2,2.3,3.4,4.5,5.6,7.8);
std::cout<<"v[0]="<<v.v.data[0]<<std::endl;
std::cout<<"v[1]="<<v.v.data[1]<<std::endl;
std::cout<<"v[2]="<<v.v.data[2]<<std::endl;
}
I believe this satisfies all the requirements:
template <size_t sz,typename T,typename... Args> struct Assign;
template <typename T,typename First,typename...Rest>
struct Assign<1,T,First,Rest...> {
static void assign(T *v,const First &first,const Rest&... args)
{
*v = first;
}
};
template <size_t sz,typename T,typename First,typename... Rest>
struct Assign<sz,T,First,Rest...> {
static void assign(T *v,const First &first,const Rest&... rest)
{
*v = first;
Assign<sz-1,T,Rest...>::assign(v+1,rest...);
}
};
template<size_t sz,typename T=float>
struct Vec{
T v[sz];
template <typename... Args>
Vec(const T& val,const Args&... nv){
Assign<sz,T,T,Args...>::assign(v,val,nv...);
}
};
This is another technique which is much simpler if it is ok not to have at least sz parameters:
template<size_t sz,typename T=float>
struct Vec {
T v[sz];
template <typename... Args>
Vec(const T& val,const Args&... nv)
{
T data[] = {val,static_cast<const T &>(nv)...};
int i=0;
for (; i<sz && i<(sizeof data)/sizeof(T); ++i) {
v[i] = data[i];
}
for (; i<sz; ++i) {
v[i] = T();
}
}
};
First declare this utility function:
template <typename T> inline void push(T* p) {}
template <typename T, typename First, typename... Args>
inline void push(T* p, First&& first, Args&&... args)
{
*p = first;
push(++p, std::forward<Args>(args)...);
}
Then, in your class:
template<size_t sz,typename T=float>
class Vec
{
T v[sz];
template <typename... Args>
Vec(T first, Args&&... args) // << we have changed const T& to T&&
{
//how do i assign `sz` number of first arguments into `this->v` array
// like this:
push(&v[0], first, std::forward<Args>(args)...);
}
}
sz is a template argument, you can directly use it in your code.
The following could work:
template <typename T, std::size_t N>
struct Foo
{
T arr[N];
template <typename ...Args> Foo(Args &&... args) : arr{std::forward<Args>(args)...} { }
};
Usage:
Foo<int, 3> a(1,2,3);
This allows you to construct the array elements from anything that's convertible to T. You can obtain the number of parameters (which can be anything not exceeding N) with sizeof...(Args).
Can you make use of something like this?
template <typename... Args> class Vec {
std :: tuple <Args...> m_args;
Vec (const Foo & a, const Bar & b, Args&&... args)
: m_args (args...)
{
// ... something with a, b
}
};
There are a few rules to constrain you:
you can only have one template... Args-style packed argument list per template class
methods which use it must have the templated arguments on the right-hand-side
You need to unpack the argument pack while keeping a count and do the necessary runtime operation in a functor. This should get you started:
template<unsigned, typename...>
struct unroll;
template<unsigned size, typename Head, typename... Tail>
struct unroll<size, Head, Tail...> {
void operator()(Head&& h, Tail&&... tail) {
// do your stuff, pass necessary arguments through the ctor of the
// struct
unroll<size - 1, Tail...>()(std::forward<Tail>(tail)...);
}
};
template<typename Head, typename... Tail>
struct unroll<1, Head, Tail...> {
void operator()(Head&& h, Tail&&... tail) {
// do your stuff the last time and do not recurse further
}
};
int main()
{
unroll<3, int, double, int>()(1, 3.0, 2);
return 0;
}
The following almost works (and for the last N arguments instead of the first, but hey). Perhaps someone can help with the compile error in the comments below:
#include <iostream>
void foo (int a, int b) {
std :: cout << "3 args: " << a << " " << b << "\n";
}
void foo (int a, int b, int c) {
std :: cout << "3 args: " << a << " " << b << " " << c << "\n";
}
template <int n, typename... Args>
struct CallFooWithout;
template <typename... Args>
struct CallFooWithout <0, Args...> {
static void call (Args... args)
{
foo (args...);
}
};
template <int N, typename T, typename... Args>
struct CallFooWithout <N, T, Args...> {
static void call (T, Args... args)
{
CallFooWithout <N-1, Args...> :: call (args...);
// ambiguous class template instantiation for 'struct CallFooWithout<0, int, int, int>'
// candidates are: struct CallFooWithout<0, Args ...>
// struct CallFooWithout<N, T, Args ...>
}
};
template <int n, typename... Args>
void call_foo_with_last (Args... args)
{
CallFooWithout <sizeof...(Args)-n, Args...> :: call (args...);
}
int main ()
{
call_foo_with_last <2> (101, 102, 103, 104, 105);
call_foo_with_last <3> (101, 102, 103, 104, 105);
}
I don't see why it's ambiguous because 0 is more specialised than N so that should satisfy the partial order ?!?!?
By contrast, the below is fine.
template <int N, typename... T>
struct Factorial
{
enum { value = N * Factorial<N - 1,T...>::value };
};
template <typename... T>
struct Factorial<0, T...>
{
enum { value = 1 };
};
void foo()
{
int x = Factorial<4,int>::value;
}
What's the difference?