diff options
author | Alec Thomas <alec@swapoff.org> | 2014-03-02 22:49:41 +1100 |
---|---|---|
committer | Alec Thomas <alec@swapoff.org> | 2014-03-02 22:49:41 +1100 |
commit | cc5201fd22250a36dbfaeec09b90750ff489d2f1 (patch) | |
tree | 85bfd12e7c1a7cb904a25bfd58d9a0d89868cd60 | |
parent | f21209417a2c4b3c874d8f58b653489c5cb2494f (diff) |
Use raw pointers for components, allocated from pools.
-rw-r--r-- | CHANGES.md | 4 | ||||
-rw-r--r-- | CMakeLists.txt | 3 | ||||
-rw-r--r-- | entityx/3rdparty/simplesignal.h | 688 | ||||
-rw-r--r-- | entityx/Benchmarks_test.cc | 2 | ||||
-rw-r--r-- | entityx/Entity.cc | 6 | ||||
-rw-r--r-- | entityx/Entity.h | 300 | ||||
-rw-r--r-- | entityx/Entity_test.cc | 82 | ||||
-rw-r--r-- | entityx/Event.h | 14 | ||||
-rw-r--r-- | entityx/Event_test.cc | 1 | ||||
-rw-r--r-- | entityx/System_test.cc | 27 | ||||
-rw-r--r-- | entityx/help/Pool.cc | 21 | ||||
-rw-r--r-- | entityx/help/Pool.h | 89 | ||||
-rw-r--r-- | entityx/help/Pool_test.cc | 75 | ||||
-rw-r--r-- | entityx/quick.h | 32 |
14 files changed, 710 insertions, 634 deletions
@@ -1,5 +1,9 @@ # Change Log +## 2014-03-02 - Version 1.0.0alpha1 - Removed most uses of shared_ptr + +I've been slightly discontent with the use of `std::shared_ptr` in EntityX since soon after release, but didn't want to break the API immediately after releasing it. But I've decided that it's time. + ## 2014-02-13 - Support for Visual C++ [Jarrett Chisholm](https://github.com/jarrettchisholm) has added conditional compilation support for VC++ and fixed some issues that prevented compilation, so EntityX now fully supports Visual C++! diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b6efb6..fc39440 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -122,7 +122,7 @@ endif() # Things to install set(install_libs entityx) -set(sources entityx/System.cc entityx/Event.cc entityx/Entity.cc entityx/help/Timer.cc) +set(sources entityx/System.cc entityx/Event.cc entityx/Entity.cc entityx/help/Timer.cc entityx/help/Pool.cc) add_library(entityx STATIC ${sources}) if (ENTITYX_BUILD_SHARED) @@ -140,6 +140,7 @@ if (ENTITYX_BUILD_TESTING) add_subdirectory(gtest-1.6.0) include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) enable_testing() + create_test(pool_test entityx/help/Pool_test.cc) create_test(entity_test entityx/Entity_test.cc) create_test(event_test entityx/Event_test.cc) create_test(system_test entityx/System_test.cc) diff --git a/entityx/3rdparty/simplesignal.h b/entityx/3rdparty/simplesignal.h index 39bb2fb..c0eb4f0 100644 --- a/entityx/3rdparty/simplesignal.h +++ b/entityx/3rdparty/simplesignal.h @@ -12,307 +12,310 @@ namespace Simple { namespace Lib { /// ProtoSignal is the template implementation for callback list. -template<typename,typename> class ProtoSignal; // undefined +template <typename, typename> +class ProtoSignal; // undefined -/// CollectorInvocation invokes signal handlers differently depending on return type. -template<typename,typename> struct CollectorInvocation; +/// CollectorInvocation invokes signal handlers differently depending on return +/// type. +template <typename, typename> +struct CollectorInvocation; -/// CollectorLast returns the result of the last signal handler from a signal emission. -template<typename Result> +/// CollectorLast returns the result of the last signal handler from a signal +/// emission. +template <typename Result> struct CollectorLast { typedef Result CollectorResult; - explicit CollectorLast () : last_() {} - inline bool operator() (Result r) { last_ = r; return true; } - CollectorResult result () { return last_; } -private: + explicit CollectorLast() : last_() {} + inline bool operator()(Result r) { + last_ = r; + return true; + } + CollectorResult result() { return last_; } + + private: Result last_; }; /// CollectorDefault implements the default signal handler collection behaviour. -template<typename Result> -struct CollectorDefault : CollectorLast<Result> -{}; +template <typename Result> +struct CollectorDefault : CollectorLast<Result> {}; /// CollectorDefault specialisation for signals with void return type. -template<> +template <> struct CollectorDefault<void> { typedef void CollectorResult; - void result () {} - inline bool operator() (void) { return true; } + void result() {} + inline bool operator()(void) { return true; } }; /// CollectorInvocation specialisation for regular signals. -template<class Collector, class R, class... Args> -struct CollectorInvocation<Collector, R (Args...)> { - inline bool - invoke (Collector &collector, const std::function<R (Args...)> &cbf, Args... args) - { - return collector (cbf (args...)); +template <class Collector, class R, class... Args> +struct CollectorInvocation<Collector, R(Args...)> { + inline bool invoke(Collector &collector, const std::function<R(Args...)> &cbf, + Args... args) { + return collector(cbf(args...)); } }; /// CollectorInvocation specialisation for signals with void return type. -template<class Collector, class... Args> -struct CollectorInvocation<Collector, void (Args...)> { - inline bool - invoke (Collector &collector, const std::function<void (Args...)> &cbf, Args... args) - { - cbf (args...); return collector(); +template <class Collector, class... Args> +struct CollectorInvocation<Collector, void(Args...)> { + inline bool invoke(Collector &collector, + const std::function<void(Args...)> &cbf, Args... args) { + cbf(args...); + return collector(); } }; /// ProtoSignal template specialised for the callback signature and collector. -template<class Collector, class R, class... Args> -class ProtoSignal<R (Args...), Collector> : private CollectorInvocation<Collector, R (Args...)> { -protected: - typedef std::function<R (Args...)> CbFunction; +template <class Collector, class R, class... Args> +class ProtoSignal<R(Args...), Collector> : private CollectorInvocation< + Collector, R(Args...)> { + protected: + typedef std::function<R(Args...)> CbFunction; typedef typename CbFunction::result_type Result; typedef typename Collector::CollectorResult CollectorResult; -private: - /// SignalLink implements a doubly-linked ring with ref-counted nodes containing the signal handlers. + + private: + /// SignalLink implements a doubly-linked ring with ref-counted nodes + /// containing the signal handlers. struct SignalLink { SignalLink *next, *prev; - CbFunction function; - int ref_count; - explicit SignalLink (const CbFunction &cbf) : next (0), prev (0), function (cbf), ref_count (1) {} - /*dtor*/ ~SignalLink () { assert (ref_count == 0); } - void incref () { ref_count += 1; assert (ref_count > 0); } - void decref () { ref_count -= 1; if (!ref_count) delete this; else assert (ref_count > 0); } - void - unlink () - { + CbFunction function; + int ref_count; + explicit SignalLink(const CbFunction &cbf) + : next(0), prev(0), function(cbf), ref_count(1) {} + /*dtor*/ ~SignalLink() { assert(ref_count == 0); } + void incref() { + ref_count += 1; + assert(ref_count > 0); + } + void decref() { + ref_count -= 1; + if (!ref_count) + delete this; + else + assert(ref_count > 0); + } + void unlink() { function = nullptr; - if (next) - next->prev = prev; - if (prev) - prev->next = next; + if (next) next->prev = prev; + if (prev) prev->next = next; decref(); // leave intact ->next, ->prev for stale iterators } - size_t - add_before (const CbFunction &cb) - { - SignalLink *link = new SignalLink (cb); - link->prev = prev; // link to last + size_t add_before(const CbFunction &cb) { + SignalLink *link = new SignalLink(cb); + link->prev = prev; // link to last link->next = this; - prev->next = link; // link from last + prev->next = link; // link from last prev = link; - static_assert (sizeof (link) == sizeof (size_t), "sizeof size_t"); - return size_t (link); + static_assert(sizeof(link) == sizeof(size_t), "sizeof size_t"); + return size_t(link); } - bool - deactivate (const CbFunction &cbf) - { - if (cbf == function) - { - function = 0; // deactivate static head + bool deactivate(const CbFunction &cbf) { + if (cbf == function) { + function = 0; // deactivate static head + return true; + } + for (SignalLink *link = this->next ? this->next : this; link != this; + link = link->next) + if (cbf == link->function) { + link->unlink(); // deactivate and unlink sibling return true; } - for (SignalLink *link = this->next ? this->next : this; link != this; link = link->next) - if (cbf == link->function) - { - link->unlink(); // deactivate and unlink sibling - return true; - } return false; } - bool - remove_sibling (size_t id) - { - for (SignalLink *link = this->next ? this->next : this; link != this; link = link->next) - if (id == size_t (link)) - { - link->unlink(); // deactivate and unlink sibling - return true; - } + bool remove_sibling(size_t id) { + for (SignalLink *link = this->next ? this->next : this; link != this; + link = link->next) + if (id == size_t(link)) { + link->unlink(); // deactivate and unlink sibling + return true; + } return false; } }; - SignalLink *callback_ring_; // linked ring of callback nodes - /*copy-ctor*/ ProtoSignal (const ProtoSignal&) = delete; - ProtoSignal& operator= (const ProtoSignal&) = delete; - void - ensure_ring () - { - if (!callback_ring_) - { - callback_ring_ = new SignalLink (CbFunction()); // ref_count = 1 - callback_ring_->incref(); // ref_count = 2, head of ring, can be deactivated but not removed - callback_ring_->next = callback_ring_; // ring head initialization - callback_ring_->prev = callback_ring_; // ring tail initialization - } + SignalLink *callback_ring_; // linked ring of callback nodes + /*copy-ctor*/ ProtoSignal(const ProtoSignal &) = delete; + ProtoSignal &operator=(const ProtoSignal &) = delete; + void ensure_ring() { + if (!callback_ring_) { + callback_ring_ = new SignalLink(CbFunction()); // ref_count = 1 + callback_ring_->incref(); // ref_count = 2, head of ring, can be + // deactivated but not removed + callback_ring_->next = callback_ring_; // ring head initialization + callback_ring_->prev = callback_ring_; // ring tail initialization + } } -public: + + public: /// ProtoSignal constructor, connects default callback if non-0. - ProtoSignal (const CbFunction &method) : - callback_ring_ (0) - { - if (method != 0) - { - ensure_ring(); - callback_ring_->function = method; - } + ProtoSignal(const CbFunction &method) : callback_ring_(0) { + if (method != 0) { + ensure_ring(); + callback_ring_->function = method; + } } /// ProtoSignal destructor releases all resources associated with this signal. - ~ProtoSignal () - { - if (callback_ring_) - { - while (callback_ring_->next != callback_ring_) - callback_ring_->next->unlink(); - assert (callback_ring_->ref_count >= 2); - callback_ring_->decref(); - callback_ring_->decref(); - } + ~ProtoSignal() { + if (callback_ring_) { + while (callback_ring_->next != callback_ring_) + callback_ring_->next->unlink(); + assert(callback_ring_->ref_count >= 2); + callback_ring_->decref(); + callback_ring_->decref(); + } + } + /// Operator to add a new function or lambda as signal handler, returns a + /// handler connection ID. + size_t connect(const CbFunction &cb) { + ensure_ring(); + return callback_ring_->add_before(cb); + } + /// Operator to remove a signal handler through it connection ID, returns if a + /// handler was removed. + bool disconnect(size_t connection) { + return callback_ring_ ? callback_ring_->remove_sibling(connection) : false; } - /// Operator to add a new function or lambda as signal handler, returns a handler connection ID. - size_t connect (const CbFunction &cb) { ensure_ring(); return callback_ring_->add_before (cb); } - /// Operator to remove a signal handler through it connection ID, returns if a handler was removed. - bool disconnect (size_t connection) { return callback_ring_ ? callback_ring_->remove_sibling (connection) : false; } - /// Emit a signal, i.e. invoke all its callbacks and collect return types with the Collector. - CollectorResult - emit (Args... args) - { + /// Emit a signal, i.e. invoke all its callbacks and collect return types with + /// the Collector. + CollectorResult emit(Args... args) { Collector collector; - if (!callback_ring_) - return collector.result(); + if (!callback_ring_) return collector.result(); SignalLink *link = callback_ring_; link->incref(); - do - { - if (link->function != 0) - { - const bool continue_emission = this->invoke (collector, link->function, args...); - if (!continue_emission) - break; - } - SignalLink *old = link; - link = old->next; - link->incref(); - old->decref(); + do { + if (link->function != 0) { + const bool continue_emission = + this->invoke(collector, link->function, args...); + if (!continue_emission) break; } - while (link != callback_ring_); + SignalLink *old = link; + link = old->next; + link->incref(); + old->decref(); + } while (link != callback_ring_); link->decref(); return collector.result(); } // Number of connected slots. - int - size () - { + int size() { int size = 0; SignalLink *link = callback_ring_; link->incref(); - do - { - if (link->function != 0) - { - size++; - } - SignalLink *old = link; - link = old->next; - link->incref(); - old->decref(); + do { + if (link->function != 0) { + size++; } - while (link != callback_ring_); + SignalLink *old = link; + link = old->next; + link->incref(); + old->decref(); + } while (link != callback_ring_); return size; } }; -} // Lib -// namespace Simple +} // Lib + // namespace Simple /** - * Signal is a template type providing an interface for arbitrary callback lists. - * A signal type needs to be declared with the function signature of its callbacks, + * Signal is a template type providing an interface for arbitrary callback + * lists. + * A signal type needs to be declared with the function signature of its + * callbacks, * and optionally a return result collector class type. - * Signal callbacks can be added with operator+= to a signal and removed with operator-=, using + * Signal callbacks can be added with operator+= to a signal and removed with + * operator-=, using * a callback connection ID return by operator+= as argument. - * The callbacks of a signal are invoked with the emit() method and arguments according to the signature. - * The result returned by emit() depends on the signal collector class. By default, the result of - * the last callback is returned from emit(). Collectors can be implemented to accumulate callback + * The callbacks of a signal are invoked with the emit() method and arguments + * according to the signature. + * The result returned by emit() depends on the signal collector class. By + * default, the result of + * the last callback is returned from emit(). Collectors can be implemented to + * accumulate callback * results or to halt a running emissions in correspondance to callback results. - * The signal implementation is safe against recursion, so callbacks may be removed and + * The signal implementation is safe against recursion, so callbacks may be + * removed and * added during a signal emission and recursive emit() calls are also safe. - * The overhead of an unused signal is intentionally kept very low, around the size of a single pointer. + * The overhead of an unused signal is intentionally kept very low, around the + * size of a single pointer. * Note that the Signal template types is non-copyable. */ -template <typename SignalSignature, class Collector = Lib::CollectorDefault<typename std::function<SignalSignature>::result_type> > -struct Signal /*final*/ : - Lib::ProtoSignal<SignalSignature, Collector> -{ +template <typename SignalSignature, + class Collector = Lib::CollectorDefault< + typename std::function<SignalSignature>::result_type>> +struct Signal /*final*/ : Lib::ProtoSignal<SignalSignature, Collector> { typedef Lib::ProtoSignal<SignalSignature, Collector> ProtoSignal; - typedef typename ProtoSignal::CbFunction CbFunction; + typedef typename ProtoSignal::CbFunction CbFunction; /// Signal constructor, supports a default callback as argument. - Signal (const CbFunction &method = CbFunction()) : ProtoSignal (method) {} + Signal(const CbFunction &method = CbFunction()) : ProtoSignal(method) {} }; -/// This function creates a std::function by binding @a object to the member function pointer @a method. -template<class Instance, class Class, class R, class... Args> std::function<R (Args...)> -slot (Instance &object, R (Class::*method) (Args...)) -{ - return [&object, method] (Args... args) { return (object .* method) (args...); }; +/// This function creates a std::function by binding @a object to the member +/// function pointer @a method. +template <class Instance, class Class, class R, class... Args> +std::function<R(Args...)> slot(Instance &object, R (Class::*method)(Args...)) { + return [&object, method](Args... args) { return (object.*method)(args...); }; } -/// This function creates a std::function by binding @a object to the member function pointer @a method. -template<class Class, class R, class... Args> std::function<R (Args...)> -slot (Class *object, R (Class::*method) (Args...)) -{ - return [object, method] (Args... args) { return (object ->* method) (args...); }; +/// This function creates a std::function by binding @a object to the member +/// function pointer @a method. +template <class Class, class R, class... Args> +std::function<R(Args...)> slot(Class *object, R (Class::*method)(Args...)) { + return [object, method](Args... args) { return (object->*method)(args...); }; } /// Keep signal emissions going while all handlers return !0 (true). -template<typename Result> +template <typename Result> struct CollectorUntil0 { typedef Result CollectorResult; - explicit CollectorUntil0 () : result_() {} - const CollectorResult& result () { return result_; } - inline bool - operator() (Result r) - { + explicit CollectorUntil0() : result_() {} + const CollectorResult &result() { return result_; } + inline bool operator()(Result r) { result_ = r; return result_ ? true : false; } -private: + + private: CollectorResult result_; }; /// Keep signal emissions going while all handlers return 0 (false). -template<typename Result> +template <typename Result> struct CollectorWhile0 { typedef Result CollectorResult; - explicit CollectorWhile0 () : result_() {} - const CollectorResult& result () { return result_; } - inline bool - operator() (Result r) - { + explicit CollectorWhile0() : result_() {} + const CollectorResult &result() { return result_; } + inline bool operator()(Result r) { result_ = r; return result_ ? false : true; } -private: + + private: CollectorResult result_; }; -/// CollectorVector returns the result of the all signal handlers from a signal emission in a std::vector. -template<typename Result> +/// CollectorVector returns the result of the all signal handlers from a signal +/// emission in a std::vector. +template <typename Result> struct CollectorVector { typedef std::vector<Result> CollectorResult; - const CollectorResult& result () { return result_; } - inline bool - operator() (Result r) - { - result_.push_back (r); + const CollectorResult &result() { return result_; } + inline bool operator()(Result r) { + result_.push_back(r); return true; } -private: + + private: CollectorResult result_; }; -} // Simple - +} // Simple #endif // SIMPLE_SIGNAL_H__ - #ifdef ENABLE_SIMPLE_SIGNAL_TESTS #include <string> @@ -325,32 +328,23 @@ private: #include <mach/mach.h> #endif - - - - -static std::string string_printf (const char *format, ...) __attribute__ ((__format__ (__printf__, 1, 2))); -static std::string -string_printf (const char *format, ...) -{ +static std::string string_printf(const char *format, ...) + __attribute__((__format__(__printf__, 1, 2))); +static std::string string_printf(const char *format, ...) { std::string result; char *str = 0; va_list args; - va_start (args, format); - if (vasprintf (&str, format, args) >= 0) - result = str; - va_end (args); - if (str) - free (str); + va_start(args, format); + if (vasprintf(&str, format, args) >= 0) result = str; + va_end(args); + if (str) free(str); return result; } -static uint64_t -timestamp_benchmark () -{ - struct timespec tp = { 0, 0 }; +static uint64_t timestamp_benchmark() { + struct timespec tp = {0, 0}; -#ifdef __MACH__ // OS X does not have clock_gettime, use clock_get_time +#ifdef __MACH__ // OS X does not have clock_gettime, use clock_get_time clock_serv_t cclock; mach_timespec_t mts; host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); @@ -366,219 +360,221 @@ timestamp_benchmark () } struct TestCounter { - static uint64_t get (); - static void set (uint64_t); - static void add2 (void*, uint64_t); + static uint64_t get(); + static void set(uint64_t); + static void add2(void *, uint64_t); }; -namespace { // Anon -void (*test_counter_add2) (void*, uint64_t) = TestCounter::add2; // external symbol to prevent easy inlining +namespace { // Anon +void (*test_counter_add2)(void *, uint64_t) = + TestCounter::add2; // external symbol to prevent easy inlining static uint64_t test_counter_var = 0; -} // Anon +} // Anon class BasicSignalTests { static std::string accu; struct Foo { - char - foo_bool (float f, int i, std::string s) - { - accu += string_printf ("Foo: %.2f\n", f + i + s.size()); + char foo_bool(float f, int i, std::string s) { + accu += string_printf("Foo: %.2f\n", f + i + s.size()); return true; } }; - static char - float_callback (float f, int, std::string) - { - accu += string_printf ("float: %.2f\n", f); + static char float_callback(float f, int, std::string) { + accu += string_printf("float: %.2f\n", f); return 0; } -public: - static void - run() - { + + public: + static void run() { accu = ""; - Simple::Signal<char (float, int, std::string)> sig1; + Simple::Signal<char(float, int, std::string)> sig1; size_t id1 = sig1.connect(float_callback); - size_t id2 = sig1.connect([] (float, int i, std::string) { accu += string_printf ("int: %d\n", i); return 0; }); - size_t id3 = sig1.connect([] (float, int, const std::string &s) { accu += string_printf ("string: %s\n", s.c_str()); return 0; }); - sig1.emit (.3, 4, "huhu"); + size_t id2 = sig1.connect([](float, int i, std::string) { + accu += string_printf("int: %d\n", i); + return 0; + }); + size_t id3 = sig1.connect([](float, int, const std::string &s) { + accu += string_printf("string: %s\n", s.c_str()); + return 0; + }); + sig1.emit(.3, 4, "huhu"); bool success; - success = sig1.disconnect(id1); assert (success == true); success = sig1.disconnect(id1); assert (success == false); - success = sig1.disconnect(id2); assert (success == true); success = sig1.disconnect(id3); assert (success == true); - success = sig1.disconnect(id3); assert (success == false); success = sig1.disconnect(id2); assert (success == false); + success = sig1.disconnect(id1); + assert(success == true); + success = sig1.disconnect(id1); + assert(success == false); + success = sig1.disconnect(id2); + assert(success == true); + success = sig1.disconnect(id3); + assert(success == true); + success = sig1.disconnect(id3); + assert(success == false); + success = sig1.disconnect(id2); + assert(success == false); Foo foo; - sig1.connect(Simple::slot (foo, &Foo::foo_bool)); - sig1.connect(Simple::slot (&foo, &Foo::foo_bool)); - sig1.emit (.5, 1, "12"); + sig1.connect(Simple::slot(foo, &Foo::foo_bool)); + sig1.connect(Simple::slot(&foo, &Foo::foo_bool)); + sig1.emit(.5, 1, "12"); - Simple::Signal<void (std::string, int)> sig2; - sig2.connect([] (std::string msg, int) { accu += string_printf ("msg: %s", msg.c_str()); }); - sig2.connect([] (std::string, int d) { accu += string_printf (" *%d*\n", d); }); - sig2.emit ("in sig2", 17); + Simple::Signal<void(std::string, int)> sig2; + sig2.connect([](std::string msg, + int) { accu += string_printf("msg: %s", msg.c_str()); }); + sig2.connect([](std::string, + int d) { accu += string_printf(" *%d*\n", d); }); + sig2.emit("in sig2", 17); accu += "DONE"; const char *expected = - "float: 0.30\n" - "int: 4\n" - "string: huhu\n" - "Foo: 3.50\n" - "Foo: 3.50\n" - "msg: in sig2 *17*\n" - "DONE"; - assert (accu == expected); + "float: 0.30\n" + "int: 4\n" + "string: huhu\n" + "Foo: 3.50\n" + "Foo: 3.50\n" + "msg: in sig2 *17*\n" + "DONE"; + assert(accu == expected); } }; std::string BasicSignalTests::accu; - class TestCollectorVector { - static int handler1 () { return 1; } - static int handler42 () { return 42; } - static int handler777 () { return 777; } - public: - static void - run () - { - Simple::Signal<int (), Simple::CollectorVector<int>> sig_vector; + static int handler1() { return 1; } + static int handler42() { return 42; } + static int handler777() { return 777; } + + public: + static void run() { + Simple::Signal<int(), Simple::CollectorVector<int>> sig_vector; sig_vector.connect(handler777); sig_vector.connect(handler42); sig_vector.connect(handler1); sig_vector.connect(handler42); sig_vector.connect(handler777); std::vector<int> results = sig_vector.emit(); - const std::vector<int> reference = { 777, 42, 1, 42, 777, }; - assert (results == reference); + const std::vector<int> reference = {777, 42, 1, 42, 777, }; + assert(results == reference); } }; class TestCollectorUntil0 { bool check1, check2; - TestCollectorUntil0() : check1 (0), check2 (0) {} - bool handler_true () { check1 = true; return true; } - bool handler_false () { check2 = true; return false; } - bool handler_abort () { abort(); } - public: - static void - run () - { + TestCollectorUntil0() : check1(0), check2(0) {} + bool handler_true() { + check1 = true; + return true; + } + bool handler_false() { + check2 = true; + return false; + } + bool handler_abort() { abort(); } + + public: + static void run() { TestCollectorUntil0 self; - Simple::Signal<bool (), Simple::CollectorUntil0<bool>> sig_until0; - sig_until0.connect(Simple::slot (self, &TestCollectorUntil0::handler_true)); - sig_until0.connect(Simple::slot (self, &TestCollectorUntil0::handler_false)); - sig_until0.connect(Simple::slot (self, &TestCollectorUntil0::handler_abort)); - assert (!self.check1 && !self.check2); + Simple::Signal<bool(), Simple::CollectorUntil0<bool>> sig_until0; + sig_until0.connect(Simple::slot(self, &TestCollectorUntil0::handler_true)); + sig_until0.connect(Simple::slot(self, &TestCollectorUntil0::handler_false)); + sig_until0.connect(Simple::slot(self, &TestCollectorUntil0::handler_abort)); + assert(!self.check1 && !self.check2); const bool result = sig_until0.emit(); - assert (!result && self.check1 && self.check2); + assert(!result && self.check1 && self.check2); } }; class TestCollectorWhile0 { bool check1, check2; - TestCollectorWhile0() : check1 (0), check2 (0) {} - bool handler_0 () { check1 = true; return false; } - bool handler_1 () { check2 = true; return true; } - bool handler_abort () { abort(); } - public: - static void - run () - { + TestCollectorWhile0() : check1(0), check2(0) {} + bool handler_0() { + check1 = true; + return false; + } + bool handler_1() { + check2 = true; + return true; + } + bool handler_abort() { abort(); } + + public: + static void run() { TestCollectorWhile0 self; - Simple::Signal<bool (), Simple::CollectorWhile0<bool>> sig_while0; - sig_while0.connect(Simple::slot (self, &TestCollectorWhile0::handler_0)); - sig_while0.connect(Simple::slot (self, &TestCollectorWhile0::handler_1)); - sig_while0.connect(Simple::slot (self, &TestCollectorWhile0::handler_abort)); - assert (!self.check1 && !self.check2); + Simple::Signal<bool(), Simple::CollectorWhile0<bool>> sig_while0; + sig_while0.connect(Simple::slot(self, &TestCollectorWhile0::handler_0)); + sig_while0.connect(Simple::slot(self, &TestCollectorWhile0::handler_1)); + sig_while0.connect(Simple::slot(self, &TestCollectorWhile0::handler_abort)); + assert(!self.check1 && !self.check2); const bool result = sig_while0.emit(); - assert (result == true && self.check1 && self.check2); + assert(result == true && self.check1 && self.check2); } }; -static void -bench_simple_signal() -{ - Simple::Signal<void (void*, uint64_t)> sig_increment; +static void bench_simple_signal() { + Simple::Signal<void(void *, uint64_t)> sig_increment; sig_increment.connect(test_counter_add2); const uint64_t start_counter = TestCounter::get(); const uint64_t benchstart = timestamp_benchmark(); uint64_t i; - for (i = 0; i < 999999; i++) - { - sig_increment.emit (0, 1); - } + for (i = 0; i < 999999; i++) { + sig_increment.emit(0, 1); + } const uint64_t benchdone = timestamp_benchmark(); const uint64_t end_counter = TestCounter::get(); - assert (end_counter - start_counter == i); - printf ("OK\n Benchmark: Simple::Signal: %fns per emission (size=%zu): ", size_t (benchdone - benchstart) * 1.0 / size_t (i), - sizeof (sig_increment)); + assert(end_counter - start_counter == i); + printf("OK\n Benchmark: Simple::Signal: %fns per emission (size=%zu): ", + size_t(benchdone - benchstart) * 1.0 / size_t(i), + sizeof(sig_increment)); } -static void -bench_callback_loop() -{ - void (*counter_increment) (void*, uint64_t) = test_counter_add2; +static void bench_callback_loop() { + void (*counter_increment)(void *, uint64_t) = test_counter_add2; const uint64_t start_counter = TestCounter::get(); const uint64_t benchstart = timestamp_benchmark(); uint64_t i; - for (i = 0; i < 999999; i++) - { - counter_increment (0, 1); - } + for (i = 0; i < 999999; i++) { + counter_increment(0, 1); + } const uint64_t benchdone = timestamp_benchmark(); const uint64_t end_counter = TestCounter::get(); - assert (end_counter - start_counter == i); - printf ("OK\n Benchmark: callback loop: %fns per round: ", size_t (benchdone - benchstart) * 1.0 / size_t (i)); + assert(end_counter - start_counter == i); + printf("OK\n Benchmark: callback loop: %fns per round: ", + size_t(benchdone - benchstart) * 1.0 / size_t(i)); } -uint64_t -TestCounter::get () -{ - return test_counter_var; -} +uint64_t TestCounter::get() { return test_counter_var; } -void -TestCounter::set (uint64_t v) -{ - test_counter_var = v; -} +void TestCounter::set(uint64_t v) { test_counter_var = v; } -void -TestCounter::add2 (void*, uint64_t v) -{ - test_counter_var += v; -} +void TestCounter::add2(void *, uint64_t v) { test_counter_var += v; } -int -main (int argc, - char *argv[]) -{ - printf ("Signal/Basic Tests: "); +int main(int argc, char *argv[]) { + printf("Signal/Basic Tests: "); BasicSignalTests::run(); - printf ("OK\n"); + printf("OK\n"); - printf ("Signal/CollectorVector: "); + printf("Signal/CollectorVector: "); TestCollectorVector::run(); - printf ("OK\n"); + printf("OK\n"); - printf ("Signal/CollectorUntil0: "); + printf("Signal/CollectorUntil0: "); TestCollectorUntil0::run(); - printf ("OK\n"); + printf("OK\n"); - printf ("Signal/CollectorWhile0: "); + printf("Signal/CollectorWhile0: "); TestCollectorWhile0::run(); - printf ("OK\n"); + printf("OK\n"); - printf ("Signal/Benchmark: Simple::Signal: "); + printf("Signal/Benchmark: Simple::Signal: "); bench_simple_signal(); - printf ("OK\n"); + printf("OK\n"); - printf ("Signal/Benchmark: callback loop: "); + printf("Signal/Benchmark: callback loop: "); bench_callback_loop(); - printf ("OK\n"); + printf("OK\n"); return 0; } -#endif // DISABLE_TESTS +#endif // DISABLE_TESTS // g++ -Wall -O2 -std=gnu++0x -pthread simplesignal.cc -lrt && ./a.out diff --git a/entityx/Benchmarks_test.cc b/entityx/Benchmarks_test.cc index 084b7bd..72ae926 100644 --- a/entityx/Benchmarks_test.cc +++ b/entityx/Benchmarks_test.cc @@ -107,7 +107,7 @@ TEST_F(BenchmarksTest, TestEntityIteration) { cout << "iterating over " << count << " entities with a component 10 times" << endl; for (int i = 0; i < 10; ++i) { - ComponentPtr<Position> position; + Position *position; for (auto e : em.entities_with_components<Position>(position)) { } } diff --git a/entityx/Entity.cc b/entityx/Entity.cc index ffdfbee..e81ae80 100644 --- a/entityx/Entity.cc +++ b/entityx/Entity.cc @@ -39,10 +39,10 @@ EntityManager::~EntityManager() { } void EntityManager::reset() { - for (auto allocator : component_allocators_) { - if (allocator) delete allocator; + for (BasePool *pool : component_pools_) { + if (pool) delete pool; } - component_allocators_.clear(); + component_pools_.clear(); entity_component_mask_.clear(); entity_version_.clear(); free_list_.clear(); diff --git a/entityx/Entity.h b/entityx/Entity.h index 18f04b8..2556caa 100644 --- a/entityx/Entity.h +++ b/entityx/Entity.h @@ -12,6 +12,7 @@ #include <stdint.h> +#include <new> #include <cstdlib> #include <algorithm> #include <bitset> @@ -25,6 +26,7 @@ #include <utility> #include <vector> +#include "entityx/help/Pool.h" #include "entityx/config.h" #include "entityx/Event.h" #include "entityx/help/NonCopyable.h" @@ -35,10 +37,6 @@ namespace entityx { class EntityManager; -template <typename T> -class ComponentPtr; - - /** A convenience handle around an Entity::Id. * * If an entity is destroyed, any copies will be invalidated. Use valid() to @@ -121,19 +119,17 @@ public: Id id() const { return id_; } - template <typename C> - ComponentPtr<C> assign(ComponentPtr<C> component); template <typename C, typename ... Args> - ComponentPtr<C> assign(Args && ... args); + C *assign(Args && ... args); template <typename C> void remove(); template <typename C> - ComponentPtr<C> component(); + C *component(); template <typename A, typename ... Args> - void unpack(ComponentPtr<A> &a, ComponentPtr<Args> & ... args); + void unpack(A *&a, Args *& ... args); /** * Destroy and invalidate this Entity. @@ -169,6 +165,12 @@ struct BaseComponent { public: typedef uint64_t Family; + // NOTE: Component memory is *always* managed by the EntityManager. + // Use Entity::destroy() instead. + void operator delete(void *p) { throw std::bad_alloc(); } + void operator delete[](void *p) { throw std::bad_alloc(); } + + protected: static Family family_counter_; }; @@ -201,43 +203,6 @@ struct Component : public BaseComponent { /** - * Managed pointer for components. - */ -template <typename T> -class ComponentPtr { -public: - ComponentPtr() : id_(0), manager_(nullptr) {} - - T *operator -> (); - const T *operator -> () const; - - operator bool () const { return valid(); } - bool valid() const; - - bool operator == (const ComponentPtr<T> &other) const { - return other.id_ == id_ && other.manager_ == manager_; - } - -private: - friend class EntityManager; - - ComponentPtr(Entity::Id id, EntityManager *manager) : id_(id), manager_(manager) {} - ComponentPtr(Entity::Id id, const EntityManager *manager) : - id_(id), manager_(const_cast<EntityManager*>(manager)) {} - - T *get_component_ptr(); - const T *get_component_ptr() const; - - template <typename R> - friend inline std::ostream &operator << (std::ostream &out, const ComponentPtr<R> &ptr); - - Entity::Id id_; - EntityManager *manager_; -}; - - - -/** * Emitted when an entity is added to the system. */ struct EntityCreatedEvent : public Event<EntityCreatedEvent> { @@ -260,94 +225,25 @@ struct EntityDestroyedEvent : public Event<EntityDestroyedEvent> { /** * Emitted when any component is added to an entity. */ -template <typename T> -struct ComponentAddedEvent : public Event<ComponentAddedEvent<T>> { - ComponentAddedEvent(Entity entity, ComponentPtr<T> component) : +template <typename C> +struct ComponentAddedEvent : public Event<ComponentAddedEvent<C>> { + ComponentAddedEvent(Entity entity, C *component) : entity(entity), component(component) {} Entity entity; - ComponentPtr<T> component; + C *component; }; /** * Emitted when any component is removed from an entity. */ -template <typename T> -struct ComponentRemovedEvent : public Event<ComponentRemovedEvent<T>> { - ComponentRemovedEvent(Entity entity, ComponentPtr<T> component) : +template <typename C> +struct ComponentRemovedEvent : public Event<ComponentRemovedEvent<C>> { + ComponentRemovedEvent(Entity entity, C *component) : entity(entity), component(component) {} Entity entity; - ComponentPtr<T> component; -}; - - -/** - * Cache-friendly component allocator. - */ -class BaseComponentAllocator { -public: - BaseComponentAllocator(int sizeof_element, int size = 0) : - sizeof_(sizeof_element), size_(0), capacity_(32), - components_(static_cast<char*>(malloc(sizeof_ * 32))) { - reserve(size); - } - - virtual ~BaseComponentAllocator() { - free(components_); - } - - /** - * Get a pointer to the nth element. - */ - void *get(int n) { - assert(n < size_); - return static_cast<void*>(components_ + n * sizeof_); - } - - const void *get(int n) const { - assert(n < size_); - return static_cast<const void*>(components_ + n * sizeof_); - } - - /** - * Resize the underlying array so it fits *at least* n elements. - */ - void reserve(int n) { - if (n >= size_) { - // Resize underlying array. - if (n >= capacity_) { - while (n >= capacity_) - capacity_ *= 2; - components_ = static_cast<char*>(realloc(components_, capacity_ * sizeof_)); - assert(components_); - } - size_ = n + 1; - } - } - - /** - * Call the destructor of the object at slot n. - */ - virtual void destroy(int n) = 0; - -private: - int sizeof_; - int size_; - int capacity_; - char *components_; -}; - - -template <typename T> -class ComponentAllocator : public BaseComponentAllocator { -public: - explicit ComponentAllocator(int size = 0) : BaseComponentAllocator(sizeof(T), size) {} - - virtual void destroy(int n) { - T *ptr = static_cast<T*>(get(n)); - ptr->~T(); - } + C *component; }; @@ -435,13 +331,13 @@ class EntityManager : entityx::help::NonCopyable { const Iterator end() const { return Iterator(manager_, predicates_, unpackers_, manager_->capacity()); } template <typename A> - View &unpack_to(ComponentPtr<A> &a) { + View &unpack_to(A *&a) { unpackers_.push_back(std::shared_ptr<Unpacker<A>>(new Unpacker<A>(manager_, a))); return *this; } template <typename A, typename B, typename ... Args> - View &unpack_to(ComponentPtr<A> &a, ComponentPtr<B> &b, ComponentPtr<Args> & ... args) { + View &unpack_to(A *&a, B *&b, Args *& ... args) { unpack_to<A>(a); return unpack_to<B, Args ...>(b, args ...); } @@ -449,17 +345,17 @@ class EntityManager : entityx::help::NonCopyable { private: friend class EntityManager; - template <typename T> + template <typename C> struct Unpacker : BaseUnpacker { - Unpacker(EntityManager *manager, ComponentPtr<T> &c) : manager_(manager), c(c) {} + Unpacker(EntityManager *manager, C *&c) : manager_(manager), c(c) {} void unpack(const Entity::Id &id) { - c = manager_->component<T>(id); + c = manager_->component<C>(id); } private: EntityManager *manager_; - ComponentPtr<T> &c; + C *&c; }; View(EntityManager *manager, Predicate predicate) : manager_(manager) { @@ -518,8 +414,8 @@ class EntityManager : entityx::help::NonCopyable { assert_valid(entity); int index = entity.index(); event_manager_.emit<EntityDestroyedEvent>(Entity(this, entity)); - for (BaseComponentAllocator *allocator : component_allocators_) { - if (allocator) allocator->destroy(index); + for (BasePool *pool : component_pools_) { + if (pool) pool->destroy(index); } entity_component_mask_[index] = 0; entity_version_[index]++; @@ -544,20 +440,20 @@ class EntityManager : entityx::help::NonCopyable { /** * Assign a Component to an Entity::Id, passing through Component constructor arguments. * - * ComponentPtr<Position> position = em.assign<Position>(e, x, y); + * Position &position = em.assign<Position>(e, x, y); * * @returns Smart pointer to newly created component. */ template <typename C, typename ... Args> - ComponentPtr<C> assign(Entity::Id id, Args && ... args) { + C *assign(Entity::Id id, Args && ... args) { assert_valid(id); const int family = C::family(); - ComponentAllocator<C> *allocator = accomodate_component<C>(); - new(allocator->get(id.index())) C(std::forward<Args>(args) ...); + // Placement new into the component pool. + Pool<C> *pool = accomodate_component<C>(); + C *component = new(pool->get(id.index())) C(std::forward<Args>(args) ...); entity_component_mask_[id.index()] |= uint64_t(1) << family; - ComponentPtr<C> component(id, this); event_manager_.emit<ComponentAddedEvent<C>>(Entity(this, id), component); - return ComponentPtr<C>(id, this); + return component; } /** @@ -570,48 +466,47 @@ class EntityManager : entityx::help::NonCopyable { assert_valid(id); const int family = C::family(); const int index = id.index(); - BaseComponentAllocator *allocator = component_allocators_[family]; - assert(allocator); + BasePool *pool = component_pools_[family]; + C *component = static_cast<C*>(pool->get(id.index())); entity_component_mask_[id.index()] &= ~(uint64_t(1) << family); - ComponentPtr<C> component(id, this); event_manager_.emit<ComponentRemovedEvent<C>>(Entity(this, id), component); - allocator->destroy(index); + pool->destroy(index); } /** * Retrieve a Component assigned to an Entity::Id. * - * @returns Component instance, or empty ComponentPtr<> if the Entity::Id does not have that Component. + * @returns Pointer to an instance of C, or nullptr if the Entity::Id does not have that Component. */ template <typename C> - ComponentPtr<C> component(Entity::Id id) { + C *component(Entity::Id id) { assert_valid(id); size_t family = C::family(); // We don't bother checking the component mask, as we return a nullptr anyway. - if (family >= component_allocators_.size()) - return ComponentPtr<C>(); - BaseComponentAllocator *allocator = component_allocators_[family]; - if (!allocator || !entity_component_mask_[id.index()][family]) - return ComponentPtr<C>(); - return ComponentPtr<C>(id, this); + if (family >= component_pools_.size()) + return nullptr; + BasePool *pool = component_pools_[family]; + if (!pool || !entity_component_mask_[id.index()][family]) + return nullptr; + return static_cast<C*>(pool->get(id.index())); } /** * Retrieve a Component assigned to an Entity::Id. * - * @returns Component instance, or empty ComponentPtr<> if the Entity::Id does not have that Component. + * @returns Component instance, or nullptr if the Entity::Id does not have that Component. */ template <typename C> - ComponentPtr<const C> component(Entity::Id id) const { + const C *component(Entity::Id id) const { assert_valid(id); size_t family = C::family(); // We don't bother checking the component mask, as we return a nullptr anyway. - if (family >= component_allocators_.size()) - return ComponentPtr<const C>(); - BaseComponentAllocator *allocator = component_allocators_[family]; - if (!allocator || !entity_component_mask_[id.index()][family]) - return ComponentPtr<const C>(); - return ComponentPtr<const C>(id, this); + if (family >= component_pools_.size()) + return nullptr; + BasePool *pool = component_pools_[family]; + if (!pool || !entity_component_mask_[id.index()][family]) + return nullptr; + return static_cast<const C*>(pool->get(id.index())); } /** @@ -619,8 +514,8 @@ class EntityManager : entityx::help::NonCopyable { * * @code * for (Entity entity : entity_manager.entities_with_components<Position, Direction>()) { - * ComponentPtr<Position> position = entity.component<Position>(); - * ComponentPtr<Direction> direction = entity.component<Direction>(); + * Position *position = entity.component<Position>(); + * Direction *direction = entity.component<Direction>(); * * ... * } @@ -637,15 +532,15 @@ class EntityManager : entityx::help::NonCopyable { * to the given parameters. * * @code - * ComponentPtr<Position> position; - * ComponentPtr<Direction> direction; + * Position *position; + * Direction *direction; * for (Entity entity : entity_manager.entities_with_components(position, direction)) { * // Use position and component here. * } * @endcode */ template <typename C, typename ... Components> - View entities_with_components(ComponentPtr<C> &c, ComponentPtr<Components> & ... args) { + View entities_with_components(C *&c, Components *& ... args) { auto mask = component_mask(c, args ...); return View(this, ComponentMaskPredicate(entity_component_mask_, mask)) @@ -653,7 +548,7 @@ class EntityManager : entityx::help::NonCopyable { } template <typename A> - void unpack(Entity::Id id, ComponentPtr<A> &a) { + void unpack(Entity::Id id, A *&a) { assert_valid(id); a = component<A>(id); } @@ -670,7 +565,7 @@ class EntityManager : entityx::help::NonCopyable { * unpack<Position, Direction>(e, p, d); */ template <typename A, typename ... Args> - void unpack(Entity::Id id, ComponentPtr<A> &a, ComponentPtr<Args> & ... args) { + void unpack(Entity::Id id, A *&a, Args *& ... args) { assert_valid(id); a = component<A>(id); unpack<Args ...>(id, args ...); @@ -682,8 +577,6 @@ class EntityManager : entityx::help::NonCopyable { void reset(); private: - template <typename C> - friend class ComponentPtr; friend class Entity; /// A predicate that matches valid entities with the given component mask. @@ -710,17 +603,17 @@ class EntityManager : entityx::help::NonCopyable { template <typename C> C *get_component_ptr(Entity::Id id) { assert(valid(id)); - BaseComponentAllocator *allocator = component_allocators_[C::family()]; - assert(allocator); - return static_cast<C*>(allocator->get(id.index())); + BasePool *pool = component_pools_[C::family()]; + assert(pool); + return static_cast<C*>(pool->get(id.index())); } template <typename C> const C *get_component_ptr(Entity::Id id) const { assert_valid(id); - BaseComponentAllocator *allocator = component_allocators_[C::family()]; - assert(allocator); - return static_cast<const C*>(allocator->get(id.index())); + BasePool *pool = component_pools_[C::family()]; + assert(pool); + return static_cast<const C*>(pool->get(id.index())); } ComponentMask component_mask(Entity::Id id) { @@ -741,12 +634,12 @@ class EntityManager : entityx::help::NonCopyable { } template <typename C> - ComponentMask component_mask(const ComponentPtr<C> &c) { + ComponentMask component_mask(const C *c) { return component_mask<C>(); } template <typename C1, typename C2, typename ... Components> - ComponentMask component_mask(const ComponentPtr<C1> &c1, const ComponentPtr<C2> &c2, ComponentPtr<Components> & ... args) { + ComponentMask component_mask(const C1 *c1, const C2 *c2, Components * ... args) { return component_mask<C1>(c1) | component_mask<C2, Components ...>(c2, args...); } @@ -754,30 +647,31 @@ class EntityManager : entityx::help::NonCopyable { if (entity_component_mask_.size() <= index) { entity_component_mask_.resize(index + 1); entity_version_.resize(index + 1); - for (BaseComponentAllocator *allocator : component_allocators_) { - if (allocator) allocator->reserve(index); - } + for (BasePool *pool : component_pools_) + if (pool) pool->expand(index + 1); } } template <typename T> - ComponentAllocator<T> *accomodate_component() { + Pool<T> *accomodate_component() { BaseComponent::Family family = T::family(); - if (component_allocators_.size() <= family) { - component_allocators_.resize(family + 1, nullptr); + if (component_pools_.size() <= family) { + component_pools_.resize(family + 1, nullptr); } - if (!component_allocators_[family]) { - component_allocators_[family] = new ComponentAllocator<T>(index_counter_); + if (!component_pools_[family]) { + Pool<T> *pool = new Pool<T>(); + pool->expand(index_counter_); + component_pools_[family] = pool; } - return static_cast<ComponentAllocator<T>*>(component_allocators_[family]); + return static_cast<Pool<T>*>(component_pools_[family]); } uint32_t index_counter_ = 0; EventManager &event_manager_; - // Each element in component_allocators_ corresponds to a Component::family(). - std::vector<BaseComponentAllocator*> component_allocators_; + // Each element in component_pools_ corresponds to a Component::family(). + std::vector<BasePool*> component_pools_; // Bitmask of components associated with each entity. Index into the vector is the Entity::Id. std::vector<ComponentMask> entity_component_mask_; // Vector of entity version numbers. Incremented each time an entity is destroyed @@ -796,7 +690,7 @@ BaseComponent::Family Component<C>::family() { template <typename C, typename ... Args> -ComponentPtr<C> Entity::assign(Args && ... args) { +C *Entity::assign(Args && ... args) { assert(valid()); return manager_->assign<C>(id_, std::forward<Args>(args) ...); } @@ -808,13 +702,13 @@ void Entity::remove() { } template <typename C> -ComponentPtr<C> Entity::component() { +C *Entity::component() { assert(valid()); return manager_->component<C>(id_); } template <typename A, typename ... Args> -void Entity::unpack(ComponentPtr<A> &a, ComponentPtr<Args> & ... args) { +void Entity::unpack(A *&a, Args *& ... args) { assert(valid()); manager_->unpack(id_, a, args ...); } @@ -824,34 +718,4 @@ inline bool Entity::valid() const { } -template <typename T> -inline T *ComponentPtr<T>::operator -> () { - return manager_->get_component_ptr<T>(id_); -} - -template <typename T> -inline const T *ComponentPtr<T>::operator -> () const { - return manager_->get_component_ptr<T>(id_); -} - -template <typename T> -inline bool ComponentPtr<T>::valid() const { - return manager_ && manager_->valid(id_); -} - -template <typename T> -inline T *ComponentPtr<T>::get_component_ptr() { - return manager_->get_component_ptr<T>(id_); -} - -template <typename T> -inline const T *ComponentPtr<T>::get_component_ptr() const { - return manager_->get_component_ptr<T>(id_); -} - - -template <typename T> -inline std::ostream &operator << (std::ostream &out, const ComponentPtr<T> &ptr) { - return out << *(ptr.get_component_ptr()); -} } // namespace entityx diff --git a/entityx/Entity_test.cc b/entityx/Entity_test.cc index 2caf8f4..e6d3d70 100644 --- a/entityx/Entity_test.cc +++ b/entityx/Entity_test.cc @@ -36,28 +36,29 @@ int size(const T &t) { struct Position : Component<Position> { Position(float x = 0.0f, float y = 0.0f) : x(x), y(y) {} - bool operator == (const Position &other) const { return x == other.x && y == other.y; } + bool operator==(const Position &other) const { + return x == other.x && y == other.y; + } float x, y; }; - -ostream &operator << (ostream &out, const Position &position) { +ostream &operator<<(ostream &out, const Position &position) { out << "Position(" << position.x << ", " << position.y << ")"; return out; } - struct Direction : Component<Direction> { Direction(float x = 0.0f, float y = 0.0f) : x(x), y(y) {} - bool operator == (const Direction &other) const { return x == other.x && y == other.y; } + bool operator==(const Direction &other) const { + return x == other.x && y == other.y; + } float x, y; }; - -ostream &operator << (ostream &out, const Direction &direction) { +ostream &operator<<(ostream &out, const Direction &direction) { out << "Direction(" << direction.x << ", " << direction.y << ")"; return out; } @@ -65,12 +66,12 @@ ostream &operator << (ostream &out, const Direction &direction) { struct Tag : Component<Tag> { explicit Tag(string tag) : tag(tag) {} - bool operator == (const Tag &other) const { return tag == other.tag; } + bool operator==(const Tag &other) const { return tag == other.tag; } string tag; }; -ostream &operator << (ostream &out, const Tag &tag) { +ostream &operator<<(ostream &out, const Tag &tag) { out << "Tag(" << tag.tag << ")"; return out; } @@ -82,11 +83,9 @@ class EntityManagerTest : public ::testing::Test { EventManager ev; EntityManager em; - virtual void SetUp() { - } + virtual void SetUp() {} }; - TEST_F(EntityManagerTest, TestCreateEntity) { ASSERT_EQ(em.size(), 0UL); @@ -184,10 +183,8 @@ TEST_F(EntityManagerTest, TestGetEntitiesWithIntersectionOfComponents) { for (int i = 0; i < 150; ++i) { Entity e = em.create(); entities.push_back(e); - if (i % 2 == 0) - e.assign<Position>(); - if (i % 3 == 0) - e.assign<Direction>(); + if (i % 2 == 0) e.assign<Position>(); + if (i % 3 == 0) e.assign<Direction>(); } ASSERT_EQ(50, size(em.entities_with_components<Direction>())); ASSERT_EQ(75, size(em.entities_with_components<Position>())); @@ -199,22 +196,19 @@ TEST_F(EntityManagerTest, TestGetEntitiesWithComponentAndUnpacking) { Entity e = em.create(); Entity f = em.create(); Entity g = em.create(); - std::vector<std::pair<ComponentPtr<Position>, ComponentPtr<Direction>>> position_directions; + std::vector<std::pair<Position *, Direction *>> position_directions; position_directions.push_back(std::make_pair( - e.assign<Position>(1.0f, 2.0f), - e.assign<Direction>(3.0f, 4.0f))); + e.assign<Position>(1.0f, 2.0f), e.assign<Direction>(3.0f, 4.0f))); position_directions.push_back(std::make_pair( - f.assign<Position>(7.0f, 8.0f), - f.assign<Direction>(9.0f, 10.0f))); + f.assign<Position>(7.0f, 8.0f), f.assign<Direction>(9.0f, 10.0f))); auto thetag = f.assign<Tag>("tag"); g.assign<Position>(5.0f, 6.0f); int i = 0; - - ComponentPtr<Position> position; + Position *position; ASSERT_EQ(3, size(em.entities_with_components(position))); - ComponentPtr<Direction> direction; + Direction *direction; for (auto unused_entity : em.entities_with_components(position, direction)) { (void)unused_entity; ASSERT_TRUE(static_cast<bool>(position)); @@ -225,9 +219,10 @@ TEST_F(EntityManagerTest, TestGetEntitiesWithComponentAndUnpacking) { ++i; } ASSERT_EQ(2, i); - ComponentPtr<Tag> tag; + Tag *tag; i = 0; - for (auto unused_entity : em.entities_with_components(position, direction, tag)) { + for (auto unused_entity : + em.entities_with_components(position, direction, tag)) { (void)unused_entity; ASSERT_TRUE(static_cast<bool>(position)); ASSERT_TRUE(static_cast<bool>(direction)); @@ -247,9 +242,9 @@ TEST_F(EntityManagerTest, TestUnpack) { auto d = e.assign<Direction>(3.0, 4.0); auto t = e.assign<Tag>("tag"); - ComponentPtr<Position> up; - ComponentPtr<Direction> ud; - ComponentPtr<Tag> ut; + Position *up; + Direction *ud; + Tag *ut; e.unpack(up); ASSERT_EQ(p, up); e.unpack(up, ud); @@ -261,14 +256,17 @@ TEST_F(EntityManagerTest, TestUnpack) { ASSERT_EQ(t, ut); } -// gcc 4.7.2 does not allow this struct to be declared locally inside the TEST_F. +// gcc 4.7.2 does not allow this struct to be declared locally inside the +// TEST_F. // TEST_F(EntityManagerTest, TestUnpackNullMissing) { // Entity e = em.create(); // auto p = e.assign<Position>(); -// std::shared_ptr<Position> up(reinterpret_cast<Position*>(0Xdeadbeef), NullDeleter()); -// std::shared_ptr<Direction> ud(reinterpret_cast<Direction*>(0Xdeadbeef), NullDeleter()); +// std::shared_ptr<Position> up(reinterpret_cast<Position*>(0Xdeadbeef), +// NullDeleter()); +// std::shared_ptr<Direction> ud(reinterpret_cast<Direction*>(0Xdeadbeef), +// NullDeleter()); // e.unpack<Position, Direction>(up, ud); // ASSERT_EQ(p, up); // ASSERT_EQ(std::shared_ptr<Direction>(), ud); @@ -279,7 +277,8 @@ TEST_F(EntityManagerTest, TestComponentIdsDiffer) { } TEST_F(EntityManagerTest, TestEntityCreatedEvent) { - struct EntityCreatedEventReceiver : public Receiver<EntityCreatedEventReceiver> { + struct EntityCreatedEventReceiver + : public Receiver<EntityCreatedEventReceiver> { void receive(const EntityCreatedEvent &event) { created.push_back(event.entity); } @@ -298,7 +297,8 @@ TEST_F(EntityManagerTest, TestEntityCreatedEvent) { } TEST_F(EntityManagerTest, TestEntityDestroyedEvent) { - struct EntityDestroyedEventReceiver : public Receiver<EntityDestroyedEventReceiver> { + struct EntityDestroyedEventReceiver + : public Receiver<EntityDestroyedEventReceiver> { void receive(const EntityDestroyedEvent &event) { destroyed.push_back(event.entity); } @@ -322,7 +322,8 @@ TEST_F(EntityManagerTest, TestEntityDestroyedEvent) { } TEST_F(EntityManagerTest, TestComponentAddedEvent) { - struct ComponentAddedEventReceiver : public Receiver<ComponentAddedEventReceiver> { + struct ComponentAddedEventReceiver + : public Receiver<ComponentAddedEventReceiver> { void receive(const ComponentAddedEvent<Position> &event) { auto p = event.component; float n = static_cast<float>(position_events); @@ -361,13 +362,14 @@ TEST_F(EntityManagerTest, TestComponentAddedEvent) { ASSERT_EQ(10, receiver.direction_events); } + TEST_F(EntityManagerTest, TestComponentRemovedEvent) { struct ComponentRemovedReceiver : public Receiver<ComponentRemovedReceiver> { void receive(const ComponentRemovedEvent<Direction> &event) { removed = event.component; } - ComponentPtr<Direction> removed; + Direction *removed = nullptr; }; ComponentRemovedReceiver receiver; @@ -375,7 +377,7 @@ TEST_F(EntityManagerTest, TestComponentRemovedEvent) { ASSERT_FALSE(receiver.removed); Entity e = em.create(); - auto p = e.assign<Direction>(1.0, 2.0); + Direction *p = e.assign<Direction>(1.0, 2.0); e.remove<Direction>(); ASSERT_EQ(receiver.removed, p); ASSERT_FALSE(e.component<Direction>()); @@ -398,13 +400,13 @@ TEST_F(EntityManagerTest, TestEntityDestroyAll) { ASSERT_FALSE(b.valid()); } - TEST_F(EntityManagerTest, TestEntityDestroyHole) { std::vector<Entity> entities; - auto count = [this]() -> int { + auto count = [this]()->int { auto e = em.entities_with_components<Position>(); - return std::count_if(e.begin(), e.end(), [] (const Entity &) { return true; }); + return std::count_if(e.begin(), e.end(), + [](const Entity &) { return true; }); }; for (int i = 0; i < 5000; i++) { diff --git a/entityx/Event.h b/entityx/Event.h index a4b3e5d..d2a2bb2 100644 --- a/entityx/Event.h +++ b/entityx/Event.h @@ -124,13 +124,13 @@ class EventManager : entityx::help::NonCopyable { * em.subscribe<Explosion>(receiver); */ template <typename E, typename Receiver> - void subscribe(Receiver &receiver) { //NOLINT + void subscribe(Receiver &receiver) { void (Receiver::*receive)(const E &) = &Receiver::receive; auto sig = signal_for(E::family()); auto wrapper = EventCallbackWrapper<E>(std::bind(receive, &receiver, std::placeholders::_1)); auto connection = sig->connect(wrapper); - static_cast<BaseReceiver&>(receiver).connections_.push_back( - std::make_pair(EventSignalWeakPtr(sig), connection)); + BaseReceiver &base = receiver; + base.connections_.push_back(std::make_pair(EventSignalWeakPtr(sig), connection)); } void emit(const BaseEvent &event); @@ -141,7 +141,8 @@ class EventManager : entityx::help::NonCopyable { template <typename E> void emit(std::unique_ptr<E> event) { auto sig = signal_for(E::family()); - sig->emit(static_cast<BaseEvent*>(event.get())); + BaseEvent *base = event.get(); + sig->emit(base); } /** @@ -157,9 +158,10 @@ class EventManager : entityx::help::NonCopyable { */ template <typename E, typename ... Args> void emit(Args && ... args) { - E event = E(std::forward<Args>(args) ...); + E event(std::forward<Args>(args) ...); auto sig = signal_for(E::family()); - sig->emit(static_cast<BaseEvent*>(&event)); + BaseEvent *base = &event; + sig->emit(base); } int connected_receivers() const { diff --git a/entityx/Event_test.cc b/entityx/Event_test.cc index 362d361..1433a0a 100644 --- a/entityx/Event_test.cc +++ b/entityx/Event_test.cc @@ -41,7 +41,6 @@ TEST(EventManagerTest, TestEmitReceive) { ASSERT_EQ(10, explosion_system.damage_received); } - TEST(EventManagerTest, TestUntypedEmitReceive) { EventManager em; ExplosionSystem explosion_system; diff --git a/entityx/System_test.cc b/entityx/System_test.cc index 16f7372..ccb3c31 100644 --- a/entityx/System_test.cc +++ b/entityx/System_test.cc @@ -14,7 +14,6 @@ #include "entityx/System.h" #include "entityx/quick.h" - // using namespace std; using namespace entityx; using std::string; @@ -31,15 +30,15 @@ struct Direction : Component<Direction> { float x, y; }; - class MovementSystem : public System<MovementSystem> { public: explicit MovementSystem(string label = "") : label(label) {} void update(EntityManager &es, EventManager &events, double) override { - EntityManager::View entities = es.entities_with_components<Position, Direction>(); - ComponentPtr<Position> position; - ComponentPtr<Direction> direction; + EntityManager::View entities = + es.entities_with_components<Position, Direction>(); + Position *position; + Direction *direction; for (auto entity : entities) { entity.unpack<Position, Direction>(position, direction); position->x += direction->x; @@ -50,7 +49,6 @@ class MovementSystem : public System<MovementSystem> { string label; }; - class TestContainer : public EntityX { public: std::vector<Entity> created_entities; @@ -59,25 +57,19 @@ class TestContainer : public EntityX { for (int i = 0; i < 150; ++i) { Entity e = entities.create(); created_entities.push_back(e); - if (i % 2 == 0) - e.assign<Position>(1, 2); - if (i % 3 == 0) - e.assign<Direction>(1, 1); + if (i % 2 == 0) e.assign<Position>(1, 2); + if (i % 3 == 0) e.assign<Direction>(1, 1); } } }; - class SystemManagerTest : public ::testing::Test { protected: TestContainer manager; - virtual void SetUp() override { - manager.initialize(); - } + virtual void SetUp() override { manager.initialize(); } }; - TEST_F(SystemManagerTest, TestConstructSystemWithArgs) { manager.systems.add<MovementSystem>("movement"); manager.systems.configure(); @@ -85,14 +77,13 @@ TEST_F(SystemManagerTest, TestConstructSystemWithArgs) { ASSERT_EQ("movement", manager.systems.system<MovementSystem>()->label); } - TEST_F(SystemManagerTest, TestApplySystem) { manager.systems.add<MovementSystem>(); manager.systems.configure(); manager.systems.update<MovementSystem>(0.0); - ComponentPtr<Position> position; - ComponentPtr<Direction> direction; + Position *position; + Direction *direction; for (auto entity : manager.created_entities) { entity.unpack<Position, Direction>(position, direction); if (position && direction) { diff --git a/entityx/help/Pool.cc b/entityx/help/Pool.cc new file mode 100644 index 0000000..c710887 --- /dev/null +++ b/entityx/help/Pool.cc @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2012-2014 Alec Thomas <alec@swapoff.org> + * All rights reserved. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. + * + * Author: Alec Thomas <alec@swapoff.org> + */ + +#include "entityx/help/Pool.h" + +namespace entityx { + +BasePool::~BasePool() { + for (char *ptr : blocks_) { + delete[] ptr; + } +} + +} // namespace entityx diff --git a/entityx/help/Pool.h b/entityx/help/Pool.h new file mode 100644 index 0000000..f7b33d5 --- /dev/null +++ b/entityx/help/Pool.h @@ -0,0 +1,89 @@ +#pragma once + +/* + * Copyright (C) 2012-2014 Alec Thomas <alec@swapoff.org> + * All rights reserved. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. + * + * Author: Alec Thomas <alec@swapoff.org> + */ + +#include <cassert> +#include <vector> + +namespace entityx { + +/** + * Provides a resizable, semi-contiguous pool of memory for constructing + * objects in. Pointers into the pool will be invalided only when the pool is + * destroyed. + * + * The semi-contiguous nature aims to provide cache-friendly iteration. + */ +class BasePool { + public: + explicit BasePool(int element_size, int chunk_size = 8192) + : element_size_(element_size), chunk_size_(chunk_size), capacity_(0) {} + virtual ~BasePool(); + + int size() const { return size_; } + int capacity() const { return capacity_; } + int chunks() const { return blocks_.size(); } + + /// Ensure at least n elements will fit in the pool. + inline void expand(int n) { + if (n >= size_) { + if (n >= capacity_) reserve(n); + size_ = n; + } + } + + inline void reserve(int n) { + while (capacity_ < n) { + char *chunk = new char[element_size_ * chunk_size_]; + blocks_.push_back(chunk); + capacity_ += chunk_size_; + } + } + + inline void *get(int n) { + assert(n < size_); + return blocks_[n / chunk_size_] + (n % chunk_size_) * element_size_; + } + + inline const void *get(int n) const { + assert(n < size_); + return blocks_[n / chunk_size_] + (n % chunk_size_) * element_size_; + } + + virtual void destroy(int n) = 0; + + protected: + std::vector<char *> blocks_; + int element_size_; + int chunk_size_; + int size_ = 0; + int capacity_; +}; + + +/** + * Implementation of BasePool that provides type-"safe" deconstruction of + * elements in the pool. + */ +template <typename T, int ChunkSize = 8192> +class Pool : public BasePool { + public: + Pool() : BasePool(sizeof(T), ChunkSize) {} + virtual ~Pool() {} + + virtual void destroy(int n) override { + assert(n < size_); + T *ptr = static_cast<T*>(get(n)); + ptr->~T(); + } +}; + +} // namespace entityx diff --git a/entityx/help/Pool_test.cc b/entityx/help/Pool_test.cc new file mode 100644 index 0000000..c214b3b --- /dev/null +++ b/entityx/help/Pool_test.cc @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012-2014 Alec Thomas <alec@swapoff.org> + * All rights reserved. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. + * + * Author: Alec Thomas <alec@swapoff.org> + */ + + +#include <gtest/gtest.h> +#include <vector> +#include "entityx/help/Pool.h" + +struct Position { + explicit Position(int *ptr = nullptr) : ptr(ptr) { + if (ptr) (*ptr)++; + } + ~Position() { + if (ptr) (*ptr)++; + } + + float x, y; + int *ptr; +}; + + +TEST(PoolTest, TestPoolReserve) { + entityx::Pool<Position, 8> pool; + ASSERT_EQ(0, pool.capacity()); + ASSERT_EQ(0, pool.chunks()); + pool.reserve(8); + ASSERT_EQ(0, pool.size()); + ASSERT_EQ(8, pool.capacity()); + ASSERT_EQ(1, pool.chunks()); + pool.reserve(16); + ASSERT_EQ(0, pool.size()); + ASSERT_EQ(16, pool.capacity()); + ASSERT_EQ(2, pool.chunks()); +} + +TEST(PoolTest, TestPoolPointers) { + entityx::Pool<Position, 8> pool; + std::vector<char*> ptrs; + for (int i = 0; i < 4; i++) { + pool.expand(i * 8 + 8); + // NOTE: This is an attempt to ensure non-contiguous allocations from + // arena allocators. + ptrs.push_back(new char[8 * sizeof(Position)]); + } + char *p0 = static_cast<char*>(pool.get(0)); + char *p7 = static_cast<char*>(pool.get(7)); + char *p8 = static_cast<char*>(pool.get(8)); + char *p16 = static_cast<char*>(pool.get(16)); + char *p24 = static_cast<char*>(pool.get(24)); + + ASSERT_EQ(p0 + 7 * sizeof(Position), p7); + ASSERT_NE(p0 + 8 * sizeof(Position), p8); + ASSERT_NE(p8 + 8 * sizeof(Position), p16); + ASSERT_NE(p16 + 8 * sizeof(Position), p24); +} + +TEST(PoolTest, TestDeconstruct) { + entityx::Pool<Position, 8> pool; + pool.expand(8); + + void *p0 = pool.get(0); + + int counter = 0; + new(p0) Position(&counter); + ASSERT_EQ(1, counter); + pool.destroy(0); + ASSERT_EQ(2, counter); +} diff --git a/entityx/quick.h b/entityx/quick.h new file mode 100644 index 0000000..d52656b --- /dev/null +++ b/entityx/quick.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2014 Alec Thomas <alec@swapoff.org> + * All rights reserved. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. + * + * Author: Alec Thomas <alec@swapoff.org> + */ + +#pragma once + + +#include "entityx/config.h" + + +namespace entityx { + +/** + * A convenience class for instantiating an EventManager, EntityManager and + * SystemManager. + */ +class EntityX { +public: + EntityX() : entities(events), systems(entities, events) {} + + EventManager events; + EntityManager entities; + SystemManager systems; +}; + +} // namespace entityx |