Reputation: 60421
Consider the following code:
template <class T, class F>
std::size_t maximum_number_of_arguments(F&& f) {
// Code here
}
I would like a function that can find the maximum number of arguments of type T
that the callable f
can take.
For example for the following overload set:
void f(int, int); // f called with 2 ints compiles
void f(int, int, int); // f called with 3 ints compiles
void f(int, char, int, double); // f called with 4 ints compiles
void f(int, std::string, int, int, int); // f called with 5 ints does not compile
As a result:
maximum_number_of_arguments<int>(f)
should return 4
.
I guess the way to fix a limiting number of parameters a function can take (for example 256), and test all the possibility, and keep track of the calls that does not fail to compile. How to do this with template/contexpr metaprogramming?
EDIT: To answer the comments on the fact that we cannot pass an overload set to a function (my example with f
is probably not the best):
#include <iostream>
struct functor
{
void operator()(int) {std::cout << "int" << std::endl;}
void operator()(int, int) {std::cout << "int, int" << std::endl;}
void operator()(int, int, int) {std::cout << "int, int, int" << std::endl;}
};
template <class F>
void caller(F&& f)
{
std::forward<F>(f)(1);
std::forward<F>(f)(1, 2);
std::forward<F>(f)(1, 2, 3);
}
int main(int argc, char* argv[])
{
functor f;
caller(f);
return 0;
}
Upvotes: 2
Views: 102
Reputation: 66230
What you ask isn't exactly trivial...
If you interested in the maximum number of integer that alone can be used to call a function or a set of function with a common identifier, excluding the calls with other types...
I mean... if the check for the following set of f()
functions
void f(int, int);
void f(int, char, int);
void f(int, std::string, int, int, int); // excluded by std::string
should get 3
because the version that receive a std::string
is to be excluded...
Well... with a macro (so my solution is intrinsic evil) that define a check struct dedicated to a symbol
setMaxStruct(f);
you can get the maximum number of integer arguments (with a settable but defaulted maximum) writing something as follows
std::cout << "--- f: " << getMaxArgsFor_f<int>::value << std::endl;
The following is a full working example
#include <utility>
#include <iostream>
#include <type_traits>
template <typename T, std::size_t>
using typer = T;
#define setMaxStruct(func) \
\
template <typename Type, std::size_t MaxArgs = 64U> \
struct getMaxArgsFor_ ## func \
{ \
template <typename, std::size_t ...> \
static std::false_type isCallable (...); \
\
template <typename T, std::size_t ... Is> \
static auto isCallable (int) \
-> decltype( func(std::declval<typer<T, Is>>()...), \
std::true_type{} ); \
\
template <typename T, std::size_t ... Is> \
static constexpr bool getMaxTH3 \
(std::index_sequence<Is...> const &) \
{ return decltype(isCallable<T, Is...>(0))::value; } \
\
template <typename T, std::size_t I> \
static constexpr bool getMaxTH2 () \
{ return getMaxTH3<T>(std::make_index_sequence<I>{}); } \
\
template <typename T, std::size_t ... Is> \
static constexpr std::size_t getMaxTH1 \
(std::index_sequence<Is...> const &) \
{ \
std::size_t ret ( -1 ); \
\
( (ret = getMaxTH2<T, Is>() ? Is : ret), ...); \
\
return ret; \
} \
\
template <typename T, std::size_t MaxAs> \
static constexpr std::size_t getMaxT () \
{ return getMaxTH1<T>(std::make_index_sequence<MaxAs>{}); } \
\
static constexpr std::size_t value = getMaxT<Type, MaxArgs>(); \
}
void f(int, int);
void f(int, int, int);
void f(int, char, int, double);
void f(int, std::string, int, int, int);
template <typename ... Args>
void h (Args ... args);
setMaxStruct(f);
setMaxStruct(g);
setMaxStruct(h);
int main()
{
std::cout << "--- f: " << getMaxArgsFor_f<int>::value << std::endl;
std::cout << "--- g: " << getMaxArgsFor_g<int>::value << std::endl;
std::cout << "--- h: " << getMaxArgsFor_h<int>::value << std::endl;
}
Observe that from f
you get 4
, from g
you get size_t(-1)
(g()
is undefined) and from h()
you get 63
(the maximum value minus 1
).
Upvotes: 3