#include <iostream>
#include <array>
#include <vector>
#include <cassert>
enum Direction {
North, East, West, South
};
enum { NumDirections = static_cast<int>(South + 1) };
constexpr Direction reciprocalDirection(Direction dir)
{
// North :- 3 - 0 = 3 -> South
// East :- 3 - 1 = 2 -> West
// West :- 3 - 2 = 1 -> East
// South :- 3 - 3 = 0 -> North
return static_cast<Direction>(
(NumDirections - 1) - static_cast<unsigned int>(dir)
);
}
struct Link
{
struct Room* m_room; // struct needed to forward-declare
Link* m_reciprocal; // The link back to us.
Link() : m_room(nullptr), m_reciprocal(nullptr) {}
operator bool() const { return m_room != nullptr; }
static void Binder(Room* lhsRoom_, Link* lhsLink_, Link* rhsLink_, Room* rhsRoom_)
{
lhsLink_->m_room = rhsRoom_;
lhsLink_->m_reciprocal = rhsLink_;
rhsLink_->m_reciprocal = lhsLink_;
rhsLink_->m_room = lhsRoom_;
}
void unbind()
{
if (m_room) {
m_room = m_reciprocal->m_room = nullptr;
m_reciprocal->m_reciprocal = nullptr;
m_reciprocal = nullptr;
}
}
~Link() { unbind(); }
};
struct Room
{
using Links = std::array<Link, NumDirections>;
template<typename T>
Room(T&& name_) : m_name(std::forward<T>(name_))
{
std::cout << "Created Room(" << m_name << ") @ " << (void*)this << "\n";
}
//! Indicates that from this room, in direction dir_, is room_
void link(Direction dir_, Room* room_);
~Room();
const Room* link(Direction dir_) const { return m_links[dir_].m_room; }
Room* link(Direction dir_) { return m_links[dir_].m_room; }
const std::string& name() const { return m_name; }
private:
std::string m_name;
Links m_links;
};
std::ostream& operator<<(std::ostream& os, const Room& rm)
{
os << "Room(" << rm.name() << ") @ " << (void*)(&rm);
return os;
}
int main()
{
Room* firstRoom = new Room("lobby");
Room* newRoom = new Room("office");
firstRoom->link(North, newRoom);
firstRoom->link(South, new Room("bedroom"));
firstRoom->link(West, new Room("bathroom"));
newRoom->link(East, new Room("closet"));
delete firstRoom;
}
void Room::link(Direction dir_, Room* room_)
{
Direction opposite = reciprocalDirection(dir_);
std::cout << "Linking " << *room_ << " to " << *this
<< " " << dir_ << ":" << opposite << "\n";
Link::Binder(this, &m_links[dir_], &room_->m_links[opposite], room_);
}
Room::~Room()
{
static size_t depth = 0;
++depth;
std::string indent(depth, '|');
std::cout << indent << "~" << *this << "\n";
std::vector<Room*> unlinked;
for (auto& link: m_links) {
if (link) {
if (link.m_room != this)
unlinked.push_back(link.m_room);
std::cout << indent << "Unlinked " << *link.m_room << " from " << *this << "\n";
link.unbind();
}
}
for (Room* room : unlinked) {
delete room;
}
--depth;
}