I am working with a set of classes A, B, ... These classes are independent except that they have one method in common. Now I want to combine these classes in a vector, to call method in one loop. It seems that the best solution is to make the classes derived classes from some Parent (see below).
Now the question is the following. I want to create a header-only library for each class (a.h, b.h, ...). There I want the classes to be completely independent. Only in the main module I want to 'attach' the classes to a Parent to be able to combine them in a vector. How do I do this? Or do I have to resort to a vector of void* pointers? Or is there another way to combine these classes in a vector?
Here is what I have been able to do to combine the classes in the vector. Note I specifically want to avoid the parent/child paradigm in the class definitions. But I still want to combine them in a vector.
#include <iostream>
#include <vector>
#include <memory>
class Parent
{
public:
virtual ~Parent(){};
virtual void method(){};
};
class A : public Parent
{
public:
A(){};
~A(){};
void method(){};
};
class B : public Parent
{
public:
B(){};
~B(){};
void method(){};
};
int main()
{
std::vector<std::unique_ptr<Parent>> vec;
vec.push_back(std::unique_ptr<Parent>(new A));
vec.push_back(std::unique_ptr<Parent>(new A));
vec.push_back(std::unique_ptr<Parent>(new B));
for ( auto &i: vec )
i->method();
return 0;
}
Compile using e.g.
clang++ -std=c++14 main.cpp
A possible solution based on type erasure, static member functions and pointers to void that doesn't make use of virtual at all (example code, far from being production-ready):
#include <iostream>
#include <vector>
struct Erased
{
using fn_type = void(*)(void *);
template<typename T>
static void proto(void *ptr) {
static_cast<T*>(ptr)->method();
}
fn_type method;
void *ptr;
};
struct A
{
void method(){ std::cout << "A" << std::endl; };
};
struct B
{
void method(){ std::cout << "B" << std::endl; };
};
int main()
{
std::vector<Erased> vec;
vec.push_back(Erased{ &Erased::proto<A>, new A });
vec.push_back(Erased{ &Erased::proto<B>, new B });
for ( auto &erased: vec ) {
erased.method(erased.ptr);
}
return 0;
}
This can help to avoid using a common base class. See it on wandbox.
As mentioned in the comments, here is a slightly modified version that adds create and invoke methods to reduce the boilerplate for the users.
This is more of a pseudocode, trivial details are omitted.
struct HolderBase
{
virtual void foo() = 0;
};
template <class T>
struct Holder : HolderBase
{
Holder(T* t) : t(t) {}
T* t;
void foo() { t->foo(); }
};
std::vector<HolderBase*> v { new Holder<A>(new A), new Holder<B>(new B) };
You can also have a variant of Holder that holds an object by value (and mix both variants in the same vector freely).
If you have a single method to call, there is a much simpler solution:
A a;
B b;
std::vector<std::function<void()> v { [](){a.foo();}, [](){b.foo();} };
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With