├── .project ├── src ├── event │ ├── EventBus.cpp │ ├── HandlerRegistration.hpp │ ├── Object.hpp │ ├── PlayerChatEvent.hpp │ ├── PlayerMoveEvent.hpp │ ├── Event.hpp │ ├── EventHandler.hpp │ └── EventBus.hpp ├── Player.hpp └── Main.cpp ├── .cproject └── README.md /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | EventBus 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.cdt.managedbuilder.core.genmakebuilder 10 | clean,full,incremental, 11 | 12 | 13 | 14 | 15 | org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder 16 | full,incremental, 17 | 18 | 19 | 20 | 21 | 22 | org.eclipse.cdt.core.cnature 23 | org.eclipse.cdt.core.ccnature 24 | org.eclipse.cdt.managedbuilder.core.managedBuildNature 25 | org.eclipse.cdt.managedbuilder.core.ScannerConfigNature 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/event/EventBus.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Dan Quist 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | #include "EventBus.hpp" 24 | 25 | // Declare the static instance since this can't be done in the header file 26 | EventBus* EventBus::instance = nullptr; 27 | 28 | -------------------------------------------------------------------------------- /src/event/HandlerRegistration.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Dan Quist 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | #ifndef _SRC_EVENT_HANDLER_REGISTRATION_HPP_ 24 | #define _SRC_EVENT_HANDLER_REGISTRATION_HPP_ 25 | 26 | #include "Object.hpp" 27 | 28 | /** 29 | * \brief Interface that that allows event handlers to be removed from the EventBus 30 | */ 31 | class HandlerRegistration : public Object { 32 | public: 33 | virtual ~HandlerRegistration() { } 34 | 35 | virtual void removeHandler() = 0; 36 | }; 37 | 38 | #endif /* _SRC_EVENT_HANDLER_REGISTRATION_HPP_ */ 39 | -------------------------------------------------------------------------------- /src/event/Object.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Dan Quist 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | #ifndef _SRC_EVENT_OBJECT_HPP_ 24 | #define _SRC_EVENT_OBJECT_HPP_ 25 | 26 | /** 27 | * \brief Root class of the type hierarchy 28 | * 29 | * All events and event handlers derive from this class 30 | */ 31 | class Object { 32 | public: 33 | /** 34 | * \brief Default empty constructor 35 | */ 36 | Object() { } 37 | 38 | 39 | /** 40 | * Empty virtual destructor 41 | */ 42 | virtual ~Object() { } 43 | 44 | 45 | /** 46 | * Default empty copy constructor 47 | * @param other The instance to copy from 48 | */ 49 | Object (const Object& other) { } 50 | }; 51 | 52 | #endif /* _SRC_EVENT_OBJECT_HPP_ */ 53 | -------------------------------------------------------------------------------- /src/event/PlayerChatEvent.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Dan Quist 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | #ifndef _SRC_EVENT_PLAYER_CHAT_EVENT_HPP_ 24 | #define _SRC_EVENT_PLAYER_CHAT_EVENT_HPP_ 25 | 26 | #include "Event.hpp" 27 | #include "Player.hpp" 28 | 29 | #include 30 | 31 | class PlayerChatEvent : public Event 32 | { 33 | public: 34 | PlayerChatEvent(Object & sender, Player & player, std::string const & msg) : 35 | Event(sender), 36 | player(player), 37 | msg(msg) { 38 | } 39 | 40 | virtual ~PlayerChatEvent() { } 41 | 42 | Player & getPlayer() { 43 | return player; 44 | } 45 | 46 | std::string const & getMessage() { 47 | return msg; 48 | } 49 | 50 | private: 51 | Player & player; 52 | std::string const & msg; 53 | 54 | }; 55 | 56 | #endif /* _SRC_EVENT_PLAYER_CHAT_EVENT_HPP_ */ 57 | -------------------------------------------------------------------------------- /src/Player.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Dan Quist 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | #ifndef _SRC_PLAYER_HPP_ 24 | #define _SRC_PLAYER_HPP_ 25 | 26 | #include "Object.hpp" 27 | //#include "PlayerMoveEvent.hpp" 28 | 29 | #include 30 | 31 | class PlayerMoveEvent; 32 | 33 | /** 34 | * \brief Demo class to showcase some of the features of the EventBus 35 | * 36 | * This is not part of the core functionality and can be modified or deleted as desired 37 | */ 38 | class Player : public Object 39 | { 40 | public: 41 | Player(std::string name) : 42 | name(name), 43 | posX(0), 44 | posY(0), 45 | posZ(0) 46 | { } 47 | 48 | virtual ~Player() { 49 | 50 | } 51 | 52 | const std::string & getName() { 53 | return name; 54 | } 55 | 56 | void setPosition(int x, int y, int z) { 57 | posX = x; 58 | posY = y; 59 | posZ = z; 60 | } 61 | 62 | int getX() { 63 | return posX; 64 | } 65 | 66 | int getY() { 67 | return posY; 68 | } 69 | 70 | int getZ() { 71 | return posZ; 72 | } 73 | 74 | private: 75 | std::string name; 76 | int posX; 77 | int posY; 78 | int posZ; 79 | 80 | }; 81 | 82 | 83 | #endif /* _SRC_PLAYER_HPP_ */ 84 | -------------------------------------------------------------------------------- /src/event/PlayerMoveEvent.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Dan Quist 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | #ifndef _SRC_EVENT_PLAYER_MOVE_EVENT_HPP_ 24 | #define _SRC_EVENT_PLAYER_MOVE_EVENT_HPP_ 25 | 26 | #include "Event.hpp" 27 | #include "Player.hpp" 28 | 29 | #include 30 | 31 | /** 32 | * \brief Example event class to showcase some of the features of the EventBus 33 | * 34 | * This is not part of the core functionality and can be modified or deleted as desired 35 | */ 36 | class PlayerMoveEvent : public Event 37 | { 38 | public: 39 | PlayerMoveEvent(Object & sender, Player & player, int oldX, int oldY, int oldZ) : 40 | Event(sender), 41 | player(player), 42 | oldX(oldX), 43 | oldY(oldY), 44 | oldZ(oldZ) { 45 | } 46 | 47 | virtual ~PlayerMoveEvent() { } 48 | 49 | Player & getPlayer() { 50 | return player; 51 | } 52 | 53 | int getOldX() { 54 | return oldX; 55 | } 56 | 57 | int getOldY() { 58 | return oldY; 59 | } 60 | 61 | int getOldZ() { 62 | return oldZ; 63 | } 64 | 65 | private: 66 | Player & player; 67 | 68 | int oldX; 69 | int oldY; 70 | int oldZ; 71 | 72 | }; 73 | 74 | #endif /* _SRC_EVENT_PLAYER_MOVE_EVENT_HPP_ */ 75 | -------------------------------------------------------------------------------- /src/event/Event.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Dan Quist 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | #ifndef _SRC_EVENT_EVENT_HPP_ 24 | #define _SRC_EVENT_EVENT_HPP_ 25 | 26 | #include "Object.hpp" 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | /** 34 | * \brief The base event class, all events inherit from this class 35 | */ 36 | class Event : public Object 37 | { 38 | public: 39 | /** 40 | * \brief Default constructor 41 | * 42 | * @param typeIndex The type ID of the inherited class 43 | * @param sender The sender of the event 44 | */ 45 | Event(Object & sender) : 46 | sender(sender), 47 | canceled(false) { 48 | } 49 | 50 | 51 | /** 52 | * \brief Empty virtual destructor 53 | */ 54 | virtual ~Event() { } 55 | 56 | 57 | /** 58 | * \brief Gets the source object for this event 59 | * 60 | * @return The event sender 61 | */ 62 | Object & getSender() { 63 | return sender; 64 | } 65 | 66 | 67 | /** 68 | * \brief Gets whether the event has been canceled 69 | * 70 | * @return true if the event is canceled 71 | */ 72 | bool getCanceled() { 73 | return canceled; 74 | } 75 | 76 | 77 | /** 78 | * \brief Sets the canceled status for the event 79 | * 80 | * @param canceled Whether the even is canceled or not 81 | */ 82 | void setCanceled(bool canceled) { 83 | this->canceled = canceled; 84 | } 85 | 86 | private: 87 | Object & sender; 88 | bool canceled; 89 | 90 | }; 91 | 92 | #endif /* _SRC_EVENT_EVENT_HPP_ */ 93 | -------------------------------------------------------------------------------- /src/event/EventHandler.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Dan Quist 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | #ifndef _SRC_EVENT_EVENT_HANDLER_HPP_ 24 | #define _SRC_EVENT_EVENT_HANDLER_HPP_ 25 | 26 | #include "Object.hpp" 27 | 28 | #include 29 | #include 30 | 31 | // Forward declare the Event class 32 | class Event; 33 | 34 | /** 35 | * \brief Base class of all classes that listen for events 36 | * 37 | * For a class to be an event listener, it needs to inherit from EventHandler 38 | * with the specific event type as the template parameter. A class can inherit from 39 | * multiple EventHandler base classes each using a different template parameter. 40 | */ 41 | template 42 | class EventHandler { 43 | public: 44 | 45 | /** 46 | * \brief Default constructor that enforces the template type 47 | */ 48 | EventHandler() { 49 | // An error here indicates you're trying to implement EventHandler with a type that is not derived from Event 50 | static_assert(std::is_base_of::value, "EventHandler: T must be a class derived from Event"); 51 | } 52 | 53 | 54 | /** 55 | * \brief Empty virtual destructor 56 | */ 57 | virtual ~EventHandler() { } 58 | 59 | 60 | /** 61 | * \brief Pure virtual method for implementing the body of the listener 62 | * 63 | * @param The event instance 64 | */ 65 | virtual void onEvent(T &) = 0; 66 | 67 | 68 | /** 69 | * \brief Dispatches a generic event to the appropriate listener method 70 | * 71 | * This method is called by the EventBus and dispatches to the correct method by 72 | * dynamic casting the event parameter to the template type for this handler. 73 | * 74 | * @param e The event to dispatch 75 | */ 76 | void dispatch(Event & e) { 77 | onEvent(dynamic_cast(e)); 78 | } 79 | }; 80 | 81 | #endif /* _SRC_EVENT_EVENT_HANDLER_HPP_ */ 82 | -------------------------------------------------------------------------------- /.cproject: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 32 | 36 | 39 | 40 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # C++11 EventBus 2 | 3 | **A lightweight synchronous event framework for C++11** 4 | 5 | ## Overview 6 | Like many other event frameworks, this project allows you to reduce inter-class dependencies and decouple your C++11 code by leveraging synchronous events and event handlers. 7 | 8 | Unlike managed languages such as C# and Java, C++ is statically typed and doesn't support reflection or anonymous delegates making event systems slightly more difficult. This project attempts to create a simple and lightweight event framework that can easily be adapted to any C++11 project. The only requirements are a compiler that supports C++11 and access to the standard C++ libraries. It was inspired by the design of several public event APIs such as in Google's Web Toolkit and those found in popular game modding frameworks. 9 | 10 | The files can be dropped into any existing project as is without any modification or can be easily modified to fit an existing design. One example of a common tweak would be to replace the included Object base class with another base class that is common to the project. The EventBus requires that all events be fired from an Object type which could be replaced by an already existing class in your project. 11 | 12 | The EventBus does perform a few 'unsafe' operations by statically casting to and from void *. This was originally done using the boost::any library to avoid static casting but was changed to void * in the interest of making runtime speed faster and the code more portable. There are many projects that don't include Boost, and the casts are protected through the use of templates which remove the possibility of an incorrect cast. 13 | 14 | ## Source Files 15 | **Core Files** 16 | * */src/event/Event.hpp* 17 | * */src/event/EventBus.cpp* 18 | * */src/event/EventBus.hpp* 19 | * */src/event/EventHandler.hpp* 20 | * */src/event/HandlerRegistration.hpp* 21 | * */src/event/Object.hpp* 22 | 23 | **Example Files** 24 | 25 | These are included for example only and can be deleted when using the framework. 26 | * */src/Main.cpp* 27 | * */src/Player.hpp* 28 | * */src/event/PlayerChatEvent.hpp* 29 | * */src/event/PlayerMoveEvent.hpp* 30 | 31 | ## Usage 32 | ### Firing an Event 33 | 34 | The event bus is designed so that each event type is a unique class that inherits from the *Event* base class. This means that you will need to create the event classes that are specific to your application. There are two example event classes included in the project, *PlayerChatEvent* and *PlayerMoveEvent*. These are two events that you might commonly see in a game development framework. Below you can observe the syntax for creating and firing a player chat event. 35 | 36 | ```c++ 37 | Player player1("Player 1"); // Player instance with a name 38 | PlayerChatEvent e(*this, player1, "This is a chat message"); // Create the event object 39 | EventBus::FireEvent(e); // Fire the event 40 | ``` 41 | 42 | The PlayerChatEvent takes three parameters: the event sender or source, a reference to the player that is chatting, and then the message text. The act of firing an event is simply to declare an event object and pass it to the static *FireEvent* method. When *FireEvent* is called, it will service all the event handlers for that event type before returning. This synchronous event-based programming is extremely useful in decoupling inter-class dependencies in large code bases. 43 | 44 | ### Creating an Event Handler 45 | 46 | For events to be useful there must be something listening for the events that get fired; that's where the *EventHandler* class comes in. To listen for a particular event, you must have a class that inherits from the *EventHandler* class and implements the virtual method for that event type. Below is a class that will listen for player chat events and print out the chat message with the player name. 47 | 48 | ```c++ 49 | class PlayerListener : public EventHandler 50 | { 51 | public: 52 | virtual void onEvent(PlayerChatEvent & e) override { 53 | // Print out the name of the player and the chat message 54 | std::cout << "The player '" << e.getPlayer().getName() << "' said " << e.getMessage(); 55 | } 56 | }; 57 | ``` 58 | 59 | The *PlayerListener* class inherits from *EventHandler* and uses the template parameter of the player chat event. This is the event type that it will listen for. *EventHandler* is a template class and must always be qualified with the type of event that is being targeted. This makes it possible for a single class to listen for multiple events. The class simply needs to inherit from *EventHandler* multiple times, each with a different template parameter. 60 | 61 | All event handler functions must be virtual and called *onEvent* with a single parameter - a reference to the handled event type. The *override* keyword is new in C++11 and tells the compiler that it intends to override an existing virtual function. The compiler will throw an error if the function signature does not match an existing virtual function from an inherited class. 62 | 63 | ### Registering and Unregistering an Event Handler 64 | 65 | The last part is to register the event handler with the event bus using the *AddHandler* method as you can see below. 66 | 67 | ```c++ 68 | // Create an instance of PlayerListener and register it with the event bus 69 | PlayerListener pListener; 70 | HandlerRegistration* reg = EventBus::AddHandler(pListener); 71 | ``` 72 | 73 | Now the pListener object has been registered and the *onEvent* will be invoked every time a player chat event is fired. The template parameter is again necessary so that the same listener class can be registered as multiple event handlers; this removes any ambiguity as to which base class is being referenced. 74 | 75 | The registered class can also be unregistered by using the returned *HandlerRegistration* object. Once a handler is unregistered, it will no longer recieve events of that type. 76 | 77 | ```c++ 78 | // Unregister the listener and delete the registration object 79 | reg->removeHandler(); 80 | delete reg; 81 | ``` 82 | 83 | The *AddHandler* method also has an optional 2nd parameter that can specify a desired event source. Providing an event source during registration means that event handler will only be invoked when the event is fired from that specified source object. 84 | 85 | ### Creating a Custom Event 86 | 87 | Creating new event classes is easy - just make a new class that inherits from the base *Event* class, implement the constructor and add custom fields and methods. This is what an empty event class looks like without any custom fields or methods. 88 | 89 | ```c++ 90 | class MyCustomEvent : public Event 91 | { 92 | public: 93 | MyCustomEvent(Object & sender) : 94 | Event(sender) { 95 | } 96 | 97 | virtual ~MyCustomEvent() { } 98 | }; 99 | ``` 100 | 101 | The constructor for the *Event* base class requires the event sender as a parameter, so at a minimum your event must have at least one parameter. Beyond this shell class, any custom fields or methods can be added as desired. 102 | 103 | 104 | ## Conclusion 105 | 106 | The above snippets are very top level and the source files, notably Main.cpp, should be examined for a more complete and in-depth example. 107 | 108 | It is quite common to have many event classes in a large application; there are some game frameworks that have hundreds of individual event classes, each in its own file. Although it is easy to make new events, it is useful to consider whether some event types could share the same class if they have similar attributes. For example, a common example is to have an event that simply states that something has happened but doesn't have any data associated with it. In that case, there could be a single status event class with an enumerated list of possible status values. This event could then be used for every type of status flag in the system instead of creating an entirely new event type. 109 | 110 | 111 | -------------------------------------------------------------------------------- /src/Main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Dan Quist 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | #include "EventHandler.hpp" 24 | #include "EventBus.hpp" 25 | #include "Player.hpp" 26 | 27 | #include "PlayerMoveEvent.hpp" 28 | #include "PlayerChatEvent.hpp" 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | 35 | 36 | /** 37 | * \brief Simple example of an event handler class 38 | * 39 | * This snippet shows how to implement multiple EventHandlers in a single class 40 | */ 41 | class PlayerListener : public EventHandler, public EventHandler 42 | { 43 | public: 44 | PlayerListener() { } 45 | 46 | virtual ~PlayerListener() { } 47 | 48 | 49 | /** 50 | * \brief This event handler keeps the player inside a specific border area 51 | * 52 | * @param e The PlayerMoveEvent event 53 | */ 54 | virtual void onEvent(PlayerMoveEvent & e) override { 55 | 56 | // Ignore the event if it's already been canceled 57 | if (e.getCanceled()) { 58 | return; 59 | } 60 | 61 | Player & p = e.getPlayer(); 62 | 63 | // Cancel the event if the new player position is outside of the border area 64 | if (std::abs(p.getX()) > BORDER_SIZE || std::abs(p.getZ()) > BORDER_SIZE) { 65 | e.setCanceled(true); 66 | printf("Canceled setting player %s position - outside of border\n", p.getName().c_str()); 67 | return; 68 | } 69 | } 70 | 71 | 72 | /** 73 | * This event handler prints out a debug message whenever a chat event is fired 74 | * 75 | * @param e The PlayerChatEvent event 76 | */ 77 | virtual void onEvent(PlayerChatEvent & e) override { 78 | 79 | // Ignore the event if it's already been canceled 80 | if (e.getCanceled()) { 81 | return; 82 | } 83 | 84 | printf("The player '%s' said: %s\n", e.getPlayer().getName().c_str(), e.getMessage().c_str()); 85 | } 86 | 87 | private: 88 | static const int BORDER_SIZE = 500; 89 | 90 | }; 91 | 92 | 93 | 94 | /** 95 | * \brief Demo class showing off some functionality of the EventBus 96 | */ 97 | class EventBusDemo : public Object 98 | { 99 | public: 100 | EventBusDemo() { 101 | playerMoveReg = nullptr; 102 | playerChatReg = nullptr; 103 | } 104 | 105 | virtual ~EventBusDemo() { } 106 | 107 | 108 | /** 109 | * Demo Function 1 110 | * 111 | * Registers an event listener on player1 and shows how events can be fired and canceled 112 | */ 113 | void Demo1() { 114 | 115 | // Two unique player objects 116 | Player player1("Player1"); 117 | Player player2("Player2"); 118 | 119 | // Declare a local PlayerMoveEvent and use the event bus to fire it 120 | // There are currently no listeners so this won't actually do anything 121 | PlayerMoveEvent e(*this, player1, 0, 0, 0); 122 | EventBus::FireEvent(e); 123 | 124 | // Create the player listener instance 125 | PlayerListener playerListener; 126 | 127 | // Register the player listener to handler PlayerMoveEvent events 128 | // Passing player1 as a second parameter means it will only listen for events from that object 129 | // The return value is a HandlerRegistration pointer that can be used to unregister the event handler 130 | playerMoveReg = EventBus::AddHandler(playerListener, player1); 131 | 132 | // The playerListener gets registered again, but this time as player chat event handler 133 | // The lack of a second parameter means that it will service ALL player chat events, 134 | // regardless of the source 135 | playerChatReg = EventBus::AddHandler(playerListener); 136 | 137 | 138 | int x = 0; 139 | 140 | // This loop will attempt to increase the X position of player one 141 | // by 200 until it reaches 1000 or if the setPosition function fails. 142 | 143 | // The Player.setPosition() method fires a PlayerMoveEvent event internally 144 | // 145 | // The PlayerListener class has an event handler that will cancel the 146 | // PlayerMoveEvent if the X position is greater than 500 147 | while (x <= 1000) { 148 | printf("Changing player 1 X to %d\n", x); 149 | 150 | // This method will fail once X > 500 because of the event handler we registered 151 | if (setPlayerPostionWithEvent(player1, x, 0, 0) == true) { 152 | x += 200; 153 | } else { 154 | printf("Setting player 1 position was canceled\n"); 155 | break; 156 | } 157 | } 158 | 159 | x = 0; 160 | 161 | // This loop does the same thing as the loop above, just with player2. 162 | // Since we only registered the PlayerListener to player1, the bounds 163 | // checking will have no effect for this loop 164 | // 165 | // This shows how an event handler will handle data from one source while ignoring others 166 | while (x <= 1000) { 167 | printf("Changing player 2 X to %d\n", x); 168 | if (setPlayerPostionWithEvent(player2, x, 0, 0) == true) { 169 | x += 200; 170 | } else { 171 | printf("Setting player 2 position was canceled\n"); 172 | break; 173 | } 174 | } 175 | 176 | 177 | // Here two chat player chat events are created for each player and fired. 178 | // Since the chat listener was registered without a source object, it will service 179 | // all chat events and print both messages 180 | // 181 | // The event handler will print out the player name with the message when the event is fired 182 | PlayerChatEvent chat1(*this, player1, "Hello I am Player 1!"); 183 | EventBus::FireEvent(chat1); 184 | 185 | PlayerChatEvent chat2(*this, player2, "Hello I am Player 2!"); 186 | EventBus::FireEvent(chat2); 187 | 188 | 189 | // The HandlerRegistration object can be used to unregister the event listener 190 | playerChatReg->removeHandler(); 191 | 192 | 193 | // If a chat event is fired again, it will not be serviced since the handler has been unregistered 194 | PlayerChatEvent chat3(*this, player2, "This chat message will not be serviced"); 195 | EventBus::FireEvent(chat3); 196 | 197 | 198 | // Clean up 199 | playerMoveReg->removeHandler(); 200 | delete playerMoveReg; 201 | delete playerChatReg; 202 | } 203 | 204 | private: 205 | HandlerRegistration* playerMoveReg; 206 | HandlerRegistration* playerChatReg; 207 | 208 | 209 | bool setPlayerPostionWithEvent(Player & player, int x, int y, int z) { 210 | 211 | int savedX = player.getX(); 212 | int savedY = player.getY(); 213 | int savedZ = player.getZ(); 214 | 215 | player.setPosition(x, y, z); 216 | 217 | PlayerMoveEvent e(player, player, savedX, savedY, savedZ); 218 | EventBus::FireEvent(e); 219 | 220 | if (e.getCanceled()) { 221 | player.setPosition(savedX, savedY, savedZ); 222 | return false; 223 | } 224 | 225 | return true; 226 | } 227 | 228 | }; 229 | 230 | 231 | int main() 232 | { 233 | printf("* * * EventBus Demo Program * * * \n"); 234 | 235 | try 236 | { 237 | EventBusDemo demo; 238 | demo.Demo1(); 239 | } 240 | catch (std::runtime_error & e) 241 | { 242 | printf("Runtime exception: %s\n", e.what()); 243 | } 244 | } 245 | 246 | 247 | -------------------------------------------------------------------------------- /src/event/EventBus.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Dan Quist 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | #ifndef _SRC_EVENT_EVENT_BUS_HPP_ 24 | #define _SRC_EVENT_EVENT_BUS_HPP_ 25 | 26 | #include "Object.hpp" 27 | #include "EventHandler.hpp" 28 | #include "Event.hpp" 29 | #include "HandlerRegistration.hpp" 30 | 31 | #include 32 | #include 33 | #include 34 | 35 | 36 | /** 37 | * \brief An Event system that allows decoupling of code through synchronous events 38 | * 39 | */ 40 | class EventBus : public Object { 41 | public: 42 | /** 43 | * \brief Default empty constructor 44 | */ 45 | EventBus() { } 46 | 47 | 48 | /** 49 | * \brief Empty virtual destructor 50 | */ 51 | virtual ~EventBus() { } 52 | 53 | 54 | /** 55 | * \brief Returns the EventBus singleton instance 56 | * 57 | * Creates a new instance of the EventBus if hasn't already been created 58 | * 59 | * @return The singleton instance 60 | */ 61 | static EventBus* const GetInstance() { 62 | if (instance == nullptr) { 63 | instance = new EventBus(); 64 | } 65 | 66 | return instance; 67 | } 68 | 69 | 70 | /** 71 | * \brief Registers a new event handler to the EventBus with a source specifier 72 | * 73 | * The template parameter is the specific type of event that is being added. Since a class can 74 | * potentially inherit multiple event handlers, the template specifier will remove any ambiguity 75 | * as to which handler pointer is being referenced. 76 | * 77 | * @param handler The event handler class 78 | * @param sender The source sender object 79 | * @return An EventRegistration pointer which can be used to unregister the event handler 80 | */ 81 | template 82 | static HandlerRegistration* const AddHandler(EventHandler & handler, Object & sender) { 83 | EventBus* instance = GetInstance(); 84 | 85 | // Fetch the list of event pairs unique to this event type 86 | Registrations* registrations = instance->handlers[typeid(T)]; 87 | 88 | // Create a new collection instance for this type if it hasn't been created yet 89 | if (registrations == nullptr) { 90 | registrations = new Registrations(); 91 | instance->handlers[typeid(T)] = registrations; 92 | } 93 | 94 | // Create a new EventPair instance for this registration. 95 | // This will group the handler, sender, and registration object into the same class 96 | EventRegistration* registration = new EventRegistration(static_cast(&handler), registrations, &sender); 97 | 98 | // Add the registration object to the collection 99 | registrations->push_back(registration); 100 | 101 | return registration; 102 | } 103 | 104 | 105 | /** 106 | * \brief Registers a new event handler to the EventBus with no source specified 107 | * 108 | * @param handler The event handler class 109 | * @return An EventRegistration pointer which can be used to unregister the event handler 110 | */ 111 | template 112 | static HandlerRegistration* const AddHandler(EventHandler & handler) { 113 | EventBus* instance = GetInstance(); 114 | 115 | // Fetch the list of event pairs unique to this event type 116 | Registrations* registrations = instance->handlers[typeid(T)]; 117 | 118 | // Create a new collection instance for this type if it hasn't been created yet 119 | if (registrations == nullptr) { 120 | registrations = new Registrations(); 121 | instance->handlers[typeid(T)] = registrations; 122 | } 123 | 124 | // Create a new EventPair instance for this registration. 125 | // This will group the handler, sender, and registration object into the same class 126 | EventRegistration* registration = new EventRegistration(static_cast(&handler), registrations, nullptr); 127 | 128 | // Add the registration object to the collection 129 | registrations->push_back(registration); 130 | 131 | return registration; 132 | } 133 | 134 | 135 | /** 136 | * \brief Fires an event 137 | * 138 | * @param e The event to fire 139 | */ 140 | static void FireEvent(Event & e) { 141 | EventBus* instance = GetInstance(); 142 | 143 | Registrations* registrations = instance->handlers[typeid(e)]; 144 | 145 | // If the registrations list is null, then no handlers have been registered for this event 146 | if (registrations == nullptr) { 147 | return; 148 | } 149 | 150 | // Iterate through all the registered handlers and dispatch to each one if the sender 151 | // matches the source or if the sender is not specified 152 | for (auto & reg : *registrations) { 153 | if ((reg->getSender() == nullptr) || (reg->getSender() == &e.getSender())) { 154 | 155 | // This is where some magic happens. The void * handler is statically cast to an event handler 156 | // of generic type Event and dispatched. The dispatch function will then do a dynamic 157 | // cast to the correct event type so the matching onEvent method can be called 158 | static_cast*>(reg->getHandler())->dispatch(e); 159 | } 160 | } 161 | } 162 | 163 | 164 | private: 165 | // Singleton class instance 166 | static EventBus* instance; 167 | 168 | 169 | /** 170 | * \brief Registration class private to EventBus for registered event handlers 171 | */ 172 | class EventRegistration : public HandlerRegistration 173 | { 174 | public: 175 | typedef std::list Registrations; 176 | 177 | 178 | /** 179 | * \brief Represents a registration object for a registered event handler 180 | * 181 | * This object is stored in a collection with other handlers for the event type. 182 | * 183 | * @param handler The event handler 184 | * @param registrations The handler collection for this event type 185 | * @param sender The registered sender object 186 | */ 187 | EventRegistration(void * const handler, Registrations * const registrations, Object * const sender ) : 188 | handler(handler), 189 | registrations(registrations), 190 | sender(sender), 191 | registered(true) 192 | { } 193 | 194 | 195 | /** 196 | * \brief Empty virtual destructor 197 | */ 198 | virtual ~EventRegistration() { } 199 | 200 | 201 | /** 202 | * \brief Gets the event handler for this registration 203 | * 204 | * @return The event handler 205 | */ 206 | void * const getHandler() { 207 | return handler; 208 | } 209 | 210 | 211 | /** 212 | * \brief Gets the sender object for this registration 213 | * 214 | * @return The registered sender object 215 | */ 216 | Object* const getSender() { 217 | return sender; 218 | } 219 | 220 | 221 | /** 222 | * \brief Removes an event handler from the registration collection 223 | * 224 | * The event handler will no longer receive events for this event type 225 | */ 226 | virtual void removeHandler() { 227 | if (registered) { 228 | registrations->remove(this); 229 | registered = false; 230 | } 231 | } 232 | 233 | private: 234 | void * const handler; 235 | Registrations* const registrations; 236 | Object* const sender; 237 | 238 | bool registered; 239 | }; 240 | 241 | typedef std::list Registrations; 242 | typedef std::unordered_map*> TypeMap; 243 | 244 | TypeMap handlers; 245 | 246 | }; 247 | 248 | #endif /* _SRC_EVENT_EVENT_BUS_HPP_ */ 249 | --------------------------------------------------------------------------------