4

I'm wondering if there is a way in C++11 to get the number of arguments of a function?

For example for the function foo I want argCount to be 3.

void foo(int a, int b, int c) {}

int main() {
  size_t argCount = MAGIC(foo);
}
Aykhan Hagverdili
  • 28,141
  • 6
  • 41
  • 93
Mayhem
  • 487
  • 2
  • 5
  • 13

3 Answers3

12

You can get that information by using a variadic function template.

#include <iostream>

template <typename R, typename ... Types> constexpr size_t getArgumentCount( R(*f)(Types ...))
{
   return sizeof...(Types);
}

//----------------------------------    
// Test it out with a few functions.
//----------------------------------    

void foo(int a, int b, int c)
{
}

int bar()
{
   return 0;
}

int baz(double)
{
   return 0;
}

int main()
{
    std::cout << getArgumentCount(foo) << std::endl;
    std::cout << getArgumentCount(bar) << std::endl;
    std::cout << getArgumentCount(baz) << std::endl;
    return 0;
}

Output:

3
0
1

See it working at http://ideone.com/oqF8E8.

Update

Barry suggested use of:

template <typename R, typename ... Types> 
constexpr std::integral_constant<unsigned, sizeof ...(Types)> getArgumentCount( R(*f)(Types ...))
{
   return std::integral_constant<unsigned, sizeof ...(Types)>{};
}

With this, you can get the number of argument by using:

// Guaranteed to be evaluated at compile time
size_t count = decltype(getArgumentCount(foo))::value;

or

// Most likely evaluated at compile time
size_t count = getArgumentCount(foo).value;
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • Too complicated :) see my answer for a shorted example. – SergeyA Apr 22 '16 at 15:41
  • @SergeyA, thanks for the prod. It's now more simplified than your answer :) – R Sahu Apr 22 '16 at 15:49
  • So very much true! :) The only thing is, mine doesn't call a function, while yours does. You need to make yours constexpr, but this would still be no guarantee of it never being called - this is why I prefer my version. However, yours have simpler code. – SergeyA Apr 22 '16 at 15:51
  • @RSahu Prefer to just return an `integral_constant` than `size_t` (see Columbo's answer on the dupe) – Barry Apr 22 '16 at 15:59
  • @Barry, my understanding of `integral_constant` being used in the dupe is to simplify the implementation of `struct get_arity`. For a free function, such as I presented here, that won't add any extra value. Please let me know if I am missing something subtle. – R Sahu Apr 22 '16 at 16:03
  • @RSahu It adds extra value from the perspective of encoding the answer in a type. It's always easy to go type --> value, but keeping everything on the type level just makes metaprogramming easier. Plus, you can then just pass this into another function and overload on different arity. – Barry Apr 22 '16 at 16:05
  • @SergeyA, Yes, making the function a `constexpr` definitely makes sense, regardless of whether the compiler translates that into a compile time evaluation or a run time call. – R Sahu Apr 22 '16 at 16:12
  • @Barry, I still don't quite grasp the value of your suggestion. I'm going need some time to mull over it. Thanks for the input. – R Sahu Apr 22 '16 at 16:13
  • How can we make this work also for member functions ? – Muhammet Ali Asan Aug 15 '19 at 12:30
  • @MuhammetAliAsan, that's a good question. If that has not been asked in SO, please create a post and ask it. You can add a link to this post from your post, just to provide some context. – R Sahu Aug 15 '19 at 15:18
9

Yes, it can be easily done:

#include <cstddef>
#include <iostream>

template <class R, class... ARGS>
struct function_ripper {
    static constexpr size_t n_args = sizeof...(ARGS);
};


template <class R, class... ARGS>
auto constexpr make_ripper(R (ARGS...) ) {
  return function_ripper<R, ARGS...>();
}

void foo(int, double, const char*);

void check_args() {
  constexpr size_t foo_args = decltype(make_ripper(foo))::n_args;

  std::cout << "Foo has  " << foo_args << " arguments.\n";
}
SergeyA
  • 61,605
  • 5
  • 78
  • 137
1

This doesn't really make sense for several reasons.

For starters, what would this really be good for? You might be looking for some sort of reflection, but that doesn't (yet) exist in C++.

The main reason this doesn't make sense, however, is overload sets:

void f(int);
void f(int, int);
std::cout << MAGIC(f); // what should this print??
Ven
  • 19,015
  • 2
  • 41
  • 61
  • 1
    There is a way to do this. – SergeyA Apr 22 '16 at 15:41
  • The fact you can do it doesn't mean it makes sense, however. But yes, you're correct. – Ven Apr 22 '16 at 15:42
  • 4
    It makes perfect sence in many applications of template metaprogramming. – SergeyA Apr 22 '16 at 15:43
  • 2
    Why say it makes no sense when you don't even know what the use case is? This is exactly what is required for binding stack based scripting language native calls, you need the argument count. – paulm Feb 26 '17 at 00:48