Creating an interface for an abstract class template in C++

44,510

Solution 1

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 :)

Solution 2

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; };
};

Solution 3

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.

Share:
44,510
bishboshbash
Author by

bishboshbash

Updated on April 21, 2020

Comments

  • bishboshbash
    bishboshbash about 4 years

    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>{
    ...
    };
    
  • bishboshbash
    bishboshbash almost 14 years
    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?
  • liori
    liori almost 14 years
    @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.
  • bishboshbash
    bishboshbash almost 14 years
    This would mean I could not create pointers of type IFoo and have them pointing to instantiations of Foo1 or Foo2.
  • bishboshbash
    bishboshbash almost 14 years
    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?
  • Igor Zevaka
    Igor Zevaka almost 14 years
    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.
  • Karmastan
    Karmastan almost 14 years
    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?
  • Nordic Mainframe
    Nordic Mainframe almost 14 years
    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.
  • bishboshbash
    bishboshbash almost 14 years
    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.
  • h9uest
    h9uest over 9 years
    @bishboshbash your requirement is ill-formed in the first place. Foo1 and Foo2 have different base classes(IFoo<int> and IFoo<double> are two different types).
  • Jens Munk
    Jens Munk over 7 years
    I have had the same issues many times and with classes containing many methods and properties in their interface (10+). What I did was to create interfaces for each instance and cut down the number of methods using closures, e.g. ErrorCode ScalarParameterSet(ParamType t, float ParamValue) for uniting all setters. If you want, I could provide an example of this