Skip to content

Commit

Permalink
Add Delegate<T> class as a lightweight signal/slot implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
mogemimi committed Aug 6, 2019
1 parent 253604c commit b755376
Show file tree
Hide file tree
Showing 7 changed files with 385 additions and 0 deletions.
2 changes: 2 additions & 0 deletions build/pomdog/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -224,11 +224,13 @@ set(POMDOG_SOURCES_CORE
${POMDOG_DIR}/include/Pomdog/Reactive/Subjects/KeyDownSubject.hpp
${POMDOG_DIR}/include/Pomdog/Signals/Connection.hpp
${POMDOG_DIR}/include/Pomdog/Signals/ConnectionList.hpp
${POMDOG_DIR}/include/Pomdog/Signals/Delegate.hpp
${POMDOG_DIR}/include/Pomdog/Signals/Event.hpp
${POMDOG_DIR}/include/Pomdog/Signals/EventQueue.hpp
${POMDOG_DIR}/include/Pomdog/Signals/Helpers.hpp
${POMDOG_DIR}/include/Pomdog/Signals/ScopedConnection.hpp
${POMDOG_DIR}/include/Pomdog/Signals/Signal.hpp
${POMDOG_DIR}/include/Pomdog/Signals/detail/DelegateBody.hpp
${POMDOG_DIR}/include/Pomdog/Signals/detail/EventBody.hpp
${POMDOG_DIR}/include/Pomdog/Signals/detail/ForwardDeclarations.hpp
${POMDOG_DIR}/include/Pomdog/Signals/detail/SignalBody.hpp
Expand Down
1 change: 1 addition & 0 deletions include/Pomdog/Pomdog.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ namespace Pomdog {

#include "Signals/Connection.hpp"
#include "Signals/ConnectionList.hpp"
#include "Signals/Delegate.hpp"
#include "Signals/Event.hpp"
#include "Signals/EventQueue.hpp"
#include "Signals/Helpers.hpp"
Expand Down
79 changes: 79 additions & 0 deletions include/Pomdog/Signals/Delegate.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright (c) 2013-2019 mogemimi. Distributed under the MIT license.

#pragma once

#include "Pomdog/Basic/Export.hpp"
#include "Pomdog/Signals/Connection.hpp"
#include "Pomdog/Signals/detail/DelegateBody.hpp"
#include <functional>
#include <memory>
#include <utility>

namespace Pomdog {

template <typename... Arguments>
class POMDOG_EXPORT Delegate<void(Arguments...)> final {
public:
Delegate();
Delegate(const Delegate&) = delete;
Delegate(Delegate&&) = default;
Delegate& operator=(const Delegate&) = delete;
Delegate& operator=(Delegate&&) = default;

[[nodiscard]] Connection Connect(const std::function<void(Arguments...)>& slot);

[[nodiscard]] Connection Connect(std::function<void(Arguments...)>&& slot);

void Disconnect();

void operator()(Arguments... arguments);

[[nodiscard]] bool IsConnected() const noexcept;

private:
using Body = Detail::Signals::DelegateBody<void(Arguments...)>;
std::shared_ptr<Body> body;
};

template <typename... Arguments>
Delegate<void(Arguments...)>::Delegate()
: body(std::make_shared<Body>())
{
}

template <typename... Arguments>
void Delegate<void(Arguments...)>::operator()(Arguments... arguments)
{
POMDOG_ASSERT(body != nullptr);
body->Emit(std::forward<Arguments>(arguments)...);
}

template <typename... Arguments>
Connection Delegate<void(Arguments...)>::Connect(const std::function<void(Arguments...)>& slot)
{
POMDOG_ASSERT(body != nullptr);
return Connection{body->Connect(slot)};
}

template <typename... Arguments>
Connection Delegate<void(Arguments...)>::Connect(std::function<void(Arguments...)>&& slot)
{
POMDOG_ASSERT(body != nullptr);
return Connection{body->Connect(std::move(slot))};
}

template <typename... Arguments>
void Delegate<void(Arguments...)>::Disconnect()
{
POMDOG_ASSERT(body != nullptr);
body->Disconnect();
}

template <typename... Arguments>
bool Delegate<void(Arguments...)>::IsConnected() const noexcept
{
POMDOG_ASSERT(body != nullptr);
return body->IsConnected();
}

} // namespace Pomdog
137 changes: 137 additions & 0 deletions include/Pomdog/Signals/detail/DelegateBody.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// Copyright (c) 2013-2019 mogemimi. Distributed under the MIT license.

#pragma once

#include "Pomdog/Basic/Export.hpp"
#include "Pomdog/Signals/detail/ForwardDeclarations.hpp"
#include "Pomdog/Utility/Assert.hpp"
#include <algorithm>
#include <cstdint>
#include <functional>
#include <memory>
#include <mutex>
#include <optional>
#include <type_traits>
#include <utility>
#include <vector>

namespace Pomdog::Detail::Signals {

template <typename Function>
class POMDOG_EXPORT DelegateConnectionBody final : public ConnectionBody {
private:
using WeakSignal = std::weak_ptr<DelegateBody<Function>>;

WeakSignal weakSignal;
std::optional<std::int32_t> slotID;

public:
DelegateConnectionBody() = default;
DelegateConnectionBody(const DelegateConnectionBody&) = delete;
DelegateConnectionBody& operator=(const DelegateConnectionBody&) = delete;

DelegateConnectionBody(WeakSignal&& weakSignalIn, std::int32_t slotIDIn)
: weakSignal(std::forward<WeakSignal>(weakSignalIn))
, slotID(slotIDIn)
{
}

void Disconnect() override
{
if (slotID == std::nullopt) {
return;
}

if (auto lockedSignal = weakSignal.lock(); lockedSignal != nullptr) {
lockedSignal->Disconnect(*slotID);
weakSignal.reset();
}
slotID = std::nullopt;
}

[[nodiscard]] bool Valid() const override
{
if (slotID == std::nullopt) {
return false;
}
if (auto lockedSignal = weakSignal.lock(); lockedSignal != nullptr) {
return lockedSignal->IsConnected(*slotID);
}
return false;
}

std::unique_ptr<ConnectionBody> DeepCopy() const override
{
auto conn = std::make_unique<DelegateConnectionBody>();
conn->weakSignal = weakSignal;
conn->slotID = slotID;
return conn;
}
};

template <typename... Arguments>
class POMDOG_EXPORT DelegateBody<void(Arguments...)> final
: public std::enable_shared_from_this<DelegateBody<void(Arguments...)>> {
private:
using ConnectionBodyType = DelegateConnectionBody<void(Arguments...)>;

public:
DelegateBody() = default;
DelegateBody(const DelegateBody&) = delete;
DelegateBody(DelegateBody&&) = delete;
DelegateBody& operator=(const DelegateBody&) = delete;
DelegateBody& operator=(DelegateBody&&) = delete;

template <typename Function>
[[nodiscard]] std::unique_ptr<ConnectionBodyType> Connect(Function&& slotIn)
{
slot = std::forward<Function>(slotIn);
++slotID;

if (slot == nullptr) {
return nullptr;
}

std::weak_ptr<DelegateBody> weakSignal = this->shared_from_this();
POMDOG_ASSERT(!weakSignal.expired());
return std::make_unique<ConnectionBodyType>(std::move(weakSignal), slotID);
}

void Disconnect()
{
slot = nullptr;
}

void Disconnect(std::int32_t slotIDIn)
{
if (slotIDIn != slotID) {
return;
}
slot = nullptr;
}

void Emit(Arguments... arguments)
{
// NOTE: Copy the function object to a temporary object to call it
// safely because std::function can be self-destroyed during call.
if (auto functor = slot; functor != nullptr) {
functor(std::forward<Arguments>(arguments)...);
}
}

[[nodiscard]] bool IsConnected() const noexcept
{
return (slotID > 0) && (slot != nullptr);
}

[[nodiscard]] bool IsConnected(std::int32_t slotIDIn) const noexcept
{
return (slotID == slotIDIn) && (slot != nullptr);
}

private:
std::function<void(Arguments...)> slot;
std::int32_t slotID = 0;
};

} // namespace Pomdog::Detail::Signals
6 changes: 6 additions & 0 deletions include/Pomdog/Signals/detail/ForwardDeclarations.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ using Slot = std::function<Function>;
template <typename Function>
class SignalBody;

template <typename Function>
class DelegateBody;

} // namespace Detail::Signals

class Event;
Expand All @@ -26,4 +29,7 @@ class ConnectionList;
template <typename Function>
class Signal;

template <typename Function>
class Delegate;

} // namespace Pomdog
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ add_executable(PomdogTest
${POMDOG_TEST_DIR}/Math/Vector4Test.cpp
${POMDOG_TEST_DIR}/Signals/ConnectionTest.cpp
${POMDOG_TEST_DIR}/Signals/ConnectionListTest.cpp
${POMDOG_TEST_DIR}/Signals/DelegateTest.cpp
${POMDOG_TEST_DIR}/Signals/EventQueueTest.cpp
${POMDOG_TEST_DIR}/Signals/EventTest.cpp
${POMDOG_TEST_DIR}/Signals/HelpersTest.cpp
Expand Down
Loading

0 comments on commit b755376

Please sign in to comment.