9

I have a class similar to the following:

class SomeClass
{
    public:
        template<typename... Args>
        void doSomething(Args && ... args);

        //... other methods etc.
};

However, what I really want is to have two different kinds of SomeClass. Ideally, I would be able to derive from a common interface to make SomeOtherClass, but I need to have a different implementation of doSomething and templated methods cannot be virtual. I could make a templated class, but then every method that takes one of these (and there are many) would themselves have to be templates etc.

The best I've been able to come up with is to implement both types of doSomething in the base class and have that method call a virtual method to determine which to use at runtime.

Is there a better solution?

Further explanation

I have many methods that look similar to this:

void foo(SomeClass * obj);

foo calls obj->doSomething and this all works fine, however I've since realized that I need a different kind of SomeClass but want it to work with these same methods, for example:

class SomeClass
{
    public:
        // This won't work
        template<typename... Args>
        virtual void doSomething(Args && ... args) = 0;

        // ... other common methods
};

class TheFirstType
{
    public:
        template<typename... Args>
        void doSomething(Args && ... args);

        // ... other specific methods
};

class TheSecondType
{
    public:
        template<typename... Args>
        void doSomething(Args && ... args);

        // ... other specific methods
};

The above would be ideal, if it were legal, but virtual methods cannot be templated. I've thus far gotten around this limitation by only having doSomething defined in the base class, but with both the implementation for TheFirstType and TheSecondType separated by an if statement that checks on what type the instance actually is:

template<typename... Args>
void SomeClass::doSomething(Args && ... args)
{
    if (this->type() == FIRST_TYPE) {
        // ... the implementation that should rightfully be part of TheFirstType
    } else if (this->type() == SECOND_TYPE) {
        // ... the implementation that should be part of TheSecondType
    }
}

This seems messy, however, so I was wondering if there is a better way.

Sydius
  • 13,567
  • 17
  • 59
  • 76

3 Answers3

8

I think @stijn's answer is correct; you have an ideal case for CRTP. You may choose to alter your logic according to that.

template<class T>
class SomeClass
{
public:
  template<typename... Args>
  void doSomething(Args && ... args)
  {
    static_cast<T*>(this)->doSomething(...);
  }
  //other method can be virtual
  virtual void foo ()
  {
    doSomething(...);
    // ... other code;
  }
};

Now simply inherit these class to your other child classes:

class TheFirstType : SomeClass<TheFirstType>
{
public:
  template<typename... Args>
  void doSomething(Args && ... args) { ... }

  virtual void foo ()
  {
  }
};   // do same for TheSecondType.

You are done.

Josh C
  • 1,035
  • 2
  • 14
  • 27
iammilind
  • 68,093
  • 33
  • 169
  • 336
  • Ah, it was the `static_cast` that hadn't occurred to me! Thanks! – Sydius Jul 16 '11 at 06:20
  • 3
    Hmm... but how can I declare a pointer to `SomeClass`, for use in the many functions I have similar to `void foo(SomeClass * obj);`? – Sydius Jul 19 '11 at 05:25
  • @Sydius, with your changed design, now `template` will follow `SomeClass` wherever it goes. You may have to change like this, `template void foo(SomeClass *obj)`. So now you have exclusive copy of `foo` for every type. This will add some code but execution speed will be faster. – iammilind Jul 19 '11 at 06:05
  • 3
    that's really what I was trying to avoid because of the ripple effect it will have on my code (since callers of those methods would themselves have to be aware of which subclass they're dealing with and be templated etc.). It's a possibility, and I'm starting to think there really isn't a good way to get around not being able to have virtual template methods. – Sydius Jul 19 '11 at 20:35
  • @Sydius, why caller would have to worry ? It's the compiler who will be taking care of generating a new copy. It's as simple as this: `SomeClassType *pSome = new TheFirstType(); ...; foo(pSome);` You can always put `template` argument `` because you know that you are `new`ing up `TheFirstType`. When it's passed to `foo()`; compiler will generate a copy of `foo` automatically. I feel you can give a try once; may be it will fit your exact needs. Good luck. – iammilind Jul 20 '11 at 01:44
  • Downside being everything that touches it becomes a template and has to put in a header (without resorting to including cpp files ofcourse) – puio Jan 03 '21 at 17:44
3

my guess is that you're after CRTP (although I can't be sure, as iammilind points out in the comment):

template< class Parent >
class SomeClassBase : public Parent
{
public:
  //common methods go here
};

class SomeClass : public SomeClassBase< SomeClass >
{
public:
  template<typename... Args>
  void doSomething(Args && ... args);
};

class SomeOtherClass : public SomeClassBase< SomeOtherClass >
{
public:
  template<typename... Args>
  void doSomething(Args && ... args);
};
stijn
  • 34,664
  • 13
  • 111
  • 163
  • This is very nearly what I need, but unfortunately, I have a lot of methods that take a pointer to what would be the base class (they need to accept either type polymorphically) and those methods need to be able to call `doSomething`. – Sydius Jul 15 '11 at 23:27
  • you cannot have a virtual method that is a template. – Sydius Jul 16 '11 at 17:57
  • no but you can have one in a templated class; that's what I was thinking of when I posted the comment but I overlooked the fact that doSomething is a template indeed, sorry ;] – stijn Jul 16 '11 at 18:06
-1

What might be helpful in this case is the use of a macro function in the wrapper class, SomeClass. The benefit of macros is text substitution. Normally we can't pass a function with all of its parameters as an object in C++ (at least before C++11, past that, I'm not sure). However, with macros, we can pass just about any type of text. The added benefit here is that it will remove the repeated code of if/else for every function that has this template-virtual conflict.

We could set up the macro as follows:

#define CLASS_SELECT(func) \
do { \
    if (this->type() == FIRST_TYPE) { \
        theFirstType.func; \
    } else if (this->type() == SECOND_TYPE) { \
        theSecondType.func; \
    } \
} while (0)

If you're unfamiliar with the do while encapsulation, look here. The class would use the macro like:

class SomeClass
{
    TheFirstType theFirstType;
    TheSecondType theSecondType;
public:
    template<typename... Args>
    void doSomething(Args && ... args)
    {
        CLASS_SELECT(doSomething (&args...) );
    }
};

In the example above, I instantiated the specialized objects inside SomeClass. I'm not sure if that is the design you would prefer, and if it isn't I'd look into using static getter functions or the like within the macro.

Anyways, I acknowledge my solution might not be ideal, but its the best I've got so far (I have the same issue as you in my code). I will admit the CRTP referenced in other answers is pretty cool, though it didn't fit my needs (having to specify template in parameter won't fly for me either, especially at runtime).

Clint Chelak
  • 232
  • 2
  • 9