Skip to content

Instantly share code, notes, and snippets.

@hoffmale hoffmale/signal_slot.cpp
Last active Oct 4, 2017

Embed
What would you like to do?
#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
You can’t perform that action at this time.