views:

163

answers:

4

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 ?

A: 

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.,

Alf P. Steinbach
A: 

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
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
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
+4  A: 

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.

GMan
awesome, thanks for stealing for me!
JQ
+6  A: 

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
Did you mean t.*extra() instead of t.GetInt()? I don't see how to use the 'extra' parameter
Chubsdad
@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
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
@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
thanks guys, but the code doesn't compile.
JQ
@Ben: could you share how to do that without making the GetInt lookup ambiguous? Thanks.
Tony
@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
+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