views:

165

answers:

2

This is best described in pseudo-code:

class Thing {};

interface ThingGetter<T extends Thing> {
    T getThing();
}

class Product extends Thing {};

class ProductGetter<Product> {
    Product getThing() {
        // Some product code
    }
}

class SpecialProductGetter extends ProductGetter {
    Product getThing() {
        p = ProductGetter::getThing();
        // Do some special stuff;
        return p;
    }
}

class NeatProductGetter extends ProductGetter {
    Product getThing() {
        p = ProductGetter::getThing();
        // Do some neat stuff;
        return p;
    }
}

I will have other "things" with other getters for those as well.

I've tried codifying this in C++ but it doesn't like :

template <> class ThingGetter <Product> {

nor:

class NeatProductGetter : public ThingGetter <Product> {
  1. Can you model this in C++ without making getThing return a Thing and then having to cast it like crazy?
  2. If so, how? If not, what is the best way to do this?

Thanks!

+1  A: 

In C++, this would look like this: (I've changed "class" to "struct" to avoid a bunch of "public:" declarations. It's the same thing, though.)

class Thing { };

template <typename T>
struct ThingGetter {
    T getThing();
};

struct Product : public Thing  { };

struct ProductGetter : public ThingGetter<Product>{
    Product getThing() { return Product(); }
};

struct SpecialProductGetter : public ProductGetter {
    Product getThing() {
        Product p = this->ProductGetter::getThing();
        return p;
    }
};

struct NeatProductGetter : public ProductGetter {
    Product getThing() {
        Product p = ProductGetter::getThing();
        return p;
    }
};

...

SpecialProductGetter spg;
spg.getThing();
NeatProductGetter npg;
npg.getThing();

Another solution is to use template specialization:

struct Thing { };
struct Widget { };

template <typename T>
struct Getter {
    T getIt() = 0;
};

template <>
struct Getter<Thing> {
    Thing getIt() { return Thing(); }
};

template <>
struct Getter<Widget> {
    Widget getIt() { return Widget(); }
};
typedef Getter<Widget> WidgetGetter;

struct SpecialWidgetGetter : public WidgetGetter {
    Widget getIt() { return this->WidgetGetter::getIt(); }
};

struct FizzledWidgetGetter : public WidgetGetter {
    Widget getFizzledWidget() { return getIt().fizzle(); }
};


...

Getter<Thing> tg;
tg.getIt();
WidgetGetter wg;
wg.getIt();
Tim Sylvester
Thanks! I'm going to try to implement the first version. I tried doing it with template specialization but can you make subclasses then? like a `SpecialGetter<Thing>` and `NeatGetter<Thing>` that share some code in `Getter<Thing>`?
Paul Tarjan
Sure, you can inherit from a template class whether you specialize it or not. In fact, this is exactly what's happening in the first example when I say "ProductGetter : public ThingGetter<Product>", that is, ProductGetter inherits from ThingGetter specialized to Products. I updated the second part with examples.
Tim Sylvester
Actually, I just realized that once you do that they're basically the same example.
Tim Sylvester
The interface you defined is flawed. Java-like syntax but different semantics, if you use a ThingGetter you will only be able to get T by value, and that will mean slicing for all the classes that implement that interface. The method should probably be virtual also (so that it can be overriden in derived classes) You should probably change the interface into `template <typename T> struct ThingGetter { virtual T* getThing(); }`
David Rodríguez - dribeas
They start to converge, except you ended up with `Getter<Widget>` being a typedef instead of a class declaration. I think I'll stick with the class 'cause then I can add helper methods for the children to use.
Paul Tarjan
... another thing, about template specialization, is that there is no relation between the different templates (general/specialized) so that providing a dumb unimplemented virtual (you cannot leave a non-virtual method unimplemented) in the generic template is useless, unless your intention is flagging a compiler error if user code tries to instantiate the template with anything different than Widget or Thing.
David Rodríguez - dribeas
+1  A: 

Since the question comes from someone with a Java background, I will interpret it in Java terms. You want to define a generic interface that will return an object derived from T:

template<typename T>
struct getter
{
   virtual T* get() = 0; // just for the signature, no implementation
};

Note the changes: the function is declared virtual so that it will behave polimorphically in derived objects. The return value is a pointer instead of an object. If you keep an object in your interface, the compiler will slice (cut the non-base part of the returned object) the return. With some metaprogramming magic (or resorting to boost) you can have the compiler test the constraint that T derives from a given type.

Now the question is why you would like to do so... If you define a non-generic interface (abstract class) that returns a Thing by pointer you can just get the same semantics more easily:

struct Thing {};
struct AnotherThing : public Thing {};
struct getter
{
   virtual Thing* get() = 0;
};
struct AnotherGetter : public getter
{
   virtual AnotherThing* get() { return 0; }
};
struct Error : public getter
{
    int get() { return 0; } // error conflicting return type for get()
};
struct AnotherError : public getter
{
};
int main() {
   AnotherError e; // error, AnotherError is abstract (get is still pure virtual at this level)
}

The compiler will require all instantiable classes derived from getter to implement a get() method that returns a Thing by pointer (or a covariant return type: pointer to a class derived from Thing). If the derived class tries to return another type the compiler will flag it as an error.

The problem, as you post it is that if you use the interface, then the objects can only be handled as pointers to the base Thing class. Now, whether that is a problem or not is another question... In general, if you have correctly designed your hierarchy, you should be able to use the returned objects polimorphically without having to resort to down casting.

Note that if you are using the different getters at the most derived level in the hierarchy, each get() method returns the most derived element from the Thing hierarchy, at that point you will not need to down cast at all. Only if you use the different getters through the base getter interface you will get the same return type for all (Thing*)

It is important to note that templates are resolved completely at compile time. That means that trying to use templates to solve the need (do you really need it?) to downcast will not help you if you use the different getters through the interface (reference/pointers to the basic getter).

Maybe posting a little more details on your specific domain, where and how you intend to use this code, can help in providing more useful responses.

David Rodríguez - dribeas
I wasn't aware that a child method can return a subclass of what the parent returns and still be considered "overriding" instead of "overloading". Neat, I'll try this.
Paul Tarjan
That is also possible in Java, from Java 5 on you can overload a parent method with a covariant return type (type that is derived from the type returned by the overriden method in the parent class)
David Rodríguez - dribeas