Consider the following base interface and two concrete implementations of it. I would like to test these with 1 template method, with arguments that are the types to be tested. All the sub-types (QueueA, QueueB) inherit from IQueue. How could I achieve an implementation for the template method tester_queues? Similar functionality can be achieved by passing in a std::vector of instances of the classes, and would be easier. I am just curious if something like what is described in method tester_queues is possible to achieve.
template<class T>
class IQueue
{
protected:
T Data[10];
public:
virtual const T pop() = 0;
virtual void push(const T&) = 0;
};
template<class T>
class QueueA : public IQueue<T>
{
const T pop() final {
std::cout << "pop queue A" << std::endl;
return T{};
}
void push(const T& aItemRef) final {
std::cout << "PUSH queue A" << std::endl;
}
};
template<class T>
class QueueB : public IQueue<T>
{
const T pop() final {
std::cout << "pop queue B" << std::endl;
return T{};
}
void push(const T& aItemRef) final {
std::cout << "PUSH queue B" << std::endl;
}
};
template<class T>
void test_push_pop(IQueue<T>* aQueueP)
{
aQueueP->push();
aQueueP->pop();
};
template<class ... ARGS>
void tester_queues()
{
// unpack ARGS
// construct objects of each type into in a vector
// std::vector<IQueue*> vec = { ARGS... };
// then execute test_push_pop for each
// std::for_each(vec.begin(), vec.end(), [](IQueue* itemP) {
// test_push_pop(itemP);
// });
};
int main()
{
tester_queues<QueueA<int>,
QueueB<char>>();
return 0;
}
If you really want to specify only the Queue types as template arguments, then you can use something like this to let tester_queues()
construct instances of those type internally for testing:
#include <iostream>
template<class T>
class IQueue
{
protected:
T Data[10];
public:
virtual const T pop() = 0;
virtual void push(const T&) = 0;
};
template<class T>
class QueueA : public IQueue<T>
{
public:
const T pop() final {
std::cout << "pop queue A" << std::endl;
return T{};
}
void push(const T& aItemRef) final {
std::cout << "PUSH queue A" << std::endl;
}
};
template<class T>
class QueueB : public IQueue<T>
{
public:
const T pop() final {
std::cout << "pop queue B" << std::endl;
return T{};
}
void push(const T& aItemRef) final {
std::cout << "PUSH queue B" << std::endl;
}
};
template <class... Ts>
struct Tester;
template <class T, class... Ts>
struct Tester<T, Ts...>
{
template<class U>
static void test_push_pop(IQueue<U>&& aQueueP)
{
aQueueP.push(U{});
aQueueP.pop();
}
static void test_push_pops()
{
test_push_pop(T{});
Tester<Ts...>::test_push_pops();
}
};
template<>
struct Tester<>
{
static void test_push_pops() {}
};
template<class... Ts>
void tester_queues()
{
Tester<Ts...>::test_push_pops();
};
int main()
{
tester_queues< QueueA<int>, QueueB<char> >();
return 0;
}
Alternatively, you can construct your own objects of the desired types and then pass them into tester_queues()
as input parameters, eg:
#include <iostream>
#include <utility>
template<class T>
class IQueue
{
protected:
T Data[10];
public:
virtual const T pop() = 0;
virtual void push(const T&) = 0;
};
template<class T>
class QueueA : public IQueue<T>
{
public:
const T pop() final {
std::cout << "pop queue A" << std::endl;
return T{};
}
void push(const T& aItemRef) final {
std::cout << "PUSH queue A" << std::endl;
}
};
template<class T>
class QueueB : public IQueue<T>
{
public:
const T pop() final {
std::cout << "pop queue B" << std::endl;
return T{};
}
void push(const T& aItemRef) final {
std::cout << "PUSH queue B" << std::endl;
}
};
template<class T>
void test_push_pop(IQueue<T>& aQueueP)
{
aQueueP.push(T{});
aQueueP.pop();
}
void test_push_pops()
{
}
template<class T, class... Ts>
void test_push_pops(T&& aQueueP, Ts&&... aQueuePs)
{
test_push_pop(std::forward<T>(aQueueP));
test_push_pops(aQueuePs...);
};
template<class... Ts>
void tester_queues(Ts&&... args)
{
test_push_pops(args...);
};
int main()
{
tester_queues( QueueA<int>{}, QueueB<char>{} );
return 0;
}