views:

96

answers:

4

I have the code as below. I have a abstract template class Foo and two subclasses (Foo1 and Foo2) which derive from instantiations of the template. I wish to use pointers in my program that can point to either objects of type Foo1 or Foo2, hence I created an interface IFoo.

My problem is I'm not sure how to include functionB in the interface, since it is dependant on the template instantiation. Is it even possible to make functionB accessible via the interface, or am I attempting the impossible?

Thank you very much for your help.

class IFoo {
    public:
        virtual functionA()=0;

};

template<class T>
class Foo : public IFoo{
    public:
        functionA(){ do something; };
        functionB(T arg){ do something; };
};

class Foo1 : public Foo<int>{
...
};

class Foo2 : public Foo<double>{
...
};
A: 

I don't think you can get what you want. Think of this if you were to implement your suggestion: if you have a pointer to an IFoo instance and you call functionB(), what type parameter should you give it? The underlying problem is that Foo1::functionB and Foo2::functionB have different signatures and do different things.

Karmastan
But technically I will not be creating an instance of IFoo, I only wish to create an instance of Foo1 (or 2) (pointed to by an IFoo pointer) and I assumed the appropriate function could be found by dynamic bounding?
bishboshbash
@bishboshbash: For a function to be found by dynamic binding all the argument types must be known; otherwise it would be unclear what to look for if you used overloaded functions.
liori
I think I understand. If I created instance of Foo1 and called Foo1.functionB(..) it would work fine, since the template has been instantiated.Could I create an abstract class to point to objects of type Foo1 or Foo2 through multiple inheritance and therefore avoid the problem of the dynamic binding attempting to pass through an uninstantiated template?
bishboshbash
+1  A: 

Easiest way is to make your interface templated.

template <class T>
class IFoo {
    public:
        virtual void functionA()=0;
        virtual void functionB(T arg){ do something; };
};

template<class T>
class Foo : public IFoo<T>{
    public:
        void functionA(){ do something; };
        void functionB(T arg){ do something; };
};
Igor Zevaka
This would mean I could not create pointers of type IFoo and have them pointing to instantiations of Foo1 or Foo2.
bishboshbash
That's correct, and that is a fundamental issue here. The type of the argument that `functionB` takes depends on the instantiated template type and **must** be known at compile time.
Igor Zevaka
+1  A: 

Since functionB's argument type must be known in advance, you have only one choice: Make it a type which can hold every possible argument. This is sometimes called a "top type" and the boost libraries have the any type which gets quite close to what a top type would do. Here is what could work:

#include <boost/any.hpp>
#include <iostream>
using namespace boost;

class IFoo {
    public:
    virtual void functionA()=0;
    virtual void functionB(any arg)=0; //<-can hold almost everything
};

template<class T>
class Foo : public IFoo{
    public:
        void functionA(){  };
        void real_functionB(T arg)
        {
         std::cout << arg << std::endl;
        };
        // call the real functionB with the actual value in arg
        // if there is no T in arg, an exception is thrown!

        virtual void functionB(any arg)
        {
            real_functionB(any_cast<T>(arg));
        }
};

int main()
{
    Foo<int> f_int;
    IFoo &if_int=f_int;

    if_int.functionB(10);

    Foo<double> f_double;
    IFoo &if_double=f_double;
if_int.functionB(10.0);

}

Unfortunately, any_cast does not know about the usual conversions. For example any_cast<double>(any(123)) throws an exception, because it does not even try to convert the integer 123 to a double. If does not care about conversions, because it is impossible to replicate all of them anyway. So there are a couple of limitations, but it is possible to find workarounds if necessary.

Luther Blissett
To use this answer, the caller (`main()`) must know the actual subtype of an `IFoo` instance (`if_int` and `if_double`) so that it can pass in the correct arguments. If it gets it wrong, a runtime exception is thrown! So then, why not have the call just use a `dynamic_cast`?
Karmastan
If I have the caller to use dynamic_cast, then I need him to know about Foo<int>,Foo<double>.But the nice thing about an abstract interface is, that I don't need to tell anybody, how the interface is actually implemented. It can be a private, 3rd-party type from a shared library or a local type in a function body. Also, it is only the limitation of boost's any_cast that it throws for the wrong argument type. You are free to enhance its functionality, such that it does the right conversions for intrinsic types for example. What can't be done, though, is to catch **all** valid conversions.
Luther Blissett
+1  A: 

You are actually attempting the impossible.

The very heart of the matter is simple: virtual and template do not mix well.

  • template is about compile-time code generation. You can think of it as some kind of type-aware macros + a few sprinkled tricks for meta programming.
  • virtual is about runtime decision, and this require some work.

virtual is usually implemented using a virtual tables (think of a table which lists the methods). The number of methods need be known at compile time and is defined in the base class.

However, with your requirement, we would need a virtual table of infinite size, containing methods for types we haven't seen yet and that will only be defined in the years to come... it's unfortunately impossible.

And if it were possible ?

Well, it just would not make sense. What happens when I call Foo2 with an int ? It's not meant for it! Therefore it breaks the principle that Foo2 implements all the methods from IFoo.

So, it would be better if you stated the real problem, this way we could help you at a design level rather than at a technical level :)

Matthieu M.
I have to classes Foo1 and Foo2. They both contain similar data structures: one contains ints and one contains rational numbers (pairs of ints). They have many methods which are implemented identically (respective to int/pair of int), which interact with other (int/pair of int) data types themselves. But they have a few methods which are implemented differently.I originally implemented it all with inheritance but then the data structures are defined in the subclasses and so I cannot write member functions which act on them in the base class, which leads to large amounts of code duplication.
bishboshbash