--- /dev/null
+#ifndef __COCO_ADT_PTR_SLOT_H__
+#define __COCO_ADT_PTR_SLOT_H__
+
+#include <vector>
+#include <memory>
+
+#include <functional>
+#include <cassert>
+
+namespace coco
+{
+
+template <typename T> class PtrSlot
+{
+public:
+ struct Hook
+ {
+ virtual ~Hook() = default;
+
+ virtual void onTake(T *value) = 0;
+ virtual void onRelease(T *value) = 0;
+ };
+
+public:
+ PtrSlot() : _value{nullptr}
+ {
+ // DO NOTHING
+ }
+
+public:
+ virtual ~PtrSlot() { value(nullptr); }
+
+public:
+ T *value(void) const { return _value; }
+
+public:
+ void value(T *value)
+ {
+ if (_value)
+ {
+ notifyRelease(_value);
+ _value = nullptr;
+ }
+
+ assert(_value == nullptr);
+
+ if (value)
+ {
+ _value = value;
+ notifyTake(_value);
+ }
+
+ assert(_value == value);
+ }
+
+protected:
+ void insert(std::unique_ptr<Hook> &&hook) { _hooks.emplace_back(std::move(hook)); }
+
+private:
+ void notifyTake(T *value) const
+ {
+ for (const auto &hook : _hooks)
+ {
+ hook->onTake(value);
+ }
+ }
+
+private:
+ void notifyRelease(T *value) const
+ {
+ for (const auto &hook : _hooks)
+ {
+ hook->onRelease(value);
+ }
+ }
+
+private:
+ T *_value;
+
+private:
+ std::vector<std::unique_ptr<Hook>> _hooks;
+};
+
+} // namespace coco
+
+#endif // __COCO_ADT_PTR_SLOT_H__
--- /dev/null
+#include "coco/ADT/PtrSlot.h"
+
+#include <nncc/foundation/Memory.h>
+
+#include <gtest/gtest.h>
+
+namespace
+{
+struct Entity
+{
+ Entity() = default;
+};
+
+struct EntitySlot final : public coco::PtrSlot<Entity>
+{
+private:
+ class Hook final : public coco::PtrSlot<Entity>::Hook
+ {
+ public:
+ Hook(std::set<Entity *> *bounded) : _bounded{bounded}
+ {
+ // DO NOTING
+ }
+
+ public:
+ void onTake(Entity *node) { _bounded->insert(node); }
+ void onRelease(Entity *node) { _bounded->erase(node); }
+
+ private:
+ std::set<Entity *> *_bounded;
+ };
+
+public:
+ EntitySlot(std::set<Entity *> *bounded) { insert(nncc::foundation::make_unique<Hook>(bounded)); }
+};
+} // namespace
+
+TEST(ADT_PTR_SLOT, usecase)
+{
+ std::set<Entity *> bounded;
+
+ EntitySlot slot{&bounded};
+
+ ASSERT_EQ(slot.value(), nullptr);
+
+ Entity e_0;
+ Entity e_1;
+
+ slot.value(&e_0);
+
+ ASSERT_EQ(slot.value(), &e_0);
+ ASSERT_EQ(bounded.size(), 1);
+ ASSERT_EQ(bounded.count(&e_0), 1);
+
+ slot.value(&e_1);
+
+ ASSERT_EQ(slot.value(), &e_1);
+ ASSERT_EQ(bounded.size(), 1);
+ ASSERT_EQ(bounded.count(&e_1), 1);
+
+ slot.value(nullptr);
+
+ ASSERT_EQ(slot.value(), nullptr);
+ ASSERT_EQ(bounded.size(), 0);
+}