Skip to content

Commit

Permalink
Add MemberSlot
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelcowan committed Aug 25, 2019
1 parent 7ae6302 commit 927ec02
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 2 deletions.
50 changes: 48 additions & 2 deletions ass.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ namespace ass {
class Signal;

template<typename... Args>
class Slot final {
class Slot {

friend class Signal<Args...>;

Expand Down Expand Up @@ -140,10 +140,12 @@ namespace ass {
}
}

private:
protected:

std::function<void(Args...)> callback;

private:

mutable std::vector<Signal<Args...> *> signals;

};
Expand Down Expand Up @@ -281,4 +283,48 @@ namespace ass {

};

template<typename T, typename... Args>
class MemberSlot : public Slot<Args...> {
public:

MemberSlot(T *instance, void (T::*function)(Args...))
: Slot<Args...>(buildCallback()), instance(instance), function(function) {}

MemberSlot(const MemberSlot &other) : Slot<Args...>(other) {
this->callback = std::move(buildCallback(&other));
}

MemberSlot &operator=(const MemberSlot &other) {
Slot<Args...>::operator=(other);
this->callback = std::move(buildCallback(&other));
}

MemberSlot(MemberSlot &&other) noexcept : Slot<Args...>(std::move(other)) {
this->callback = std::move(buildCallback(&other));
}

MemberSlot &operator=(MemberSlot &&other) noexcept {
Slot<Args...>::operator=(std::move(other));
this->callback = std::move(buildCallback(&other));
}

private:

std::function<void(Args...)> buildCallback(const MemberSlot *other = nullptr) {
if (other) { calculateCallbackPointersFrom(other); }
return [=](Args... args) { (instance->*function)(args...); };
}

void calculateCallbackPointersFrom(const MemberSlot *other) {
auto offset = this - other;
instance = other->instance + offset;
function = other->function;
}

private:
T *instance = nullptr;

void (T::*function)(Args...) = nullptr;
};

}
89 changes: 89 additions & 0 deletions tests/unit_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,30 @@ class CountingCallable {
int m_count;
};

class TestableSlotOwner {
public:

MemberSlot<TestableSlotOwner> trigger =
MemberSlot<TestableSlotOwner>(this, &TestableSlotOwner::callback);

TestableSlotOwner() {
callbacks.clear();
}

static void requireCallbacks(const std::vector<TestableSlotOwner *> &required) {
REQUIRE(callbacks == required);
}

static std::vector<TestableSlotOwner *> callbacks;

private:
void callback() {
callbacks.push_back(this);
}
};

std::vector<TestableSlotOwner *> TestableSlotOwner::callbacks;

TEST_CASE("a new Signal should have no connections") {
Signal<> signal;

Expand Down Expand Up @@ -914,3 +938,68 @@ TEST_CASE("Slot can callback on a class function") {

REQUIRE(count == 5);
}

TEST_CASE("Class exposing MemberSlot should receive callback to its instance") {
TestableSlotOwner testable;
Signal<> signal;
signal.connect(testable.trigger);

signal.emit();

TestableSlotOwner::requireCallbacks({&testable});
}

TEST_CASE("Copy constructing class exposing MemberSlot should update callback to new instance") {
TestableSlotOwner testable;

Signal<> signal;
signal.connect(testable.trigger);

TestableSlotOwner copied(testable);

signal.emit();

TestableSlotOwner::requireCallbacks({&testable, &copied});
}

TEST_CASE("Copy assigning class exposing MemberSlot should update callback to new instance") {
TestableSlotOwner testable;

Signal<> signal;
signal.connect(testable.trigger);

TestableSlotOwner copied;
copied = testable;

signal.emit();

TestableSlotOwner::requireCallbacks({&testable, &copied});
}

TEST_CASE("Move constructing class exposing MemberSlot should update callback to new instance") {
TestableSlotOwner testable;

Signal<> signal;
signal.connect(testable.trigger);

TestableSlotOwner moved(std::move(testable));

signal.emit();

TestableSlotOwner::requireCallbacks({&moved});
}

TEST_CASE("Move assigning class exposing MemberSlot should update callback to new instance") {
TestableSlotOwner testable;

Signal<> signal;
signal.connect(testable.trigger);

TestableSlotOwner moved;
moved = std::move(testable);

signal.emit();

TestableSlotOwner::requireCallbacks({&moved});
}

0 comments on commit 927ec02

Please sign in to comment.