A program has an interface called Component:
class Component
{
public:
virtual void Start() = 0;
virtual void Update() = 0;
};
This interface is implemented by MeshComponent:
.h
#include "Component.h"
#include "Mesh.h"
class MeshComponent: public Component
{
public:
Mesh* mesh;
int a = 0; // for test
MeshComponent();
void Start();
void Update();
};
.cpp
#include "MeshComponent.h"
MeshComponent::MeshComponent(): mesh(nullptr)
{
}
void MeshComponent::Start()
{
std::cout << this << " " << a << "Start Mesh component\n";
}
void MeshComponent::Update()
{}
And it used in Object:
#include "Mesh.h"
#include "Vector3.h"
#include "Component.h"
class Object
{
private:
void bindPosition();
void updateComponents();
std::map<std::string, std::vector<std::unique_ptr<Component>>> components;
public:
Vector3 position;
Object();
~Object();
// T:Component
template<typename T, typename std::enable_if<std::is_base_of<Component, T>::value>::type* = nullptr>
void addComponent(T component) {
Component* comp = ((Component*)&component);
std::string name(typeid(T).name());
std::vector<std::unique_ptr<Component>>& arr = components[name];
arr.push_back(std::unique_ptr<Component>(comp));
T& c = (T&)*arr.back();
c.Start();
}
};
Like you see I use std::map<std::string, std::vectorstd::unique_ptr<Component>> to contain components. It is because I want to use something like this:
parent.getComponent<Movement>(); // first one
parent.getComponents<Material>(); // array
Every component have to be unique - that's why it is copied and I want user to know it is copied. I don't want copy it twice so I thought of using unique_ptr.
I want to avoid using destructor when I deleta Object. That's why I avoid pointers. I also want to avoid using move schematic.
If you read this code you probably see my mistake that's the 'T component' is deleted when it is out of scope (I found it by writing this question. This was the main reason for this question).
I want you ask for advice, how should I do it properly. By looking at it I think I do unnecessary transformations that can be avoided (working with abstract class is hell) or the way to contain components is wrong.
I'm counting on you.
template<typename T, typename std::enable_if<std::is_base_of<Component, T>::value>::type* = nullptr>
void addComponent(T component) {
Component* comp = ((Component*)&component);
// ...
arr.push_back(std::unique_ptr<Component>(comp));
// ...
}
Yeah you cannot do that. The proper way would be to copy it into a new unique pointer:
template<typename T, typename std::enable_if<std::is_base_of<Component, T>::value>::type* = nullptr>
void addComponent(T component) {
// ...
arr.push_back(std::make_unique<T>(comp));
// ...
}
As you said though you don't want two copies, so you can take your argument by const reference:
template<typename T, typename std::enable_if<std::is_base_of<Component, T>::value>::type* = nullptr>
void addComponent(T const& component) {
// ...
arr.push_back(std::make_unique<T>(comp));
// ...
}
You can also add an overload to move the object if you send in an rvalue, making it zero copy when possible:
template<typename T, typename std::enable_if<std::is_base_of<Component, T>::value
&& !std::is_reference<T>::value>::type* = nullptr>
void addComponent(T&& component) {
// ...
arr.push_back(std::make_unique<T>(std::move(comp)));
// ...
}