4

Is it possible to write c++ template/macros to check whether two functions have the same signatures (return type and arguments list) ?

Here's a simple example of how I want to use it:

int foo(const std::string& s) {...}
int bar(const std::string& s) {...}

if (SAME_SIGNATURES(foo, bar))
{
    // do something useful... make Qt signal-slot connection for example...
}
else
{
    // signatures mismatch.. report a problem or something...
}

So is it possible somehow or is it just a pipe dream ?

P.S. Actually I'm interesting in c++ 2003 standard.

sergeyz
  • 1,308
  • 2
  • 14
  • 23

3 Answers3

17

C++11 Solution

No need to write any template yourself.

You can use decltype along with std::is_same:

if (std::is_same<decltype(foo),decltype(bar)>::value )
{
    std::cout << "foo and bar has same signature" << std::endl;
}

Here decltype returns the type of the expression which is function in this case, and std::is_same compares the two types, and returns true if both are same, else false.


C++03 Solution

In C++03, you don't have decltype, so you can implement overloaded function templates as:

template<typename T>
bool is_same(T,T) { return true; }

template<typename T, typename U>
bool is_same(T,U) { return false; }

Now you can use it as:

if (is_same(foo, bar))
{
    std::cout << "foo and bar has same signature" << std::endl;
}

Now that in this case is_same is a function template, not class template. So it is evaluated at runtime as opposed to compile-time. So this will give error:

int a[is_same(foo,bar) ? 10 : 20]; //error (in Standard C++03)
                                   //the size must be known at compile-time!

However, if you need to know it at compile-time, then you've to work more, and implement the functionality as:

typedef char same[1];
typedef char different[2];

template<typename T>
same& is_same_helper(T,T);  //no need to define it now!

template<typename T, typename U>
different& is_same_helper(T,U); //no definition needed!

#define is_same(x,y)   (sizeof(is_same_helper(x,y)) == sizeof(same))

Now use it as:

if (is_same(foo, bar))
{
    std::cout << "foo and bar has same signature" << std::endl;
}

You can use it at compile-time also. so you can write it:

int a[is_same(foo,bar) ? 10 : 20]; //okay

Hope that helps.

Community
  • 1
  • 1
Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • decltype was introduced in C++11 standard. Is it possible for c++ 2003? – sergeyz Jan 26 '13 at 21:06
  • 1
    pass foo and bar as arguments to a template function, that's another way to get the types automatically deduced. – Marc Glisse Jan 26 '13 at 21:09
  • Need to instantiate value explicitly. An alternative would be to have is_same(T,U) return false and an overload is_same(T,T) returns true. – Marc Glisse Jan 26 '13 at 21:14
  • @MarcGlisse: That is nice. I will opt for that instead. Let me edit it. – Nawaz Jan 26 '13 at 21:16
  • @Nawaz what you say is impossible is exactly what you posted as an answer, so there must be a misunderstanding ;-) – Marc Glisse Jan 26 '13 at 21:16
  • The version with is_same_helper is good for pedagogy, you may want to keep it. – Marc Glisse Jan 26 '13 at 21:17
  • @MarcGlisse: Yes, I misunderstood your comment. I thought you're talking about *class* template deducing the type arguments. – Nawaz Jan 26 '13 at 21:19
  • I know I'm being pedantic, but the choice of `is_name` as a name for the function template is maybe a bit unfortunate, because it could clash with the name of the `is_same<>` class template from the standard `type_traits` header. But yeah, +1 :-) – Andy Prowl Jan 26 '13 at 21:21
  • @AndyProwl: That is fine. You can write your own `iostream`, `fstream`, `copy`, `remove_if` etc, as long as you keep them in *your* namespace. And that is what *namespaces* are for; to avoid name clash. Boost has defined so many names which otherwise would clash with `std` names! – Nawaz Jan 26 '13 at 21:24
  • 1
    Make the overloads return types with sizes one and two, so you can tell the difference at compile time with sizeof. – R. Martinho Fernandes Jan 26 '13 at 21:41
3

What about something like this:

#include <iostream>

void a(int)
{ }

void a2(int)
{ }

void b(float)
{ }

struct true_type
{ enum { value = 1 }; };

struct false_type
{ enum { value = 0 }; };

template <typename T, typename U>
false_type
is_same_sig (T, U)
{ return false_type (); }

template <typename T>
true_type
is_same_sig (T, T)
{ return true_type (); }

int
main ()
{
  std::cout << is_same_sig (a, a2).value
            << is_same_sig (a, b).value
            << "\n";
}
wilx
  • 17,697
  • 6
  • 59
  • 114
2

Here is another alternative solution that works with C++03:

#include <iostream>

using namespace std;

template<typename F1, typename F2>
bool same_signature(F1 const&, F2 const&)
{
    return false;
}

template<typename F>
bool same_signature(F const&, F const&)
{
    return true;
}

void test1(std::string, int) { }
void test2(std::string, int) { }
void test3(std::string, double) { }

int main()
{
    cout << same_signature(test1, test2);
    cout << same_signature(test1, test3);
}
Andy Prowl
  • 124,023
  • 23
  • 387
  • 451