1

I'm trying to write a Testbench that can test different implementations of an Interface. Coming from Java I would love to just specify an Interface, create some Classes that implement it and write a class Testbench<T extends MyInterface>

Transforming this approach to C++ gives something like:

// The Interface
class Animal {
public:
    Animal(int age) {};

    virtual ~Animal() {};
    virtual void Say();
};

// An Implementor
class Cow : Animal {
private:
    int age;

public:
    Cow(int age) : Animal(age) {
        this.age = age;
    };
    void Say() {
        std::cout << "I'm an " << age << " year old Cow" << std::endl;
    }
}

Next I define the templated class that can test different Animals:

template<> void AnimalTestbench<class T> {
   static void TestSay();
}

But when I try to implement the TestSay method, it gives me "Allocation of incomplete type T"

template<> void AnimalTestbench<class T>::TestSay() {
    T *animal = new T(5);
    animal->Say();
}

Of course I didn't specify that T should be an Animal, this is my first question. The latter is: why does this code fail?

I've heard that Templates are a fancy way for Macros that know about types, but if it's a Macro in a way, then the compiler should first replace T with my (complete) Type which he should be able to allocate and instantiate.

KillPinguin
  • 390
  • 4
  • 15
  • 1
    1) `template<> void AnimalTestbench` huh? Didn't you mean `template void AnimalTestbench`? Consider learning from a [good C++ book](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list). 2) In addition: `this.age = age;` doesn't compile as well. It should be, at the very least, `this->age = age;` (or you could use constructor initializer list to initialize members). – Algirdas Preidžius Jun 20 '19 at 15:17
  • Also, you don't need templates. If you have a concrete interface, you can use a pointer to the base class. And the base class should be abstract if it's not intended to be an instance on its own. And the age should be in the base class as well (probably). As @AlgirdasPreidžius said, you need a good C++ book. You're translating Java concepts in a "not quite right" way to C++. – Kevin Anderson Jun 20 '19 at 15:25
  • A C++ book seems like a good suggestion. That `new T` leaks. – MSalters Jun 20 '19 at 15:25
  • Be sure to implement it in the header file or to explicitly define it in the header. Also `TestSay` leaks `animal`. – Neijwiert Jun 20 '19 at 15:25
  • I want to use templates because the tests should invoke the constructors of their targeted classes. Or could I also do that with pointers? The memory leak was just to keep the example small but you're right I should definitely deconstruct the animal after the test. – KillPinguin Jun 20 '19 at 15:43

2 Answers2

4

There are a number of issues with your code:

  • The Animal class should declare Say as pure virtual
  • The Animal class uses this. instead of this->
  • The Cow class does not derive publicly from Animal
  • The AnimalTestbench class does not use templates correctly, template<> defines a specialization, which is not what you want
  • T *animal = new T(5); is a memory leak, because a delete doesn't follow.
    • we don't need to allocate at all, actually

Fixed Animal class:

class Animal {
public:
    Animal(int) {};

    virtual ~Animal() {};
    virtual void Say() = 0;
};

Fixed Cow class:

class Cow : public Animal {
private:
    int age;

public:
    Cow(int age) : Animal(age) {
        this->age = age;
    };
    void Say() override{
        std::cout << "I'm an " << age << " year old Cow" << std::endl;
    }
};

Fixed AnimalTestbench (we don't need to separate the impl of TestSay from the declaration, but I'm following your approach:

template<class T>
struct AnimalTestbench
{
   static void TestSay();
};

template<class T>
void AnimalTestbench<T>::TestSay() {
    T animal(5);
    Animal *animal_base = &animal;
    animal_base->Say();
}

Usage:

int main()
{
    AnimalTestbench<Cow>::TestSay();
}

TestSay could be a standalone templated function, but I presume there are other virtual functions you wish to test and it's convenient to put them all in a single test class.

Demo

Community
  • 1
  • 1
AndyG
  • 39,700
  • 8
  • 109
  • 143
0

The correct syntax for template classes is:

template<typename T> void AnimalTestbench {
   static void TestSay();
}

And the method:

template<typename T> void AnimalTestbench<T>::TestSay() {
    T *animal = new T(5);
    animal->Say();
}
Silvano Cerza
  • 954
  • 5
  • 16