5

Possible Duplicate:
Is it possible to write a C++ template to check for a function's existence?

say there are 2 classes:

struct A{ int GetInt(){ return 10; } };
struct B{ int m; };

I want to use object of type A or B in following function

tempate< typename T >
int GetInt( const T & t )
{
   //if it's A, I'll call: return t.GetInt();
   //if its' B, I'll call: return t.m;
}

Now, because there are whole bunch of classes, some contain GetInt(), some don't, I don't want to write specialization for each type, I only want to distinguish them by 'containing GetInt() or not in compile time', how should I do this ?

Community
  • 1
  • 1
JQ.
  • 678
  • 7
  • 17
  • 2
    Perhaps you should look into virtual functions and polymorphism. – JoshD Oct 18 '10 at 23:47
  • Unless you are going to assume your non-`GetInt` instantiating classes all have an `int` member called `m` I don't see how you can extend this without specializations for each. What is the default implementation of your function template? – Steve Townsend Oct 18 '10 at 23:52
  • 2
    You cannot make that function work because `GetInt` isn't const, but `t` is. – GManNickG Oct 18 '10 at 23:58

4 Answers4

6

Substitution Failure Is Not An Error, or more compactly, SFINAE

But in your particular case, you don't need SFINAE, virtual members, or anything fancy like that.

You just need an ordinary overloaded function.

int GetInt(A& t) { return t.GetInt(); }
int GetInt(const B& t) { return t.m; }

If there's code that needs to be shared between the different versions, refactor it so that there's a template that calls an overloaded inline function, all type-specific behavior is in the inline function, and all shared behavior is in the template.

For your "I have many many classes" need, SFINAE would look more or less like this:

template<typename T>
int GetInt(const T& t, int (T::*extra)() const = &T::GetInt)
{
    return t.GetInt();
}

template<typename T>
auto GetInt(const T& t) -> decltype(t.m)
{
    return t.m;
}

EDIT: The reality of SFINAE is much uglier, at least until C++0x comes around. In fact it starts looking just as bad as GMan's answer.

struct A{ int GetInt() const { return 10; } };
struct B{ int m; };

template<typename T, int (T::*extra)() const>
struct has_mfunc
{
    typedef int type;
};

template<typename T>
typename has_mfunc<T, &T::GetInt>::type GetInt(const T& t)
{
    return t.GetInt();
}

template<typename T, typename U, U (T::*extra)>
struct has_field
{
    typedef U type;
};

template<typename T>
typename has_field<T, int, &T::m>::type GetInt(const T& t)
{
    return t.m;
}

int main(void)
{
   A a;
   B b;
   b.m = 5;
   return GetInt(a) + GetInt(b);
}
Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • Did you mean t.*extra() instead of t.GetInt()? I don't see how to use the 'extra' parameter – Chubsdad Oct 19 '10 at 03:27
  • 1
    @Chubsdad: The extra parameter is present solely to cause substitution failure, so that the overload is removed from the list of matches **before** the template body is instantiated (at which time the compiler would error out trying to find `B::GetInt`). – Ben Voigt Oct 19 '10 at 03:30
  • 1
    Worth mentioning decltype's not in the current Standard... :-/. Also, question makes it very clear "I don't want to write specialization for each type" - which I'd take to exclude the overloading suggestion. – Tony Delroy Oct 19 '10 at 03:37
  • @Tony: easy enough to use a pointer-to-member to check for `t.m` as well, just thought since I'd already given an example of that, I'd give an example with `decltype` too. – Ben Voigt Oct 19 '10 at 03:40
  • thanks guys, but the code doesn't compile. – JQ. Oct 19 '10 at 05:13
  • @Ben: could you share how to do that without making the GetInt lookup ambiguous? Thanks. – Tony Delroy Oct 19 '10 at 05:53
  • 1
    @Tony: apparently default parameters don't get resolved until it's too late for SFINAE to kick in. With C++0x, suffix return type using decltype will make this easy. Until then, it's quite the mess. – Ben Voigt Oct 19 '10 at 06:05
  • +1 for seeing it through. So used to this ugly mess I hadn't considered how much simpler it will be under C++0x... thanks for that :-). – Tony Delroy Oct 19 '10 at 06:56
4

Stealing from here, and assuming you fix your code so GetInt is const, we get:

HAS_MEM_FUNC(GetInt, has_GetInt);

template <bool B>
struct bool_type
{
    static const bool value = B;
};

typedef bool_type<true> true_type;
typedef bool_type<false> false_type;

namespace detail
{
    template <typename T>
    int get_int(const T& pX, true_type)
    {
        return pX.GetInt();
    }

    template <typename T>
    int get_int(const T& pX, false_type)
    {
        return pX.m;
    }
}

template <typename T>
int get_int(const T& pX)
{
    return detail::get_int(pX,
                            has_GetInt<T, int (T::*)() const>::value);
}

This is pretty awful design though. You should fix the problem rather than apply a patch.

Community
  • 1
  • 1
GManNickG
  • 494,350
  • 52
  • 494
  • 543
0

Technically it just involves a few template arcana, which you can find by googling e.g. has_member or the like. Off the cuff, in the detection code, if I were to write such, I'd just sort of fake-derive from the class in question, and check size of derived class' member.

However, don't do that.

What else to do depends. But it seems like your classes conform to two different "schemas", so to speak, without those schemas being available via the type system (like, it seems the classes don't derive from two base classes A and B). Then one option is to introduce a traits template that tells you wrappers whether the template param T is schema A or B. Specialize the traits for each relevant class that differs from the default. Choose the default so as to minimize work.

Cheers & hth.,

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
0

This is exactly what inheritance is for. You can easily use use dynamic_cast for is type of questions at runtime. For example you can define an abstract base class called HasGetInt and derive the classes that need that function from it and not reinvent the wheel.

rerun
  • 25,014
  • 6
  • 48
  • 78
  • In a templated type he can do what ever he wants until it get instantiated with a type. He could certainly overload template functions types but I often find these types of solutions better solved by an interface model. – rerun Oct 19 '10 at 00:17
  • thanks guys, but the code is legacy, I try not to change the existing code, but add some utility functions on top of it to make life easier. – JQ. Oct 19 '10 at 05:24