ObjectManager



  • Hallo, ich habe eine Objekt Manager Klasse geschrieben. Allerdings bin ich mir nicht sicher ob die Funktion DetachObject das tut was sie soll und/oder ob jemandem eine andere Möglichkeit zum Entfernen von Objekten einfällt.

    Hier ist die ObjektManager.h :

    #ifndef OBJECT_MANAGER_H
    #define OBJECT_MANAGER_H
    
    #include <array>
    #include <bitset>
    #include <memory>
    #include <vector>
    
    #include "Configuration.h"
    
    #include "ObjectInterface.h"
    
    namespace sw
    {
    	template < typename TObjectType, typename = typename std::enable_if<std::is_base_of<ObjectInterface, TObjectType>::value>::type >
    	class SWIFT_API ObjectManager
    	{
    	public:
    		template < typename TObject >
    		bool HasObject() const
    		{
    			if (std::is_base_of<TObjectType, TObject>::value)
    			{
    				return object_bitset_[GetObjectId<TObjectType, TObject>()];
    			}
    
    			return false;
    		}
    
    		template < typename TObject, typename... TArgs >
    		TObject* AttachObject(TArgs&&... args)
    		{
    			if (!HasObject<TObject>())
    			{
    				objects_.emplace_back(std::make_unique<TObject>(GetObjectId<TObjectType, TObject>(), std::forward<TArgs>(args)...));
    				object_bitset_[GetObjectId<TObjectType, TObject>()] = true;
    				object_handles_[GetObjectId<TObjectType, TObject>()] = objects_.back().get();
    
    				return GetObject<TObject>();
    			}
    
    			return nullptr;
    		}
    
    		template < typename TObject >
    		TObject* GetObject()
    		{
    			if (HasObject<TObject>())
    			{
    				return static_cast<TObject*>(object_handles_[GetObjectId<TObjectType, TObject>()]);
    			}
    
    			return nullptr;
    		}
    
    		template < typename TObject >
    		bool DetachObject()
    		{
    			if (HasObject<TObject>())
    			{
    				std::vector < std::unique_ptr < TObjectType > >::iterator it = 
    					std::find_if(objects_.begin(), objects_.end(), [](std::unique_ptr< TObjectType >& object){ return object->GetObjectId() == GetObjectId<TObjectType, TObject>() ? true : false; });
    
    				auto id = objects_.back()->GetObjectId();
    
    				std::iter_swap(it, --objects_.end());
    
    				object_handles_[id] = *it->get();
    
    				object_bitset_[GetObjectId<TObjectType, TObject>()] = false;
    				object_handles_[GetObjectId<TObjectType, TObject>()] = nullptr;
    
    				objects_.pop_back();
    
    				return true;
    			}
    
    			return false;
    		}
    
    		std::bitset< 32 >& GetObjectBitset()
    		{ 
    			return object_bitset_; 
    		}
    		std::vector< std::unique_ptr< TObjectType > >& GetObjectContainer()
    		{
    			return objects_;
    		}
    		std::array< TObjectType*, 32 >& GetObjectHandleContainer()
    		{
    			return object_handles_;
    		}
    
    	protected:
    		std::vector< std::unique_ptr< TObjectType > > objects_;
    		std::bitset< 32 > object_bitset_;
    		std::array< TObjectType*, 32 > object_handles_;
    
    	}; // class ObjectManager
    
    } // namespace sw
    
    #endif // OBJECT_MANAGER_H
    

    Hier ist die ObjectInterface.h, falls diese auch benötigt wird :

    #ifndef OBJECT_INTERFACE_H
    #define OBJECT_INTERFACE_H
    
    #include <iostream>
    
    #include "Configuration.h"
    
    namespace sw
    {
    	using ObjectId = std::size_t;
    
    	namespace internal
    	{
    		template < typename TObjectType > 
    		inline SWIFT_API ObjectId CreateObjectId()
    		{
    			static ObjectId kLastObjectId{ 0u };
    			return kLastObjectId++;
    		}
    	}
    
    	template < typename TObjectType, typename TObject >
    	inline SWIFT_API ObjectId GetObjectId()
    	{
    		static ObjectId kObjectId{ internal::CreateObjectId<TObjectType>() };
    		return kObjectId;
    	}
    
    	class SWIFT_API ObjectInterface
    	{
    	public:
    		inline bool IsAlive() const
    		{
    			return alive_;
    		}
    
    		void Destroy()
    		{
    			alive_ = false;
    		}
    
    		inline ObjectId GetObjectId() const
    		{
    			return id_;
    		}
    
    	protected:
    		ObjectInterface(ObjectId id) : alive_{ true }, id_{ id }{}
    
    		bool alive_;
    		const ObjectId id_;
    
    	}; // class ObjectInterface
    
    } // namespace sw
    
    #endif // OBJECT_INTERFACE_H
    

    Danke schon einmal im Voraus 👍



  • Um zu wissen,ob eine Funktion das tut,was sie soll, müsste man wissen, was sie tun soll.



  • Ich empfehle dir typedefs zu verwenden, dann wird der Code sehr schnell viel lesbarer.

    Ansonsten, folgendes geht nicht: --objects_.end()
    Aber ich habe beim kurzen drüber schauen den Sinn des Codes nicht erfasst. Also keine Ahnung ob da irgendwas anderes passt oder nicht.

    Und beim Code schreiben: versuche immer möglichst links zu bleiben:

    statt

    void* foo() {
      if(!bar()) {
        code();
        return value;
      }
      return nullptr;
    }
    

    schreib besser:

    void* foo() {
      if(bar()) {
        return nullptr;
      }
      code();
      return value;
    }
    

    gerade wenn es mal mehr ifs werden ist das viel besser lesbar 🙂



  • manni66 schrieb:

    Um zu wissen,ob eine Funktion das tut,was sie soll, müsste man wissen, was sie tun soll.

    Oh ja das hatte ich anscheinend komplett vergessen 🤡

    Die Funktion soll ein Objekt aus dem Manager entfernen ohne dabei die anderen Handles, bis auf den des letzten Objektes, ungültig zu machen. Dies wäre der Fall wenn ich einfach ein Objekt aus dem Vektor löschen würde. Also muss ich das zu entfernende Object mit dem letzten Objekt vertauschen und dann lediglich 2 Handler anpassen.
    Die Funktionsweise ähnelt also der von std::remove_if, nur das die Funktion auch noch das Bitset und den Handlecontainer verändert.

    Shade of Mine schrieb:

    Ansonsten, folgendes geht nicht: --objects_.end()

    Ich bin mir ziemlich sicher, dass das funktioniert, hier ein Beispiel:

    int main()
    {
    	std::vector<std::unique_ptr<int>> integers_;
    
    	integers_.emplace_back(std::make_unique<int>(10));
    	integers_.emplace_back(std::make_unique<int>(20));
    	integers_.emplace_back(std::make_unique<int>(30));
    
    	std::iter_swap(integers_.begin(), --integers_.end());
    
    	for (std::unique_ptr<int>& it : integers_)
    		std::cout << "Value: " << *it.get() << "\n";
    
    	std::getchar();
    
    	return EXIT_SUCCESS;
    }
    

    Output : "Value: 30 \n Value: 20 \n Value: 10"

    Ein Beispiel für die Verwendung des Managers:

    #ifndef SYSTEM_INTERFACE_H
    #define SYSTEM_INTERFACE_H
    
    #include "Configuration.h"
    
    #include "ObjectInterface.h"
    
    namespace sw
    {
    	class SWIFT_API SystemInterface : public ObjectInterface
    	{
    	public:
    		virtual ~SystemInterface(){}
    
    		virtual void Update(float dt) = 0;
    
    	protected:
    		SystemInterface(ObjectId id) : ObjectInterface(id){}
    
    	}; // class SystemInterface
    
    } // namespace sw
    
    #endif // SYSTEM_INTERFACE_H
    

    Jetzt kann ich zum Beispiel einfach eine Klasse SystemManager erstellen

    #ifndef SYSTEM_MANAGER_H
    #define SYSTEM_MANAGER_H
    
    #include "Configuration.h"
    
    #include "ObjectManager.h"
    #include "SystemInterface.h"
    
    namespace sw
    {
    	class SWIFT_API SystemManager : public ObjectManager < SystemInterface >
    	{
    	public:
    		void UpdateAll(float dt)
    		{
    			for (auto& it : objects_)
    			{
    				it->Update(dt);
    			}
    		}
    
    	private:
    
    	};
    }
    
    #endif
    

    Und diesem dann von SystemInterface abgeleitete Klassen anhängen.

    Shade of Mine schrieb:

    Ich empfehle dir typedefs zu verwenden, dann wird der Code sehr schnell viel lesbarer.

    Hier noch einmal überarbeitet:

    #ifndef OBJECT_MANAGER_H
    #define OBJECT_MANAGER_H
    
    #define MAX_OBJECT_COUNT 32
    
    #include <array>
    #include <bitset>
    #include <memory>
    #include <vector>
    
    #include "Configuration.h"
    
    #include "ObjectInterface.h"
    
    namespace sw
    {
    	template < typename TObjectType >
    	using ObjectContainer = std::vector < std::unique_ptr<TObjectType> > ;
    
    	template < typename TObjectType >
    	using ObjectHandleContainer = std::array < TObjectType*, MAX_OBJECT_COUNT > ;
    
    	using ObjectBitset = std::bitset < MAX_OBJECT_COUNT > ;
    
    	template < typename TObjectType, typename = typename std::enable_if<std::is_base_of<ObjectInterface, TObjectType>::value>::type >
    	class SWIFT_API ObjectManager
    	{
    	public:
    		virtual ~ObjectManager(){}
    
    		template < typename TObject >
    		bool HasObject() const
    		{
    			if (std::is_base_of<TObjectType, TObject>::value)
    			{
    				return object_bitset_[GetObjectId<TObjectType, TObject>()];
    			}
    
    			return false;
    		}
    
    		template < typename TObject, typename... TArgs >
    		TObject* AttachObject(TArgs&&... args)
    		{
    			if (!HasObject<TObject>())
    			{
    				objects_.emplace_back(std::make_unique<TObject>(GetObjectId<TObjectType, TObject>(), std::forward<TArgs>(args)...));
    				object_bitset_[GetObjectId<TObjectType, TObject>()] = true;
    				object_handles_[GetObjectId<TObjectType, TObject>()] = objects_.back().get();
    
    				return GetObject<TObject>();
    			}
    
    			return nullptr;
    		}
    
    		template < typename TObject >
    		TObject* GetObject()
    		{
    			if (HasObject<TObject>())
    			{
    				return static_cast<TObject*>(object_handles_[GetObjectId<TObjectType, TObject>()]);
    			}
    
    			return nullptr;
    		}
    
    		template < typename TObject >
    		bool DetachObject()
    		{
    			if (HasObject<TObject>())
    			{
    				std::vector < std::unique_ptr < TObjectType > >::iterator it = 
    					std::find_if(objects_.begin(), objects_.end(), [](std::unique_ptr< TObjectType >& object){ return object->GetObjectId() == GetObjectId<TObjectType, TObject>() ? true : false; });
    
    				auto id = objects_.back()->GetObjectId();
    
    				std::iter_swap(it, --objects_.end());
    
    				object_handles_[id] = *it->get();
    
    				object_bitset_[GetObjectId<TObjectType, TObject>()] = false;
    				object_handles_[GetObjectId<TObjectType, TObject>()] = nullptr;
    
    				objects_.pop_back();
    
    				return true;
    			}
    
    			return false;
    		}
    
    		ObjectBitset& GetObjectBitset()
    		{ 
    			return object_bitset_; 
    		}
    		ObjectContainer<TObjectType>& GetObjectContainer()
    		{
    			return objects_;
    		}
    		ObjectHandleContainer<TObjectType>& GetObjectHandleContainer()
    		{
    			return object_handles_;
    		}
    
    	protected:
    		ObjectManager(){}
    
    		ObjectContainer<TObjectType>       objects_;
    		ObjectBitset                       object_bitset_;
    		ObjectHandleContainer<TObjectType> object_handles_;
    
    	}; // class ObjectManager
    
    } // namespace sw
    
    #endif // OBJECT_MANAGER_H
    

Anmelden zum Antworten