nf7/common/generic_memento.hh

128 lines
2.7 KiB
C++

#pragma once
#include <cassert>
#include <memory>
#include <unordered_map>
#include <utility>
#include "nf7.hh"
#include "common/file_base.hh"
#include "common/generic_context.hh"
#include "common/memento.hh"
namespace nf7 {
template <typename T>
class GenericMemento : public nf7::FileBase::Feature, public nf7::Memento {
public:
class CustomTag;
GenericMemento(nf7::FileBase& f, T&& data) noexcept :
nf7::FileBase::Feature(f),
file_(f), initial_(T(data)), data_(std::move(data)) {
}
~GenericMemento() noexcept {
tag_ = nullptr;
last_ = nullptr;
assert(map_.empty());
}
T* operator->() noexcept {
return &data_;
}
const T* operator->() const noexcept {
return &data_;
}
std::shared_ptr<Tag> Save() noexcept override {
if (tag_) return tag_;
auto [itr, emplaced] = map_.emplace(next_++, data_);
assert(emplaced);
return last_ = tag_ = std::make_shared<CustomTag>(*this, itr->first);
}
void Restore(const std::shared_ptr<Tag>& tag) override {
assert(tag);
auto itr = map_.find(tag->id());
assert(itr != map_.end());
data_ = itr->second;
tag_ = tag;
last_ = tag;
onRestore();
file_.Touch();
}
void Commit(bool quiet = false) noexcept {
tag_ = nullptr;
onCommit();
if (!quiet) file_.Touch();
}
void CommitQuiet() noexcept {
Commit(true);
}
void CommitAmend(bool quiet = false) noexcept {
if (!tag_) return;
auto itr = map_.find(tag_->id());
assert(itr != map_.end());
itr->second = data_;
onCommit();
if (!quiet) file_.Touch();
}
void CommitAmendQuiet() noexcept {
CommitAmend(true);
}
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<void()> onRestore = [](){};
std::function<void()> onCommit = [](){};
private:
nf7::File& file_;
const T initial_;
T data_;
Tag::Id next_ = 0;
std::unordered_map<Tag::Id, T> map_;
std::shared_ptr<nf7::Memento::Tag> tag_;
std::shared_ptr<nf7::Memento::Tag> last_;
void Handle(const nf7::File::Event& e) noexcept override {
switch (e.type) {
case nf7::File::Event::kAdd:
file_.env().ExecMain(
std::make_shared<nf7::GenericContext>(file_),
[this]() { CommitQuiet(); });
return;
default:
return;
}
}
};
template <typename T>
class GenericMemento<T>::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