While creating a game engine using c++, when defining a component, I was tired of writing down essential but repeated elements such as 'GameObject* parent' every time I define a new component constructor.
So I thought of a shortcut. It's debatable, and I feel it can be improved.
- When I need to create many different components, the following code is sufficient to disable the 'C6011', 'C26495' warnings?
- Any potential problems with this code?
- Is there a better way?
concept
Create a component class using malloc.
Enter the value of component begin.
Call the component`s constructor.
ComponentType* mptr = (ComponentType*)malloc(sizeof(ComponentType)); mptr->_begin = new Initialize(owner_game_object_ptr); new(mptr) ComponentType(std::forward<Args>(args)...); return mptr:
code
#pragma once
#include <memory>
#include <list>
#include <string>
#include <algorithm>
#include <iostream>
struct Initialize;
class GameObject;
class ComponentConstructor;
struct Initialize
{
GameObject* owner;
Initialize() = delete;
Initialize(const Initialize&) = delete;
Initialize(Initialize&&) = delete;
Initialize(GameObject* owner_game_object) :
owner(owner_game_object)
{}
};
class Component
{
private:
Initialize* _begin;
public:
__declspec(property(get = get_initialize)) Initialize* begin;
inline const Initialize* get_initialize() { return _begin; }
public:
Component() = default;
virtual ~Component() = default;
friend GameObject;
friend ComponentConstructor;
};
class ComponentConstructor
{
std::unique_ptr<Initialize> begin;
Component* component;
public:
ComponentConstructor()
: component(nullptr)
{}
~ComponentConstructor()
{
if (component != nullptr)
{
component->~Component();
free(component);
}
}
void make_begin(GameObject* owner)
{
begin = std::make_unique<Initialize>(owner);
}
template <typename Type, typename... Args
, typename = typename std::enable_if<std::is_convertible<Type, Component>::value>::type
>
void make_component(Args&&... args)
{
Type* mptr = (Type*)malloc(sizeof(Type));
mptr->_begin = begin.get();
new(mptr) Type(std::forward<Args>(args)...);
component = mptr;
}
friend GameObject;
};
/*
for use initialize in derived component
*/
class GameObject
{
public:
std::string name;
GameObject(const std::string& object_name) : name(object_name)
{}
private:
std::list<std::unique_ptr<ComponentConstructor>> child_components;
public:
template <typename Type, typename... Args
, typename = typename std::enable_if<std::is_convertible<Type, Component>::value>::type
>
Type* add_component(Args&&... args)
{
child_components.emplace_back();
auto& elum = child_components.back();
elum = std::make_unique<ComponentConstructor>();
elum->make_begin(this);
elum->make_component<Type>(std::forward<Args>(args)...);
return static_cast<Type*>(elum->component);
}
void remove_component(Component* component)
{
auto it = std::find_if(child_components.begin(), child_components.end(),
[component](const std::unique_ptr<ComponentConstructor>& child_component)
{
return child_component->component == component;
});
if (it != child_components.end())
child_components.erase(it);
}
};
use
class GameObjectComponent : public Component
{
GameObjectComponent()
{
std::cout << "GameObjectComponent is created" << std::endl;
}
~GameObjectComponent()
{
std::cout << "GameObjectComponent is deleted" << std::endl;
}
friend ComponentConstructor;
};
class CheckNameComponent : public Component
{
CheckNameComponent()
{
std::cout << "CheckNameComponent is created" << std::endl;
if (begin->owner->name == "Game object")
begin->owner->add_component<GameObjectComponent>();
}
~CheckNameComponent()
{
std::cout << "CheckNameComponent is deleted" << std::endl;
}
friend ComponentConstructor;
};
int main()
{
GameObject object("Game object");
object.add_component<CheckNameComponent>();
}
mallocandfreerather thannewanddelete. Usingmallocin C++ is not recommended. \$\endgroup\$GameObjectbefore you introduced yourComponentConstructorclass? \$\endgroup\$