I've been experimenting with making a component based system similar to Unity's, but in C++. I'm wondering how the GetComponent() method that Unity implements works. It is a very powerful function. Specifically, I want to know what kind of container it uses to store its components.
The two criteria I need in my clone of this function are as follows. 1. I need any inherited components to be returned as well. For example, if SphereCollider inherits Collider, GetComponent<Collider>() will return the SphereCollider attached to the GameObject, but GetComponent<SphereCollider>() will not return any Collider attached. 2. I need the function to be fast. Preferably, it would use some kind of hash function.
For criteria one, I know that I could use something similar to the following implementation
std::vector<Component*> components
template <typename T>
T* GetComponent()
{
for each (Component* c in components)
if (dynamic_cast<T>(*c))
return (T*)c;
return nullptr;
}
But that doesn't fit the second criteria of being fast. For that, I know I could do something like this.
std::unordered_map<type_index, Component*> components
template <typename T>
T* GetComponent()
{
return (T*)components[typeid(T)];
}
But again, that doesn't fit the first criteria.
If anybody knows of some way to combine those two features, even if it's a little slower than the second example, I would be willing to sacrifice a little bit. Thank you!
Since I'm writing my own game engine and incorporating the same design, I thought I'd share my results.
Overview
I wrote my own RTTI for the classes I cared to use as Components of my GameObject instances. The amount of typing is reduced by #defineing the two macros: CLASS_DECLARATION and CLASS_DEFINITION
CLASS_DECLARATION declares the unique static const std::size_t that will be used to identify the class type (Type), and a virtual function that allows objects to traverse their class hierarchy by calling their parent-class function of the same name (IsClassType).
CLASS_DEFINITION defines those two things. Namely the Type is initialized to a hash of a stringified version of the class name (using TO_STRING(x) #x), so that Type comparisons are just an int compare and not a string compare.
std::hash<std::string> is the hash function used, which guarantees equal inputs yield equal outputs, and the number of collisions is near-zero.
Aside from the low risk of hash collisions, this implementation has the added benefit of allowing users to create their own Component classes using those macros without ever having to refer to|extend some master include file of enum classs, or use typeid (which only provides the run-time type, not the parent-classes).
AddComponent
This custom RTTI simplifies the call syntax for Add|Get|RemoveComponent to just specifying the template type, just like Unity.
The AddComponent method perfect-forwards a universal reference variadic parameter pack to the user's constructor. So, for example, a user-defined Component-derived class CollisionModel could have the constructor:
CollisionModel( GameObject * owner, const Vec3 & size, const Vec3 & offset, bool active );
then later on the user simply calls:
myGameObject.AddComponent<CollisionModel>(this, Vec3( 10, 10, 10 ), Vec3( 0, 0, 0 ), true );
Note the explicit construction of the Vec3s because perfect-forwarding can fail to link if using deduced initializer-list syntax like { 10, 10, 10 } regardless of Vec3's constructor declarations.
This custom RTTI also resolves 3 issues with the std::unordered_map<std::typeindex,...> solution:
Even with the hierarchy traversal using std::tr2::direct_bases the end result is still duplicates of the same pointer in the map.
A user can't add multiple Components of equivalent type, unless a map is used that allows/solves collisions without overwriting, which further slows down the code.
No uncertain and slow dynamic_cast is needed, just a straight static_cast.
GetComponent
GetComponent just uses the static const std::size_t Type of the template type as an argument to the virtual bool IsClassType method and iterates over std::vector< std::unique_ptr< Component > > looking for the first match.
I've also implemented a GetComponents method that can get all components of the requested type, again including getting from the parent-class.
Note that the static member Type can be accessed both with and without an instance of the class.
Also note that Type is public, declared for each Component-derived class, ...and capitalized to emphasize its flexible use, despite being a POD member.
RemoveComponent
Lastly, RemoveComponent uses C++14's init-capture to pass that same static const std::size_t Type of the template type into a lambda so it can basically do the same vector traversal, this time getting an iterator to the first matching element.
There are a few comments in the code about ideas for a more flexible implementation, not to mention const versions of all these could also easily be implemented.
The Code
Classes.h
#ifndef TEST_CLASSES_H
#define TEST_CLASSES_H
#include <string>
#include <functional>
#include <vector>
#include <memory>
#include <algorithm>
#define TO_STRING( x ) #x
//****************
// CLASS_DECLARATION
//
// This macro must be included in the declaration of any subclass of Component.
// It declares variables used in type checking.
//****************
#define CLASS_DECLARATION( classname ) \
public: \
static const std::size_t Type; \
virtual bool IsClassType( const std::size_t classType ) const override; \
//****************
// CLASS_DEFINITION
//
// This macro must be included in the class definition to properly initialize
// variables used in type checking. Take special care to ensure that the
// proper parentclass is indicated or the run-time type information will be
// incorrect. Only works on single-inheritance RTTI.
//****************
#define CLASS_DEFINITION( parentclass, childclass ) \
const std::size_t childclass::Type = std::hash< std::string >()( TO_STRING( childclass ) ); \
bool childclass::IsClassType( const std::size_t classType ) const { \
if ( classType == childclass::Type ) \
return true; \
return parentclass::IsClassType( classType ); \
} \
namespace rtti {
//***************
// Component
// base class
//***************
class Component {
public:
static const std::size_t Type;
virtual bool IsClassType( const std::size_t classType ) const {
return classType == Type;
}
public:
virtual ~Component() = default;
Component( std::string && initialValue )
: value( initialValue ) {
}
public:
std::string value = "uninitialized";
};
//***************
// Collider
//***************
class Collider : public Component {
CLASS_DECLARATION( Collider )
public:
Collider( std::string && initialValue )
: Component( std::move( initialValue ) ) {
}
};
//***************
// BoxCollider
//***************
class BoxCollider : public Collider {
CLASS_DECLARATION( BoxCollider )
public:
BoxCollider( std::string && initialValue )
: Collider( std::move( initialValue ) ) {
}
};
//***************
// RenderImage
//***************
class RenderImage : public Component {
CLASS_DECLARATION( RenderImage )
public:
RenderImage( std::string && initialValue )
: Component( std::move( initialValue ) ) {
}
};
//***************
// GameObject
//***************
class GameObject {
public:
std::vector< std::unique_ptr< Component > > components;
public:
template< class ComponentType, typename... Args >
void AddComponent( Args&&... params );
template< class ComponentType >
ComponentType & GetComponent();
template< class ComponentType >
bool RemoveComponent();
template< class ComponentType >
std::vector< ComponentType * > GetComponents();
template< class ComponentType >
int RemoveComponents();
};
//***************
// GameObject::AddComponent
// perfect-forwards all params to the ComponentType constructor with the matching parameter list
// DEBUG: be sure to compare the arguments of this fn to the desired constructor to avoid perfect-forwarding failure cases
// EG: deduced initializer lists, decl-only static const int members, 0|NULL instead of nullptr, overloaded fn names, and bitfields
//***************
template< class ComponentType, typename... Args >
void GameObject::AddComponent( Args&&... params ) {
components.emplace_back( std::make_unique< ComponentType >( std::forward< Args >( params )... ) );
}
//***************
// GameObject::GetComponent
// returns the first component that matches the template type
// or that is derived from the template type
// EG: if the template type is Component, and components[0] type is BoxCollider
// then components[0] will be returned because it derives from Component
//***************
template< class ComponentType >
ComponentType & GameObject::GetComponent() {
for ( auto && component : components ) {
if ( component->IsClassType( ComponentType::Type ) )
return *static_cast< ComponentType * >( component.get() );
}
return *std::unique_ptr< ComponentType >( nullptr );
}
//***************
// GameObject::RemoveComponent
// returns true on successful removal
// returns false if components is empty, or no such component exists
//***************
template< class ComponentType >
bool GameObject::RemoveComponent() {
if ( components.empty() )
return false;
auto & index = std::find_if( components.begin(),
components.end(),
[ classType = ComponentType::Type ]( auto & component ) {
return component->IsClassType( classType );
} );
bool success = index != components.end();
if ( success )
components.erase( index );
return success;
}
//***************
// GameObject::GetComponents
// returns a vector of pointers to the the requested component template type following the same match criteria as GetComponent
// NOTE: the compiler has the option to copy-elide or move-construct componentsOfType into the return value here
// TODO: pass in the number of elements desired (eg: up to 7, or only the first 2) which would allow a std::array return value,
// except there'd need to be a separate fn for getting them *all* if the user doesn't know how many such Components the GameObject has
// TODO: define a GetComponentAt<ComponentType, int>() that can directly grab up to the the n-th component of the requested type
//***************
template< class ComponentType >
std::vector< ComponentType * > GameObject::GetComponents() {
std::vector< ComponentType * > componentsOfType;
for ( auto && component : components ) {
if ( component->IsClassType( ComponentType::Type ) )
componentsOfType.emplace_back( static_cast< ComponentType * >( component.get() ) );
}
return componentsOfType;
}
//***************
// GameObject::RemoveComponents
// returns the number of successful removals, or 0 if none are removed
//***************
template< class ComponentType >
int GameObject::RemoveComponents() {
if ( components.empty() )
return 0;
int numRemoved = 0;
bool success = false;
do {
auto & index = std::find_if( components.begin(),
components.end(),
[ classType = ComponentType::Type ]( auto & component ) {
return component->IsClassType( classType );
} );
success = index != components.end();
if ( success ) {
components.erase( index );
++numRemoved;
}
} while ( success );
return numRemoved;
}
} /* rtti */
#endif /* TEST_CLASSES_H */
Classes.cpp
#include "Classes.h"
using namespace rtti;
const std::size_t Component::Type = std::hash<std::string>()(TO_STRING(Component));
CLASS_DEFINITION(Component, Collider)
CLASS_DEFINITION(Collider, BoxCollider)
CLASS_DEFINITION(Component, RenderImage)
main.cpp
#include <iostream>
#include "Classes.h"
#define MORE_CODE 0
int main( int argc, const char * argv ) {
using namespace rtti;
GameObject test;
// AddComponent test
test.AddComponent< Component >( "Component" );
test.AddComponent< Collider >( "Collider" );
test.AddComponent< BoxCollider >( "BoxCollider_A" );
test.AddComponent< BoxCollider >( "BoxCollider_B" );
#if MORE_CODE
test.AddComponent< RenderImage >( "RenderImage" );
#endif
std::cout << "Added:\n------\nComponent\t(1)\nCollider\t(1)\nBoxCollider\t(2)\nRenderImage\t(0)\n\n";
// GetComponent test
auto & componentRef = test.GetComponent< Component >();
auto & colliderRef = test.GetComponent< Collider >();
auto & boxColliderRef1 = test.GetComponent< BoxCollider >();
auto & boxColliderRef2 = test.GetComponent< BoxCollider >(); // boxColliderB == boxColliderA here because GetComponent only gets the first match in the class hierarchy
auto & renderImageRef = test.GetComponent< RenderImage >(); // gets &nullptr with MORE_CODE 0
std::cout << "Values:\n-------\ncomponentRef:\t\t" << componentRef.value
<< "\ncolliderRef:\t\t" << colliderRef.value
<< "\nboxColliderRef1:\t" << boxColliderRef1.value
<< "\nboxColliderRef2:\t" << boxColliderRef2.value
<< "\nrenderImageRef:\t\t" << ( &renderImageRef != nullptr ? renderImageRef.value : "nullptr" );
// GetComponents test
auto allColliders = test.GetComponents< Collider >();
std::cout << "\n\nThere are (" << allColliders.size() << ") collider components attached to the test GameObject:\n";
for ( auto && c : allColliders ) {
std::cout << c->value << '\n';
}
// RemoveComponent test
test.RemoveComponent< BoxCollider >(); // removes boxColliderA
auto & boxColliderRef3 = test.GetComponent< BoxCollider >(); // now this is the second BoxCollider "BoxCollider_B"
std::cout << "\n\nFirst BoxCollider instance removed\nboxColliderRef3:\t" << boxColliderRef3.value << '\n';
#if MORE_CODE
// RemoveComponent return test
int removed = 0;
while ( test.RemoveComponent< Component >() ) {
++removed;
}
#else
// RemoveComponents test
int removed = test.RemoveComponents< Component >();
#endif
std::cout << "\nSuccessfully removed (" << removed << ") components from the test GameObject\n";
system( "PAUSE" );
return 0;
}
Output
Added:
------
Component (1)
Collider (1)
BoxCollider (2)
RenderImage (0)
Values:
-------
componentRef: Component
colliderRef: Collider
boxColliderRef1: BoxCollider_A
boxColliderRef2: BoxCollider_A
renderImageRef: nullptr
There are (3) collider components attached to the test GameObject:
Collider
BoxCollider_A
BoxCollider_B
First BoxCollider instance removed
boxColliderRef3: BoxCollider_B
Successfully removed (3) components from the test GameObject
Side-note: granted Unity uses Destroy(object) and not RemoveComponent, but my version suits my needs for now.
Apologies if this is not what you are looking for, but I had an idea to use the unordered map with a type index and, with the help of some metaprogramming and TR2, place multiple pointers to the component into the map, including its direct base classes as additional keys. So getComponent<SphereCollider>() and getComponent<Collider>() along with a down-cast will have the same pointee.
#include <tr2/type_traits>
#include <tuple>
#include <typeindex>
#include <unordered_map>
#include <iostream>
class Component {
public:
virtual ~Component() {}
};
class GameObject {
public:
template <typename T>
void addComponent(T *component);
template <typename T>
T *getComponent();
std::unordered_map<std::typeindex, Component *> components;
};
template <typename>
struct direct_bases_as_tuple {};
template <typename... Types>
struct direct_bases_as_tuple<std::tr2::__reflection_typelist<Types...>> {
typedef std::tuple<Types...> type;
};
template <std::size_t N, typename ComponentBases, typename ComponentType>
struct AddComponent {
GameObject *owner;
explicit AddComponent(GameObject *owner) : owner(owner) {}
void operator()(ComponentType *component) {
AddComponent<N-1, ComponentBases, ComponentType>{owner}(component);
using BaseType = std::tuple_element<N-1, ComponentBases>::type;
owner->components[typeid(BaseType)] = component;
}
};
template <typename ComponentBases, typename ComponentType>
struct AddComponent<0u, ComponentBases, ComponentType> {
GameObject *owner;
explicit AddComponent(GameObject *owner) : owner(owner) {}
void operator()(ComponentType *component) {
return;
}
};
template <typename T>
void GameObject::addComponent(T *component) {
using ComponentBases = direct_bases_as_tuple<std::tr2::direct_bases<ComponentType>::type>::type;
constexpr classCount = std::tuple_size<ComponentBases>::value;
AddComponent<classCount, ComponentBases, T>{this}(component);
components[typeid(T)] = component;
}
template <typename T>
T * GameObject::getComponent() {
auto iter = components.find(typeid(T));
if (iter != std::end(components)) {
return dynamic_cast<T *>(iter->second);
}
return nullptr;
}
class Collider : public Component {};
class SphereCollider : public Collider {};
int main() {
GameObject gameObject;
gameObject.addComponent(new SphereCollider);
//get by derived class
SphereCollider *sphereColliderA = gameObject.getComponent<SphereCollider>();
//get by subclass
SphereCollider *sphereColliderB = dynamic_cast<SphereCollider *>(
gameObject.getComponent<Collider>()
);
if (sphereColliderA == sphereColliderB) {
std::cout << "good" << std::endl;
}
}
I created the AddComponent struct to recurse through the component base classes at compile-time and insert the pointer (value) with the corresponding class (key) each iteration. The helper struct direct_bases_as_tuple was inspired by Andy Prowl's answer to change the direct bases into a tuple. I compiled this using GCC 4.9.2 using C++11 features.
I know this post is already answered, but if you look into Game Programming Patterns, in this book he has a design pattern called Service Locator, and at the end, it says Unity uses this pattern together with the Component Pattern. I wish I could answer into more specifics, but this could be another way to approach this.
The Unity engine is linked with a forked mono runtime, on which unity scripts are executed.
In UnityEngine.Component
public class Component : Object
{
.
.
[TypeInferenceRule(TypeInferenceRules.TypeReferencedByFirstArgument)]
public Component GetComponent(Type type)
{
return this.gameObject.GetComponent(type);
}
[GeneratedByOldBindingsGenerator]
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern void GetComponentFastPath(Type type, IntPtr oneFurtherThanResultValue);
[SecuritySafeCritical]
public unsafe T GetComponent<T>()
{
CastHelper<T> castHelper = default(CastHelper<T>);
this.GetComponentFastPath(typeof(T), new IntPtr((void*)(&castHelper.onePointerFurtherThanT)));
return castHelper.t;
}
.
.
}
The C# code performs native calls, called Icalls to C++ methods that have been bound to the C# methods using the C# runtime library API. Bodyless (unimplemented) methods need either an extern, abstract or partial specifier as a rule so all internal calls are marked as extern. When the runtime sees a method with the [MethodImpl(MethodImplOptions.InternalCall)] attribute it knows it needs to make an Icall, so it looks up the function it has been bound to and jumps to that address.
An Icall does not need to be static in C# and automatically passes the this MonoObject of the component to the C++ handler function. If they are static then the this object is usually deliberately passed as a parameter using a C# shim method and making the shim method the static Icall. Using Icalls, types are not marshalled unless they are blittable types, meaning all other types are passed as MonoObject, MonoString etc.
Typically the C++ methods are functions or static methods but I think they can be non static methods as well, so long as they aren't virtual, because the address cannot be fixed by the runtime.
in UnityEngine.GameObject
public sealed class GameObject : Object
{
.
.
public GameObject(string name)
{
GameObject.Internal_CreateGameObject(this, name);
}
public GameObject()
{
GameObject.Internal_CreateGameObject(this, (string) null);
}
[WrapperlessIcall]
[TypeInferenceRule(TypeInferenceRules.TypeReferencedByFirstArgument)]
[MethodImpl(MethodImplOptions.InternalCall)]
public extern Component GetComponent(System.Type type);
[WrapperlessIcall]
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void Internal_CreateGameObject([Writable] GameObject mono, string name);
.
.
}
The C# constructor for the GameObject contains a call to a native method. The body of the constructor is run after initialisation of the C# object such that there is already a this pointer. Internal_CreateGameObject is the static shim function that is actually called.
Someone's example implementation of their own C++ Internal_CreateGameObject using mono:
bool GameObjectBinding::init()
{
MonoClass *gameObjectClass = Mono::get().getClass("GameObject");
gameObject_NativeID_Field = mono_class_get_field_from_name(gameObjectClass, "nativeID");
MonoClass *transformClass = Mono::get().getClass("Transform");
transform_NativeID_Field = mono_class_get_field_from_name(transformClass, "nativeID");
mono_add_internal_call("GameEngine_CS.GameObject::internal_createGameObject", GameObjectBinding::createGameObject);
mono_add_internal_call("GameEngine_CS.GameObject::internal_deleteGameObject", GameObjectBinding::deleteGameObject);
mono_add_internal_call("GameEngine_CS.GameObject::internal_getGameObject", GameObjectBinding::getGameObject);
mono_add_internal_call("GameEngine_CS.GameObject::internal_getTransform", GameObjectBinding::getTransform);
return true;
}
void GameObjectBinding::createGameObject(MonoObject * monoGameObject)
{
Object *newObject = LevelManager::get().getCurrentLevel()->createObject(0);
mono_field_set_value(monoGameObject, gameObject_NativeID_Field, (void*)newObject->getID());
}
mono_add_internal_call has been used to bind this method to GameObjectBinding::createGameObject, to which the this pointer is passed as a MonoObject pointer. A native object is then created to represent the GameObject, and mono_field_set_value is then used to set the NativeID field of the C# object to the ID of the new native object. This way the native object can be accessed from the MonoObject which is the internal implementation of the C# object. The GameObject is represented by 2 objects essentially.
public sealed class GameObject : Object
{
.
.
private UInt32 nativeID;
public UInt32 id { get { return nativeID; } }
.
.
}
This field is bound in the runtime using
mono_set_dirs( "/Library/Frameworks/Mono.framework/Home/lib", "/Library/Frameworks/Mono.framework/Home/etc" );
mono_config_parse( nullptr );
const char* managedbinarypath = "C:/Test.dll";
MonoDomain* domain = mono_jit_init(managedbinarypath)
MonoAssembly* assembly = mono_domain_assembly_open (domain, managedbinarypath);
MonoImage* image = mono_assembly_get_image (assembly);
MonoClass* gameobjectclass = mono_class_from_name(image, "ManagedLibrary", "GameObject");
gameObject_NativeID_Field = mono_class_get_field_from_name( gameobjectclass, "nativeID" );
GetComponent<T>() passes typeof(T) to GetComponentFastPath (the native call) which passes the this pointer of the component as well. The native implementation of GetComponentFastPath will receive this as a MonoObject* and a MonoReflectionType* for the type. The bound C++ method will then call mono_reflection_type_get_type() on the MonoReflectionType* to get the MonoType* (here are the primitive types: https://github.com/samneirinck/cemono/blob/master/src/native/inc/mono/mono/metadata/blob.h), or for object types you can get the MonoClass* from MonoType* using mono_class_from_mono_type(). It will then get the game object that is attached to the Component and search the components that the object has in some internal data structure.
Someone's example implementation of their own C++ GetComponent using mono:
id ModuleScriptImporter::RegisterAPI()
{
//GAMEOBJECT
mono_add_internal_call("TheEngine.TheGameObject::CreateNewGameObject", (const void*)CreateGameObject);
mono_add_internal_call("TheEngine.TheGameObject::AddComponent", (const void*)AddComponent);
mono_add_internal_call("TheEngine.TheGameObject::GetComponent", (const void*)GetComponent);
}
MonoObject* ModuleScriptImporter::GetComponent(MonoObject * object, MonoReflectionType * type)
{
return current_script->GetComponent(object, type);
}
MonoObject* CSharpScript::GetComponent(MonoObject* object, MonoReflectionType* type)
{
if (!CheckMonoObject(object))
{
return nullptr;
}
if (currentGameObject == nullptr)
{
return nullptr;
}
MonoType* t = mono_reflection_type_get_type(type);
std::string name = mono_type_get_name(t);
const char* comp_name = "";
if (name == "CulverinEditor.Transform")
{
comp_name = "Transform";
}
MonoClass* classT = mono_class_from_name(App->importer->iScript->GetCulverinImage(), "CulverinEditor", comp_name);
if (classT)
{
MonoObject* new_object = mono_object_new(CSdomain, classT);
if (new_object)
{
return new_object;
}
}
return nullptr;
}
C# methods can be invoked from C++:
MonoMethodDesc* desc = mono_method_desc_new (const char *name, gboolean include_namespace);
MonoClass* class = mono_class_from_name (MonoImage *image, const char* name_space, const char *name);
MonoMethod* method = mono_method_desc_search_in_class (MonoMethodDesc *desc, MonoClass *klass);
MonoMethod* method = mono_method_desc_search_in_image (MonoMethodDesc *desc, MonoImage *image);
MonoObject* obj = mono_runtime_invoke (MonoMethod *method, void *obj, void **params,
MonoObject **exc);
See: https://gamedev.stackexchange.com/questions/115573/how-are-methods-like-awake-start-and-update-called-in-unity/183091#183091
There is a good introduction article to ECS: https://austinmorlan.com/posts/entity_component_system/ made differently than TOM__'s post.
Related
Let there be a C++ library (let's call it lib) which gets included as a static library in an application (let's call it app). Within the lib there's a base class node. Each subclass of a node is identified by a UUID.
I employ a self registering pattern to ensure that new classes register themselves at the factory. The factory allows to build a node subclass object based on the provided UUID. The app builds objects through the lib's factory::build() function.
My factory is based on the code of this brilliant blog post.
I've adapted this code to use a UUID (using boost.uuid) instead of a string as all the classes being created need a UUID assigned by them anyway (for external dependency reasons).
The problem I am hitting is that if I do not "manually" create an object instance of each node_template subclass (i.e. A and B), the factory::m_generators map is empty.
This is of course due to the fact that the various node-subclasses were never instantiated and therefore never registered themselves.
Here's my runnable minimum example (live demo at coliru):
#include <iostream>
#include <unordered_map>
#include <functional>
#include <memory>
#include <boost/functional/hash.hpp>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/string_generator.hpp>
class node;
/**
* #brief The factory class to build #p node items.
*/
class factory
{
public:
using key_type = boost::uuids::uuid;
using key_hash = boost::hash<key_type>;
using generator = std::function<std::unique_ptr<node>()>;
template<typename Derived>
struct registrar
{
registrar(const key_type& key)
{
factory::instance().register_generator(key, [](){
return std::make_unique<Derived>();
});
}
registrar(const std::string& uuid_string)
{
try {
boost::uuids::string_generator gen;
registrar(gen(uuid_string));
} catch (...) {
;
}
}
};
static factory& instance() noexcept
{
static factory f;
return f;
}
bool register_generator(const key_type& key, generator&& generator)
{
auto [it, emplaced] = m_generators.try_emplace(key, std::move(generator));
return emplaced;
}
[[nodiscard]] std::unique_ptr<node> build(const key_type& key) const
{
if (const auto& it = m_generators.find(key); it not_eq m_generators.cend())
return it->second();
return nullptr;
}
[[nodiscard]] std::unique_ptr<node> build(const char* uuid_string) const noexcept
{
try {
boost::uuids::string_generator gen;
return build(gen(uuid_string));
} catch (...) {
return nullptr;
}
}
private:
std::unordered_map<key_type, generator, key_hash> m_generators;
factory() = default;
factory(const factory& other) = default;
factory(factory&& other) = default;
virtual ~factory() = default;
};
/**
* #brief The node base class.
*/
struct node
{
node(const std::string& uuid_string) :
m_uuid_string(uuid_string)
{
}
[[nodiscard]] const std::string& uuid_string() const noexcept {
return m_uuid_string;
}
private:
std::string m_uuid_string;
};
/**
* #brief A template for #p node subclasses.
*/
template <class derived>
struct node_template :
node,
factory::registrar<derived>
{
node_template(const std::string& uuid_string) :
node(uuid_string),
factory::registrar<derived>(uuid_string)
{
}
};
struct A : node_template<A> {
A() : node_template("63cb8eeb-b90b-46c7-aaa8-3a349fcba3c5") { }
};
struct B : node_template<B> {
B() : node_template("1f24abfc-936f-4524-ae3b-cc346335ecbb") { }
};
static void build_and_print(const std::string& uuid_string)
{
if (auto node = factory::instance().build(uuid_string.c_str()); node)
std::cout << "node.uuid_string() = " << node->uuid_string() << std::endl;
else
std::cout << "Cannot build node object: Unknown UUID." << std::endl;
}
int main(void)
{
////////////////////////////////////////////////////////////////////////////////////////////////////
/// PROBLEM: If I do not construct these objects, they never register themselves at the factory. ///
////////////////////////////////////////////////////////////////////////////////////////////////////
#if 1
A a;
B b;
#endif
// A
build_and_print("63cb8eeb-b90b-46c7-aaa8-3a349fcba3c5");
// B
build_and_print("1f24abfc-936f-4524-ae3b-cc346335ecbb");
// Unknown UUID
build_and_print("9b20cc29-c7ca-4796-acb2-6ca6b80fa934");
return 0;
}
As long as I keep the object instantiations in line 136 and 137 I am able to build further objects through the factory. But once I remove then (eg. change line 135 to #if 0) the factory generators map is empty.
I think that I understand the problem as in that the class never gets to register itself as there's never an object being constructed. However, I am not sure how to fix the problem.
Currently there's a very ugly header file in lib which gets included by app. The header creates a dummy object of each class. This is of course anything but nice and also kind of defeats the entire purpose of a self registering class.
What am I missing here?
From the blog post, you are missing the static member registered (also called "// The really fun part"). Having and instantiating such a static variable in the base class forces it to be instantiated in all derived classes and this will register the class as a side effect.
EDIT: There is another very small but very important piece of code in the blog post:
Registrar() : Base(Key{}) { (void)registered; }
This will ensure that registered is used. Because a static variable is only instantiated the first time it is used, otherwise the function is not called.
In your case, adding the following to node_template should work:
template <class derived>
struct node_template :
node,
factory::registrar<derived>
{
node_template(const std::string& uuid_string) :
node(uuid_string),
factory::registrar<derived>(uuid_string)
{
(void) registered;
}
static bool do_register() {
derived d; // I am not sure if one should in some way force this to not be optimized away.
return true;
}
inline static bool registered = do_register();
};
I've been experimenting with making a component based system similar to Unity's, but in C++. I'm wondering how the GetComponent() method that Unity implements works. It is a very powerful function. Specifically, I want to know what kind of container it uses to store its components.
The two criteria I need in my clone of this function are as follows. 1. I need any inherited components to be returned as well. For example, if SphereCollider inherits Collider, GetComponent<Collider>() will return the SphereCollider attached to the GameObject, but GetComponent<SphereCollider>() will not return any Collider attached. 2. I need the function to be fast. Preferably, it would use some kind of hash function.
For criteria one, I know that I could use something similar to the following implementation
std::vector<Component*> components
template <typename T>
T* GetComponent()
{
for each (Component* c in components)
if (dynamic_cast<T>(*c))
return (T*)c;
return nullptr;
}
But that doesn't fit the second criteria of being fast. For that, I know I could do something like this.
std::unordered_map<type_index, Component*> components
template <typename T>
T* GetComponent()
{
return (T*)components[typeid(T)];
}
But again, that doesn't fit the first criteria.
If anybody knows of some way to combine those two features, even if it's a little slower than the second example, I would be willing to sacrifice a little bit. Thank you!
Since I'm writing my own game engine and incorporating the same design, I thought I'd share my results.
Overview
I wrote my own RTTI for the classes I cared to use as Components of my GameObject instances. The amount of typing is reduced by #defineing the two macros: CLASS_DECLARATION and CLASS_DEFINITION
CLASS_DECLARATION declares the unique static const std::size_t that will be used to identify the class type (Type), and a virtual function that allows objects to traverse their class hierarchy by calling their parent-class function of the same name (IsClassType).
CLASS_DEFINITION defines those two things. Namely the Type is initialized to a hash of a stringified version of the class name (using TO_STRING(x) #x), so that Type comparisons are just an int compare and not a string compare.
std::hash<std::string> is the hash function used, which guarantees equal inputs yield equal outputs, and the number of collisions is near-zero.
Aside from the low risk of hash collisions, this implementation has the added benefit of allowing users to create their own Component classes using those macros without ever having to refer to|extend some master include file of enum classs, or use typeid (which only provides the run-time type, not the parent-classes).
AddComponent
This custom RTTI simplifies the call syntax for Add|Get|RemoveComponent to just specifying the template type, just like Unity.
The AddComponent method perfect-forwards a universal reference variadic parameter pack to the user's constructor. So, for example, a user-defined Component-derived class CollisionModel could have the constructor:
CollisionModel( GameObject * owner, const Vec3 & size, const Vec3 & offset, bool active );
then later on the user simply calls:
myGameObject.AddComponent<CollisionModel>(this, Vec3( 10, 10, 10 ), Vec3( 0, 0, 0 ), true );
Note the explicit construction of the Vec3s because perfect-forwarding can fail to link if using deduced initializer-list syntax like { 10, 10, 10 } regardless of Vec3's constructor declarations.
This custom RTTI also resolves 3 issues with the std::unordered_map<std::typeindex,...> solution:
Even with the hierarchy traversal using std::tr2::direct_bases the end result is still duplicates of the same pointer in the map.
A user can't add multiple Components of equivalent type, unless a map is used that allows/solves collisions without overwriting, which further slows down the code.
No uncertain and slow dynamic_cast is needed, just a straight static_cast.
GetComponent
GetComponent just uses the static const std::size_t Type of the template type as an argument to the virtual bool IsClassType method and iterates over std::vector< std::unique_ptr< Component > > looking for the first match.
I've also implemented a GetComponents method that can get all components of the requested type, again including getting from the parent-class.
Note that the static member Type can be accessed both with and without an instance of the class.
Also note that Type is public, declared for each Component-derived class, ...and capitalized to emphasize its flexible use, despite being a POD member.
RemoveComponent
Lastly, RemoveComponent uses C++14's init-capture to pass that same static const std::size_t Type of the template type into a lambda so it can basically do the same vector traversal, this time getting an iterator to the first matching element.
There are a few comments in the code about ideas for a more flexible implementation, not to mention const versions of all these could also easily be implemented.
The Code
Classes.h
#ifndef TEST_CLASSES_H
#define TEST_CLASSES_H
#include <string>
#include <functional>
#include <vector>
#include <memory>
#include <algorithm>
#define TO_STRING( x ) #x
//****************
// CLASS_DECLARATION
//
// This macro must be included in the declaration of any subclass of Component.
// It declares variables used in type checking.
//****************
#define CLASS_DECLARATION( classname ) \
public: \
static const std::size_t Type; \
virtual bool IsClassType( const std::size_t classType ) const override; \
//****************
// CLASS_DEFINITION
//
// This macro must be included in the class definition to properly initialize
// variables used in type checking. Take special care to ensure that the
// proper parentclass is indicated or the run-time type information will be
// incorrect. Only works on single-inheritance RTTI.
//****************
#define CLASS_DEFINITION( parentclass, childclass ) \
const std::size_t childclass::Type = std::hash< std::string >()( TO_STRING( childclass ) ); \
bool childclass::IsClassType( const std::size_t classType ) const { \
if ( classType == childclass::Type ) \
return true; \
return parentclass::IsClassType( classType ); \
} \
namespace rtti {
//***************
// Component
// base class
//***************
class Component {
public:
static const std::size_t Type;
virtual bool IsClassType( const std::size_t classType ) const {
return classType == Type;
}
public:
virtual ~Component() = default;
Component( std::string && initialValue )
: value( initialValue ) {
}
public:
std::string value = "uninitialized";
};
//***************
// Collider
//***************
class Collider : public Component {
CLASS_DECLARATION( Collider )
public:
Collider( std::string && initialValue )
: Component( std::move( initialValue ) ) {
}
};
//***************
// BoxCollider
//***************
class BoxCollider : public Collider {
CLASS_DECLARATION( BoxCollider )
public:
BoxCollider( std::string && initialValue )
: Collider( std::move( initialValue ) ) {
}
};
//***************
// RenderImage
//***************
class RenderImage : public Component {
CLASS_DECLARATION( RenderImage )
public:
RenderImage( std::string && initialValue )
: Component( std::move( initialValue ) ) {
}
};
//***************
// GameObject
//***************
class GameObject {
public:
std::vector< std::unique_ptr< Component > > components;
public:
template< class ComponentType, typename... Args >
void AddComponent( Args&&... params );
template< class ComponentType >
ComponentType & GetComponent();
template< class ComponentType >
bool RemoveComponent();
template< class ComponentType >
std::vector< ComponentType * > GetComponents();
template< class ComponentType >
int RemoveComponents();
};
//***************
// GameObject::AddComponent
// perfect-forwards all params to the ComponentType constructor with the matching parameter list
// DEBUG: be sure to compare the arguments of this fn to the desired constructor to avoid perfect-forwarding failure cases
// EG: deduced initializer lists, decl-only static const int members, 0|NULL instead of nullptr, overloaded fn names, and bitfields
//***************
template< class ComponentType, typename... Args >
void GameObject::AddComponent( Args&&... params ) {
components.emplace_back( std::make_unique< ComponentType >( std::forward< Args >( params )... ) );
}
//***************
// GameObject::GetComponent
// returns the first component that matches the template type
// or that is derived from the template type
// EG: if the template type is Component, and components[0] type is BoxCollider
// then components[0] will be returned because it derives from Component
//***************
template< class ComponentType >
ComponentType & GameObject::GetComponent() {
for ( auto && component : components ) {
if ( component->IsClassType( ComponentType::Type ) )
return *static_cast< ComponentType * >( component.get() );
}
return *std::unique_ptr< ComponentType >( nullptr );
}
//***************
// GameObject::RemoveComponent
// returns true on successful removal
// returns false if components is empty, or no such component exists
//***************
template< class ComponentType >
bool GameObject::RemoveComponent() {
if ( components.empty() )
return false;
auto & index = std::find_if( components.begin(),
components.end(),
[ classType = ComponentType::Type ]( auto & component ) {
return component->IsClassType( classType );
} );
bool success = index != components.end();
if ( success )
components.erase( index );
return success;
}
//***************
// GameObject::GetComponents
// returns a vector of pointers to the the requested component template type following the same match criteria as GetComponent
// NOTE: the compiler has the option to copy-elide or move-construct componentsOfType into the return value here
// TODO: pass in the number of elements desired (eg: up to 7, or only the first 2) which would allow a std::array return value,
// except there'd need to be a separate fn for getting them *all* if the user doesn't know how many such Components the GameObject has
// TODO: define a GetComponentAt<ComponentType, int>() that can directly grab up to the the n-th component of the requested type
//***************
template< class ComponentType >
std::vector< ComponentType * > GameObject::GetComponents() {
std::vector< ComponentType * > componentsOfType;
for ( auto && component : components ) {
if ( component->IsClassType( ComponentType::Type ) )
componentsOfType.emplace_back( static_cast< ComponentType * >( component.get() ) );
}
return componentsOfType;
}
//***************
// GameObject::RemoveComponents
// returns the number of successful removals, or 0 if none are removed
//***************
template< class ComponentType >
int GameObject::RemoveComponents() {
if ( components.empty() )
return 0;
int numRemoved = 0;
bool success = false;
do {
auto & index = std::find_if( components.begin(),
components.end(),
[ classType = ComponentType::Type ]( auto & component ) {
return component->IsClassType( classType );
} );
success = index != components.end();
if ( success ) {
components.erase( index );
++numRemoved;
}
} while ( success );
return numRemoved;
}
} /* rtti */
#endif /* TEST_CLASSES_H */
Classes.cpp
#include "Classes.h"
using namespace rtti;
const std::size_t Component::Type = std::hash<std::string>()(TO_STRING(Component));
CLASS_DEFINITION(Component, Collider)
CLASS_DEFINITION(Collider, BoxCollider)
CLASS_DEFINITION(Component, RenderImage)
main.cpp
#include <iostream>
#include "Classes.h"
#define MORE_CODE 0
int main( int argc, const char * argv ) {
using namespace rtti;
GameObject test;
// AddComponent test
test.AddComponent< Component >( "Component" );
test.AddComponent< Collider >( "Collider" );
test.AddComponent< BoxCollider >( "BoxCollider_A" );
test.AddComponent< BoxCollider >( "BoxCollider_B" );
#if MORE_CODE
test.AddComponent< RenderImage >( "RenderImage" );
#endif
std::cout << "Added:\n------\nComponent\t(1)\nCollider\t(1)\nBoxCollider\t(2)\nRenderImage\t(0)\n\n";
// GetComponent test
auto & componentRef = test.GetComponent< Component >();
auto & colliderRef = test.GetComponent< Collider >();
auto & boxColliderRef1 = test.GetComponent< BoxCollider >();
auto & boxColliderRef2 = test.GetComponent< BoxCollider >(); // boxColliderB == boxColliderA here because GetComponent only gets the first match in the class hierarchy
auto & renderImageRef = test.GetComponent< RenderImage >(); // gets &nullptr with MORE_CODE 0
std::cout << "Values:\n-------\ncomponentRef:\t\t" << componentRef.value
<< "\ncolliderRef:\t\t" << colliderRef.value
<< "\nboxColliderRef1:\t" << boxColliderRef1.value
<< "\nboxColliderRef2:\t" << boxColliderRef2.value
<< "\nrenderImageRef:\t\t" << ( &renderImageRef != nullptr ? renderImageRef.value : "nullptr" );
// GetComponents test
auto allColliders = test.GetComponents< Collider >();
std::cout << "\n\nThere are (" << allColliders.size() << ") collider components attached to the test GameObject:\n";
for ( auto && c : allColliders ) {
std::cout << c->value << '\n';
}
// RemoveComponent test
test.RemoveComponent< BoxCollider >(); // removes boxColliderA
auto & boxColliderRef3 = test.GetComponent< BoxCollider >(); // now this is the second BoxCollider "BoxCollider_B"
std::cout << "\n\nFirst BoxCollider instance removed\nboxColliderRef3:\t" << boxColliderRef3.value << '\n';
#if MORE_CODE
// RemoveComponent return test
int removed = 0;
while ( test.RemoveComponent< Component >() ) {
++removed;
}
#else
// RemoveComponents test
int removed = test.RemoveComponents< Component >();
#endif
std::cout << "\nSuccessfully removed (" << removed << ") components from the test GameObject\n";
system( "PAUSE" );
return 0;
}
Output
Added:
------
Component (1)
Collider (1)
BoxCollider (2)
RenderImage (0)
Values:
-------
componentRef: Component
colliderRef: Collider
boxColliderRef1: BoxCollider_A
boxColliderRef2: BoxCollider_A
renderImageRef: nullptr
There are (3) collider components attached to the test GameObject:
Collider
BoxCollider_A
BoxCollider_B
First BoxCollider instance removed
boxColliderRef3: BoxCollider_B
Successfully removed (3) components from the test GameObject
Side-note: granted Unity uses Destroy(object) and not RemoveComponent, but my version suits my needs for now.
Apologies if this is not what you are looking for, but I had an idea to use the unordered map with a type index and, with the help of some metaprogramming and TR2, place multiple pointers to the component into the map, including its direct base classes as additional keys. So getComponent<SphereCollider>() and getComponent<Collider>() along with a down-cast will have the same pointee.
#include <tr2/type_traits>
#include <tuple>
#include <typeindex>
#include <unordered_map>
#include <iostream>
class Component {
public:
virtual ~Component() {}
};
class GameObject {
public:
template <typename T>
void addComponent(T *component);
template <typename T>
T *getComponent();
std::unordered_map<std::typeindex, Component *> components;
};
template <typename>
struct direct_bases_as_tuple {};
template <typename... Types>
struct direct_bases_as_tuple<std::tr2::__reflection_typelist<Types...>> {
typedef std::tuple<Types...> type;
};
template <std::size_t N, typename ComponentBases, typename ComponentType>
struct AddComponent {
GameObject *owner;
explicit AddComponent(GameObject *owner) : owner(owner) {}
void operator()(ComponentType *component) {
AddComponent<N-1, ComponentBases, ComponentType>{owner}(component);
using BaseType = std::tuple_element<N-1, ComponentBases>::type;
owner->components[typeid(BaseType)] = component;
}
};
template <typename ComponentBases, typename ComponentType>
struct AddComponent<0u, ComponentBases, ComponentType> {
GameObject *owner;
explicit AddComponent(GameObject *owner) : owner(owner) {}
void operator()(ComponentType *component) {
return;
}
};
template <typename T>
void GameObject::addComponent(T *component) {
using ComponentBases = direct_bases_as_tuple<std::tr2::direct_bases<ComponentType>::type>::type;
constexpr classCount = std::tuple_size<ComponentBases>::value;
AddComponent<classCount, ComponentBases, T>{this}(component);
components[typeid(T)] = component;
}
template <typename T>
T * GameObject::getComponent() {
auto iter = components.find(typeid(T));
if (iter != std::end(components)) {
return dynamic_cast<T *>(iter->second);
}
return nullptr;
}
class Collider : public Component {};
class SphereCollider : public Collider {};
int main() {
GameObject gameObject;
gameObject.addComponent(new SphereCollider);
//get by derived class
SphereCollider *sphereColliderA = gameObject.getComponent<SphereCollider>();
//get by subclass
SphereCollider *sphereColliderB = dynamic_cast<SphereCollider *>(
gameObject.getComponent<Collider>()
);
if (sphereColliderA == sphereColliderB) {
std::cout << "good" << std::endl;
}
}
I created the AddComponent struct to recurse through the component base classes at compile-time and insert the pointer (value) with the corresponding class (key) each iteration. The helper struct direct_bases_as_tuple was inspired by Andy Prowl's answer to change the direct bases into a tuple. I compiled this using GCC 4.9.2 using C++11 features.
I know this post is already answered, but if you look into Game Programming Patterns, in this book he has a design pattern called Service Locator, and at the end, it says Unity uses this pattern together with the Component Pattern. I wish I could answer into more specifics, but this could be another way to approach this.
The Unity engine is linked with a forked mono runtime, on which unity scripts are executed.
In UnityEngine.Component
public class Component : Object
{
.
.
[TypeInferenceRule(TypeInferenceRules.TypeReferencedByFirstArgument)]
public Component GetComponent(Type type)
{
return this.gameObject.GetComponent(type);
}
[GeneratedByOldBindingsGenerator]
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern void GetComponentFastPath(Type type, IntPtr oneFurtherThanResultValue);
[SecuritySafeCritical]
public unsafe T GetComponent<T>()
{
CastHelper<T> castHelper = default(CastHelper<T>);
this.GetComponentFastPath(typeof(T), new IntPtr((void*)(&castHelper.onePointerFurtherThanT)));
return castHelper.t;
}
.
.
}
The C# code performs native calls, called Icalls to C++ methods that have been bound to the C# methods using the C# runtime library API. Bodyless (unimplemented) methods need either an extern, abstract or partial specifier as a rule so all internal calls are marked as extern. When the runtime sees a method with the [MethodImpl(MethodImplOptions.InternalCall)] attribute it knows it needs to make an Icall, so it looks up the function it has been bound to and jumps to that address.
An Icall does not need to be static in C# and automatically passes the this MonoObject of the component to the C++ handler function. If they are static then the this object is usually deliberately passed as a parameter using a C# shim method and making the shim method the static Icall. Using Icalls, types are not marshalled unless they are blittable types, meaning all other types are passed as MonoObject, MonoString etc.
Typically the C++ methods are functions or static methods but I think they can be non static methods as well, so long as they aren't virtual, because the address cannot be fixed by the runtime.
in UnityEngine.GameObject
public sealed class GameObject : Object
{
.
.
public GameObject(string name)
{
GameObject.Internal_CreateGameObject(this, name);
}
public GameObject()
{
GameObject.Internal_CreateGameObject(this, (string) null);
}
[WrapperlessIcall]
[TypeInferenceRule(TypeInferenceRules.TypeReferencedByFirstArgument)]
[MethodImpl(MethodImplOptions.InternalCall)]
public extern Component GetComponent(System.Type type);
[WrapperlessIcall]
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void Internal_CreateGameObject([Writable] GameObject mono, string name);
.
.
}
The C# constructor for the GameObject contains a call to a native method. The body of the constructor is run after initialisation of the C# object such that there is already a this pointer. Internal_CreateGameObject is the static shim function that is actually called.
Someone's example implementation of their own C++ Internal_CreateGameObject using mono:
bool GameObjectBinding::init()
{
MonoClass *gameObjectClass = Mono::get().getClass("GameObject");
gameObject_NativeID_Field = mono_class_get_field_from_name(gameObjectClass, "nativeID");
MonoClass *transformClass = Mono::get().getClass("Transform");
transform_NativeID_Field = mono_class_get_field_from_name(transformClass, "nativeID");
mono_add_internal_call("GameEngine_CS.GameObject::internal_createGameObject", GameObjectBinding::createGameObject);
mono_add_internal_call("GameEngine_CS.GameObject::internal_deleteGameObject", GameObjectBinding::deleteGameObject);
mono_add_internal_call("GameEngine_CS.GameObject::internal_getGameObject", GameObjectBinding::getGameObject);
mono_add_internal_call("GameEngine_CS.GameObject::internal_getTransform", GameObjectBinding::getTransform);
return true;
}
void GameObjectBinding::createGameObject(MonoObject * monoGameObject)
{
Object *newObject = LevelManager::get().getCurrentLevel()->createObject(0);
mono_field_set_value(monoGameObject, gameObject_NativeID_Field, (void*)newObject->getID());
}
mono_add_internal_call has been used to bind this method to GameObjectBinding::createGameObject, to which the this pointer is passed as a MonoObject pointer. A native object is then created to represent the GameObject, and mono_field_set_value is then used to set the NativeID field of the C# object to the ID of the new native object. This way the native object can be accessed from the MonoObject which is the internal implementation of the C# object. The GameObject is represented by 2 objects essentially.
public sealed class GameObject : Object
{
.
.
private UInt32 nativeID;
public UInt32 id { get { return nativeID; } }
.
.
}
This field is bound in the runtime using
mono_set_dirs( "/Library/Frameworks/Mono.framework/Home/lib", "/Library/Frameworks/Mono.framework/Home/etc" );
mono_config_parse( nullptr );
const char* managedbinarypath = "C:/Test.dll";
MonoDomain* domain = mono_jit_init(managedbinarypath)
MonoAssembly* assembly = mono_domain_assembly_open (domain, managedbinarypath);
MonoImage* image = mono_assembly_get_image (assembly);
MonoClass* gameobjectclass = mono_class_from_name(image, "ManagedLibrary", "GameObject");
gameObject_NativeID_Field = mono_class_get_field_from_name( gameobjectclass, "nativeID" );
GetComponent<T>() passes typeof(T) to GetComponentFastPath (the native call) which passes the this pointer of the component as well. The native implementation of GetComponentFastPath will receive this as a MonoObject* and a MonoReflectionType* for the type. The bound C++ method will then call mono_reflection_type_get_type() on the MonoReflectionType* to get the MonoType* (here are the primitive types: https://github.com/samneirinck/cemono/blob/master/src/native/inc/mono/mono/metadata/blob.h), or for object types you can get the MonoClass* from MonoType* using mono_class_from_mono_type(). It will then get the game object that is attached to the Component and search the components that the object has in some internal data structure.
Someone's example implementation of their own C++ GetComponent using mono:
id ModuleScriptImporter::RegisterAPI()
{
//GAMEOBJECT
mono_add_internal_call("TheEngine.TheGameObject::CreateNewGameObject", (const void*)CreateGameObject);
mono_add_internal_call("TheEngine.TheGameObject::AddComponent", (const void*)AddComponent);
mono_add_internal_call("TheEngine.TheGameObject::GetComponent", (const void*)GetComponent);
}
MonoObject* ModuleScriptImporter::GetComponent(MonoObject * object, MonoReflectionType * type)
{
return current_script->GetComponent(object, type);
}
MonoObject* CSharpScript::GetComponent(MonoObject* object, MonoReflectionType* type)
{
if (!CheckMonoObject(object))
{
return nullptr;
}
if (currentGameObject == nullptr)
{
return nullptr;
}
MonoType* t = mono_reflection_type_get_type(type);
std::string name = mono_type_get_name(t);
const char* comp_name = "";
if (name == "CulverinEditor.Transform")
{
comp_name = "Transform";
}
MonoClass* classT = mono_class_from_name(App->importer->iScript->GetCulverinImage(), "CulverinEditor", comp_name);
if (classT)
{
MonoObject* new_object = mono_object_new(CSdomain, classT);
if (new_object)
{
return new_object;
}
}
return nullptr;
}
C# methods can be invoked from C++:
MonoMethodDesc* desc = mono_method_desc_new (const char *name, gboolean include_namespace);
MonoClass* class = mono_class_from_name (MonoImage *image, const char* name_space, const char *name);
MonoMethod* method = mono_method_desc_search_in_class (MonoMethodDesc *desc, MonoClass *klass);
MonoMethod* method = mono_method_desc_search_in_image (MonoMethodDesc *desc, MonoImage *image);
MonoObject* obj = mono_runtime_invoke (MonoMethod *method, void *obj, void **params,
MonoObject **exc);
See: https://gamedev.stackexchange.com/questions/115573/how-are-methods-like-awake-start-and-update-called-in-unity/183091#183091
There is a good introduction article to ECS: https://austinmorlan.com/posts/entity_component_system/ made differently than TOM__'s post.
I have several "resources" in my code base. All of them are classes and share the same interface except one class, the ShaderProgram is different in just one way, it needs two strings for files names of the vertex and fragment files.
I have a template class called ResourceManager that handles all these resource except the shader one because it needs two files and the others need one, can I solve this with a template specialization? It needs to be that ResourceManager sees GetOrLoadFromFile( string, string ) and not (string) versions, while the others have the opposite, they see (string) and not (string, string). Also AttemptLoad needs the treatment too. How can I make a solution for this please include code, I have never done template specializations before.
template < class ResType > class ResourceManager
{
public:
ResourceManager(void);
~ResourceManager(void);
SmartPointer<ResType> GetOrLoadFromFile( const std::string & fileName );
//weak_ptr<ResType> GetResourceFromID( ResourceID & resID );
void DestroyResources();
void ReleaseResources();
void ReloadResources();
protected:
private:
SmartPointer<ResType> AttemptLoad( const std::string & fileName );
std::unordered_map<string, SmartPointer<ResType> > mResMap;
};
// Relevant methods ( SNIPPED )
template < class ResType> SmartPointer<ResType> ResourceManager<ResType>::GetOrLoadFromFile( const std::string & fileName )
{
if ( !mResMap.empty() )
{
auto index = mResMap.begin();
auto end = mResMap.end();
while ( index != end )
{
if ( index->first == fileName )
{
return index->second;
}
++index;
}
}
return AttemptLoad(fileName);
}
template < class ResType > SmartPointer<ResType> ResourceManager<ResType>::AttemptLoad( const std::string & fileName )
{
SmartPointer<ResType> pRes( new ResType() );
if ( pRes->LoadFromFile( fileName ) )
{
mResMap.insert( std::make_pair( fileName, pRes ) );
return pRes;
}
else
{
LogFailure("Failed to load resource file " + fileName)
return SmartPointer<ResType>(nullptr);
}
}
If both classes are under your control I would suggest a different solution. Why don't you change the AttempLoad method into something like
SmartPointer<ResType> AttemptLoad( const LoadConfiguration &p_loadConfiguration );
Where
class LoadConfiguration
{
public:
std::string FirstFileName;
};
and
class ExtendedLoadConfiguration : public LoadConfiguration
{
public:
std::string SecondFileName;
};
you could then always work with LoadConfiguration and each AttemptLoad would be able to take what he needs. Adding new arguments will be easy, it's less code with the same signature and you wouldn't have to work with template specialization.
The idea behind templates is that you known your types before the execution times, i.e., at compilation time. If this is true, than what you are trying to do is an overloading using templates. So, bellow I just put a generic code, that you can adapt to your code, that do overloading at compilation time.
Note that, to avoid writing code twice, every common methods are put in base class and let to the derived class only the ones that diverges.
#include <memory>
#include <string>
#include <iostream>
using namespace std;
class Base
{
// put common codes here
};
template <typename ResType>
class ResourceManager : public Base
{
public:
unique_ptr<ResType> GetorLoad(const string &f) { cout << f << endl; return 0;}
};
// Specilizing class ResourceManager for string type
template <>
class ResourceManager<string> : public Base
{
public:
unique_ptr<string> GetorLoad(const string &f1, const string &f2) {cout << f1 << f2 << endl; return 0;}
};
int main()
{
ResourceManager<int> i;
ResourceManager<string> s;
i.GetorLoad("int");
s.GetorLoad("string", "string");
}
PS. To compile and test this example, you need to use '--std=c++11' flag from gcc or clang++ compilers
Just implement both 'GetOrLoadFromFile' functions:
#include <string>
struct R1
{
void load (const std::string &name) {}
};
struct R2
{
void load (const std::string &name0, const std::string name1) {}
};
template<typename R>
struct M
{
R *get_or_load (const std::string &name)
{
R *p = new R();
p->load (name);
return p;
}
R *get_or_load (const std::string &name0,
const std::string &name1)
{
R *p = new R();
p->load (name0, name1);
return p;
}
};
M<R1> m1;
M<R2> m2;
int
main ()
{
R1 *p0 = m1.get_or_load ("foo");
// R1 *p1 = m2.get_or_load ("foo"); // error
R2 *q0 = m2.get_or_load ("foo", "bar");
// R2 *q1 = m1.get_or_load ("foo", "bar"); // error
}
The "wrong" member function will not be instantiated, unless actually there's a call to it, in which case the compiler will exit with diagnostics.
Here is the code I have...
struct Test {
string foo() { return "bar"; }
};
#define callFn(obj,method) obj->method();
int main() {
Test* t = new Test();
cout << callFn(t,foo); // bar
return 0;
}
...and here is the code I'd like to have
int main() {
Test* t = new Test();
string method = "foo";
cout << callFn(t,method); // bar
return 0;
}
Is it possible?
You can't. C++ doesn't have reflection capabilities.
You would have to define e.g. a std::map that maps strings to function pointers.
void foo(int x) { std::cout << "foo " << (x+3) << "\n"; }
void bar(int x) { std::cout << "bar " << (x+5) << "\n"; }
int main() {
std::map<std::string, void (*)(int)> mapper;
mapper["foo"] = &foo;
mapper["bar"] = &bar;
// ...
mapper["foo"](42);
mapper["bar"](42);
}
You probably want something like member function pointers:
typedef std::string (Test::*ptmf)();
#define CALL_MF(pobject, p) (((pobject)->*(p))())
int main()
{
ptmf method = &Test::foo;
Test * t = new Test;
std::string result = CALL_MF(t, method); // or directly: (t->*method)()
}
You can create containers whose elements are of type ptmf to manage different member function pointers at runtime:
std::map<int, ptmf> function_registry;
std::string call(int key, Test * t)
{
auto it = function_registry.find(key);
return (it != function_registry.end()) ? CALL_MF(t, *it) : "[ERROR]";
}
You can do something like this, but because C++ lacks reflection capabilities you have to do some extra work to make it possible.
struct base {
virtual void call_method( std::string const & ) = 0;
};
struct derived : public base {
std::string foo( ) const {
return "bar";
}
// More methods.
void call_method( std::string const &p_name ) {
if( p_name == "foo" ) {
this -> foo( );
}
// More checks on method names.
else {
// Handle invalid function name.
}
}
};
This is called a data-driven interface, where you pass commands to objects and they respond to the commands that they recognize in a polymorphic fashion. You can improve on what I showed by creating a statically initialized unordered map from commands to function pointer and then using that to resolve which function to call. It's good to avoid this type of function dispatch if you can, though, because it's slow in comparison to static function dispatch and error prone since typos may result incorrect calls or errors. It also has the downside that you can't get the return value easily, though it is possible in some cases.
EDIT: I wanted to give a more complete example of how this can be done, so here goes:
#include <cassert>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/blank.hpp>
#include <boost/variant.hpp>
#include <boost/function.hpp>
#include <boost/unordered_map.hpp>
#include <boost/assign/list_of.hpp>
// A base class that defines an interface to call methods by name
// and to access the list of methods. We use a map of argument
// names to boost::variants to pass arguments to the functions.
// Right now we support only ints and strings, but we can expand
// this to other types if we want. In particular, we can use
// boost::any to support arbitrary types, but it will be slow.
// Maybe that's not a big deal since function dispatch through
// named functions is slow anyway.
struct base {
typedef boost::variant< boost::blank, int, std::string > argument_t;
typedef boost::variant< boost::blank, int, std::string > return_t;
typedef boost::unordered_map< std::string, argument_t > param_map_t;
typedef boost::function< return_t ( base *, param_map_t const & ) >
method_t;
typedef boost::unordered_map< std::string, method_t > method_map_t;
return_t call_method(
std::string const &p_method
, param_map_t const &p_params = param_map_t( )
)
{
method_map_t::const_iterator l_itr =
get_methods( ).find( p_method );
if( l_itr == get_methods( ).end( )) {
// Handle undefined method identifier.
}
return l_itr -> second( this, p_params );
}
virtual method_map_t const &get_methods( ) const = 0;
};
// A trampoline object to elide the concrete type that
// implements the base interface and to provide appropriate
// casting. This is necessary to force all functions in our
// method map to have the same type.
template< typename U >
base::return_t trampoline(
base::return_t (U::*p_fun)( base::param_map_t const & )
, base *p_obj
, base::param_map_t const &p_param_map
)
{
U *l_obj = static_cast< U* >( p_obj );
return (l_obj ->* p_fun)( p_param_map );
}
// A derived type that implements the base interface and
// provides a couple functions that we can call by name.
struct derived : public base {
static method_map_t const c_method_map;
return_t foo( param_map_t const &p_params ) {
std::cout << "foo" << std::endl; return 1;
}
return_t bar( param_map_t const &p_params ) {
std::cout << "bar" << std::endl; return std::string( "bar" );
}
method_map_t const &get_methods( ) const {
return c_method_map;
}
};
// Construct map of method names to method pointers for derived.
base::method_map_t const derived::c_method_map = boost::assign::map_list_of
( "foo", boost::bind( &trampoline< derived >, &derived::foo, _1, _2 ))
( "bar", boost::bind( &trampoline< derived >, &derived::bar, _1, _2 ))
;
int main( ) {
base *blah = new derived( );
// Call methods by name and extract return values.
assert( boost::get< int >( blah -> call_method( "foo" )) == 1 );
assert( boost::get< std::string >( blah -> call_method( "bar" )) == "bar" );
// Iterate over available methods
typedef base::method_map_t::const_iterator iterator;
iterator l_itr = blah -> get_methods( ).begin( );
iterator l_end = blah -> get_methods( ).end ( );
for( ; l_itr != l_end; ++l_itr ) {
if( l_itr -> first == "foo" ) l_itr -> second( blah, base::param_map_t( ));
}
}
The output is:
foo
bar
foo
As you can see it's quite a bit of work to set this up, but adding new types that implement the interface is pretty easy.
This is essentially the reflection mechanism that's available in post Java1.5
Here's an example of reflections in C++
http://www.garret.ru/cppreflection/docs/reflect.html
Is there any non-awful way to have a collection of objects of more than one type? I'm perfectly happy to derive each type from a common base. I need sensible semantics so the collection can be copied, assigned, and so on.
Obviously, I can't just use a vector or list of the base class. Objects will be sliced and copying won't work at all. Using vectors or lists of pointers or smart pointers works, but you don't get sane copy semantics.
To get sane copy semantics, you need to use something like Boost's ptr_vector. But this requires a painful and error-prone infrastructure. Essentially, you can't just derive a new class from the base class because if it ever goes into the collection, it will not be properly copied.
This seems like such a common thing to do and all the solutions I know of are so awful. It seems like C++ is fundamentally missing a way to create a new instance of an object identical to a given instance -- even if that type has a copy constructor. And making a clone or duplicate function requires careful overloading in every derived class. If you fail to do it when creating a new class derived from the base (or any other class derived from that base) -- boom, your collection breaks.
Is there really no better way?
You can use std::vector<boost::any> to do most of this I think.
#include "boost/any.hpp"
#include <vector>
#include <iostream>
//Simple class so we can see what's going on
class MM {
public:
MM() { std::cout<<"Create # "<<this<<std::endl; }
MM( const MM & o ) { std::cout<<"Copy "<<&o << " -> "<<this<<std::endl; }
~MM() { std::cout<<"Destroy # "<<this<<std::endl; }
};
int main()
{
//Fill a vector with some stuff
std::vector<boost::any> v;
v.push_back(0);
v.push_back(0);
v.push_back(0);
v.push_back(0);
//Overwrite one entry with one of our objects.
v[0] = MM();
std::cout<<"Copying the vector"<<std::endl;
std::vector<boost::any> w;
w = v;
std::cout<<"Done"<<std::endl;
}
For which I get the output:
Create # 0xbffff6ae
Copy 0xbffff6ae -> 0x100154
Destroy # 0xbffff6ae
Copying the vector
Copy 0x100154 -> 0x100194
Done
Destroy # 0x100194
Destroy # 0x100154
Which is what I expect to see.
EDIT:
In line with your requirements to be able to treat members as some common base-type you'll need something very similar to boost::any, which thankfully is a relatively simple class.
template<typename BASE>
class any_with_base
{
// ... Members as for boost::any
class placeholder
{
virtual BASE * as_base() = 0;
//Other members as in boost::any::placeholder
};
template<typename ValueType>
class holder : public placeholder
{
virtual BASE * as_base() { return (BASE*)&held; }
//Other members as in boost::any::holder<T>
};
BASE* as_base() { return content?content->as_base():0; }
}
Now you should be able to do this:
vector< any_with_base<Base> > v;
v.push_back( DerivedA() );
v.push_back( DerivedB() );
v[0].as_base()->base_fn();
v[1].as_base()->base_fn();
any_cast<DerivedA>(v[0])->only_in_a();
I actually dislike the syntax of any_cast and would use this opportunity to add an "as" member function.. so that I could write the last line as:
v[0].as<DerivedA>()->only_in_a();
Alright, to follow up my comment, there is a way to do this without using boost::any that should offer superior performance in most cases, but it's admittedly a bit more involved. My solution combines two ideas: the use of a holder class that elides the type of its contents, and lightweight custom RTTI. We give the holder class meaningful copy and assignment semantics and we use containers of holders to manage the collection of objects. We use our lightweight RTTI to discover the true types of the objects when necessary. Here's some code to demonstrate what I'm proposing:
#include <vector>
#include <cassert>
#include <iostream>
#include <boost/cast.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/static_assert.hpp>
/// This template makes it possible to enforce the invariant that every type in a
/// hierarchy defines the id( ) function, which is necessary for our RTTI. Using
/// a static assertion, we can force a compile error if a type doesn't provide id( ).
template< typename T >
struct provides_id {
typedef char one;
typedef long two;
template< typename U, std::string const &(U::*)( ) const = &U::id >
struct id_detector { };
template< typename U > static one test( id_detector< U > * );
template< typename U > static two test( ... );
enum { value = sizeof(test<T>(0)) == sizeof(one) };
};
/// Base class for the holder. It elides the true type of the object that it holds,
/// providing access only through the base class interface. Since there is only one
/// derived type, there is no risk of forgetting to define the clone() function.
template< typename T >
struct holder_impl_base {
virtual ~holder_impl_base( ) { }
virtual T *as_base( ) = 0;
virtual T const *as_base( ) const = 0;
virtual holder_impl_base *clone( ) const = 0;
};
/// The one and only implementation of the holder_impl_base interface. It stores
/// a derived type instance and provides access to it through the base class interface.
/// Note the use of static assert to force the derived type to define the id( )
/// function that we use to recover the instance's true type.
template< typename T, typename U >
struct holder_impl : public holder_impl_base< T > {
BOOST_STATIC_ASSERT(( provides_id< U >::value ));
holder_impl( U const &p_data )
: m_data( p_data )
{ }
virtual holder_impl *clone( ) const {
return new holder_impl( *this );
}
virtual T *as_base( ) {
return &m_data;
}
virtual T const *as_base( ) const {
return &m_data;
}
private:
U m_data;
};
/// The holder that we actually use in our code. It can be constructed from an instance
/// of any type that derives from T and it uses a holder_impl to elide the type of the
/// instance. It provides meaningful copy and assignment semantics that we are looking
/// for.
template< typename T >
struct holder {
template< typename U >
holder( U const &p_data )
: m_impl( new holder_impl< T, U >( p_data ))
{ }
holder( holder const &p_other )
: m_impl( p_other.m_impl -> clone( ))
{ }
template< typename U >
holder &operator = ( U const &p_data ) {
m_impl.reset( new holder_impl< T, U >( p_data ));
return *this;
}
holder &operator = ( holder const &p_other ) {
if( this != &p_other ) {
m_impl.reset( p_other.m_impl -> clone( ));
}
return *this;
}
T *as_base( ) {
return m_impl -> as_base( );
}
T const *as_base( ) const {
return m_impl -> as_base( );
}
/// The next two functions are what we use to cast elements to their "true" types.
/// They use our custom RTTI (which is guaranteed to be defined due to our static
/// assertion) to check if the "true" type of the object in a holder is the same as
/// as the template argument. If so, they return a pointer to the object; otherwise
/// they return NULL.
template< typename U >
U *as( ) {
T *base = as_base( );
if( base -> id( ) == U::static_id( )) {
return boost::polymorphic_downcast< U * >( base );
}
return 0;
}
template< typename U >
U const *as( ) const {
T *base = as_base( );
if( base -> id( ) == U::static_id( )) {
return boost::polymorphic_downcast< U const * >( base );
}
return 0;
}
private:
boost::scoped_ptr< holder_impl_base< T > > m_impl;
};
/// A base type and a couple derived types to demonstrate the technique.
struct base {
virtual ~base( )
{ }
virtual std::string const &id( ) const = 0;
};
struct derived1 : public base {
std::string const &id( ) const {
return c_id;
}
static std::string const &static_id( ) {
return c_id;
}
private:
static std::string const c_id;
};
std::string const derived1::c_id( "derived1" );
struct derived2 : public base {
std::string const &id( ) const {
return c_id;
}
static std::string const &static_id( ) {
return c_id;
}
private:
static std::string const c_id;
};
std::string const derived2::c_id( "derived2" );
/// A program to demonstrate that the technique works as advertised.
int main( ) {
std::vector< holder< base > > vector1;
vector1.push_back( derived1( ));
vector1.push_back( derived2( ));
std::vector< holder< base > > vector2 = vector1;
/// We see that we have true copies!
assert( vector1[0].as_base( ) != vector2[0].as_base( ));
assert( vector1[1].as_base( ) != vector2[0].as_base( ));
/// Easy assignment of container elements to new instances!
vector2[0] = derived2( );
vector2[1] = derived1( );
// Recovery of the "true" types!
std::vector< holder< base > >::iterator l_itr = vector1.begin( );
std::vector< holder< base > >::iterator l_end = vector1.end ( );
for( ; l_itr != l_end; ++l_itr ) {
if( derived1 *ptr = l_itr -> as< derived1 >( )) {
std::cout << ptr -> static_id( ) << std::endl;
}
else if( derived2 *ptr = l_itr -> as< derived2 >( )) {
std::cout << ptr -> static_id( ) << std::endl;
}
}
}
And here's the output:
derived1
derived2