#pragma once #include #include #include #include #include "nf7.hh" #include "common/mutable_memento.hh" namespace nf7 { template class GenericMemento : public nf7::MutableMemento { public: class CustomTag; GenericMemento(T&& data, nf7::File* f = nullptr) noexcept : file_(f), initial_(T(data)), data_(std::move(data)) { } GenericMemento(T&& data, nf7::File& f) noexcept : GenericMemento(std::move(data), &f) { } ~GenericMemento() noexcept { tag_ = nullptr; last_ = nullptr; assert(map_.empty()); } std::shared_ptr Save() noexcept override { if (tag_) return tag_; auto [itr, emplaced] = map_.emplace(next_++, data_); assert(emplaced); return last_ = tag_ = std::make_shared(*this, itr->first); } void Restore(const std::shared_ptr& tag) override { assert(tag); auto itr = map_.find(tag->id()); assert(itr != map_.end()); data_ = itr->second; tag_ = tag; last_ = tag; onRestore(); if (file_) file_->Touch(); } void Commit() noexcept override { tag_ = nullptr; onCommit(); if (file_) file_->Touch(); } void CommitAmend() noexcept override { if (!tag_) return; auto itr = map_.find(tag_->id()); assert(itr != map_.end()); itr->second = data_; onCommit(); if (file_) file_->Touch(); } T& data() noexcept { return data_; } const T& data() const noexcept { return data_; } const T& last() const noexcept { if (!last_) return initial_; auto itr = map_.find(last_->id()); assert(itr != map_.end()); return itr->second; } std::function onRestore = [](){}; std::function onCommit = [](){}; private: nf7::File* const file_; const T initial_; T data_; Tag::Id next_ = 0; std::unordered_map map_; std::shared_ptr tag_; std::shared_ptr last_; }; template class GenericMemento::CustomTag final : public Tag { public: CustomTag(GenericMemento& owner, Id id) noexcept : Tag(id), owner_(&owner) { } ~CustomTag() noexcept { owner_->map_.erase(id()); } private: GenericMemento* owner_; }; } // namespace nf7