Last active
October 4, 2017 14:55
-
-
Save hoffmale/9d6b2b948dd3e6f7f6aba11cb2bc993d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <memory> | |
#include <vector> | |
template<typename... Args> class slot; | |
template<typename... Args> | |
class signal { | |
using slot_ptr = std::weak_ptr<slot<Args...>>; | |
private: | |
std::vector<slot_ptr> slots; | |
public: | |
Signal() : slots() {} | |
void trigger(Args&&... args) { | |
for(auto& slot : slots) { | |
auto lock = slot.lock(); | |
if(lock) lock->invoke(std::forward<Args>(args)...); | |
} | |
} | |
void add_slots(slot_ptr slot) { | |
slots.push_back(slot); | |
} | |
}; | |
template<typename... Args> | |
class slot { | |
private: | |
std::function<void(Args...)> handler; | |
bool blocked; | |
public: | |
template<typename Func> | |
slot(Func&& func) : handler(std::forward<Func>(func)), blocked(false) {} | |
void invoke(Args... args) { | |
if(blocked) return; | |
handler(args...); | |
} | |
void block() { | |
blocked = true; | |
} | |
void unblock() { | |
blocked = false; | |
} | |
}; | |
// usage example 1 | |
auto onEscapePressed = signal<>{}; | |
auto onCPressed = signal<>{}; | |
auto onRMBDown = signal<>{}; | |
auto onRMBUp = signal<>{}; | |
auto quitHandler = std::make_shared<slot<>>(quit); | |
onEscapePressed.add_slot(quitHandler); | |
auto toggleMouseTrackHandler = std::make_shared<slot<>>(toggleMouseTrack); | |
onCPressed.add_slot(toggleMouseTrackHandler); | |
onRMBDown.add_slot(toggleMouseTrackHandler); | |
onRMBUp.add_slot(toggleMouseTrackHandler); | |
// usage example 2 | |
// assuming keyBindings is of type std::unordered_map<KeyEvent, signal<>> | |
for(auto& keyEvent : keyEventBuffer) { | |
auto& signal = keyBindings.find(keyEvent); | |
if(signal != std::end(keyBindings)) { | |
signal.trigger(); | |
} | |
} | |
// NOTE: this loop runs in O( |keyEventBuffer| ), compared to the original loop, which ran in O( |keyEventBuffer| * |EventSlot<KeyEvent>::instances| ) | |
// alternatively, reusing the signals defined in example 1 | |
const auto escapePressed = KeyEvent(GLFW_KEY_ESCAPE, KeyCondition(1, 0)); | |
const auto cPressed = KeyEvent(GLFW_KEY_C, KeyCondition(1, 0)); | |
for(auto& keyEvent : keyEventBuffer) { | |
if(keyEvent == escapePressed) { | |
onEscapePressed.trigger(); | |
} | |
else if(keyEvent == cPressed) { | |
onCPressed.trigger(); | |
} | |
} | |
// example 3 | |
// technically, nothing to do here... (as slots get called automatically) | |
// however, it might make sense to provide an onTick signal (triggered each frame) | |
#include <chrono> | |
static const auto onTick = signal<float>{}; | |
// main game loop | |
void run(...) { | |
// ... | |
auto lastFrameTime = std::chrono::high_resolution_clock::now(); | |
auto frameTime = lastFrameTime; | |
while(game_is_running) { | |
lastFrameTime = frameTime; | |
frameTime = std::chrono::high_resolution_clock::now(); | |
auto deltaTime = std::chrono::duration<float>(frameTime - lastFrameTime).count(); | |
onTick.trigger(deltaTime); | |
// ... | |
} | |
// ... | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment