#pragma once #include #include #include #include #include #include #include namespace erebos { using std::enable_if_t; using std::function; using std::is_same_v; using std::make_shared; using std::monostate; using std::optional; using std::shared_ptr; using std::static_pointer_cast; using std::vector; using std::weak_ptr; class BhvCurTime; class BhvTime { BhvTime(uint64_t t): t(t) {} friend BhvCurTime; public: BhvTime(const BhvCurTime &); bool operator==(const BhvTime & other) const { return t == other.t; } bool operator!=(const BhvTime & other) const { return t != other.t; } bool operator<(const BhvTime & other) const { return t < other.t; } bool operator<=(const BhvTime & other) const { return t <= other.t; } bool operator>(const BhvTime & other) const { return t > other.t; } bool operator>=(const BhvTime & other) const { return t >= other.t; } private: uint64_t t; }; class BhvCurTime { public: BhvCurTime(); ~BhvCurTime(); BhvCurTime(const BhvCurTime &) = delete; BhvCurTime(BhvCurTime &&); BhvCurTime & operator=(const BhvCurTime &) = delete; BhvCurTime & operator=(BhvCurTime &&); BhvTime time() const { return t.value(); } private: optional t; }; template class Watched { public: Watched() = default; Watched(const Watched &) = default; Watched & operator=(const Watched &) = default; Watched(Watched &&) = default; Watched & operator=(Watched &&) = default; Watched(shared_ptr> && cb): cb(move(cb)) {} ~Watched(); private: shared_ptr> cb; }; template Watched::~Watched() { BhvCurTime ctime; cb.reset(); } class BhvImplBase : public std::enable_shared_from_this { public: virtual ~BhvImplBase(); protected: void dependsOn(const BhvCurTime &, shared_ptr other); void updated(const BhvCurTime &); virtual bool needsUpdate(const BhvCurTime &) const; virtual void doUpdate(const BhvCurTime &); bool isDirty(const BhvCurTime &) const { return dirty; } vector>> watchers; private: void markDirty(const BhvCurTime &, vector> &); void updateDirty(const BhvCurTime &); bool dirty = false; vector> depends; vector> rdepends; template friend class BhvFun; }; template class BhvImpl : public BhvImplBase { public: virtual B get(const BhvCurTime &, const A &) const = 0; }; template using BhvSource = BhvImpl; template class BhvFun { public: BhvFun(shared_ptr> impl): impl(move(impl)) {} template BhvFun(shared_ptr impl): BhvFun(static_pointer_cast>(impl)) {} B get(const A & x) const { BhvCurTime ctime; return impl->get(ctime, x); } template BhvFun lens() const; const shared_ptr> impl; }; template class BhvFun { public: BhvFun(shared_ptr> impl): impl(move(impl)) {} template BhvFun(shared_ptr impl): BhvFun(static_pointer_cast>(impl)) {} A get() const { BhvCurTime ctime; return impl->get(ctime, monostate()); } Watched watch(function); template BhvFun lens() const; const shared_ptr> impl; }; template using Bhv = BhvFun; template Watched Bhv::watch(function f) { BhvCurTime ctime; auto & impl = BhvFun::impl; if (impl->needsUpdate(ctime)) impl->doUpdate(ctime); auto cb = make_shared>( [impl = BhvFun::impl, f] (const BhvCurTime & ctime) { f(impl->get(ctime, monostate())); }); impl->watchers.push_back(cb); f(impl->get(ctime, monostate())); return Watched(move(cb)); } template class BhvLambda : public BhvImpl { public: BhvLambda(function f): f(f) {} B get(const BhvCurTime &, const A & x) const override { return f(x); } private: function f; }; template BhvFun bfun(function f) { return make_shared>(f); } template class BhvComp; template BhvFun operator>>(const BhvFun & f, const BhvFun & g); template class BhvComp : public BhvImpl { public: BhvComp(const BhvFun & f, const BhvFun): f(f), g(g) {} C get(const BhvCurTime & ctime, const A & x) const override { return g.impl.get(ctime, f.impl.get(ctime, x)); } private: BhvFun f; BhvFun g; friend BhvFun operator>> (const BhvFun &, const BhvFun &); }; template class BhvComp : public BhvSource { public: BhvComp(const BhvFun & f, const BhvFun & g): f(f), g(g) {} bool needsUpdate(const BhvCurTime & ctime) const override { return !x || g.impl->get(ctime, f.impl->get(ctime, monostate())) != x.value(); } void doUpdate(const BhvCurTime & ctime) override { x = g.impl->get(ctime, f.impl->get(ctime, monostate())); } C get(const BhvCurTime & ctime, const monostate & m) const override { return x && !BhvImplBase::isDirty(ctime) ? x.value() : g.impl->get(ctime, f.impl->get(ctime, m)); } private: BhvFun f; BhvFun g; optional x; friend BhvFun operator>> (const BhvFun &, const BhvFun &); }; template BhvFun operator>>(const BhvFun & f, const BhvFun & g) { BhvCurTime ctime; auto impl = make_shared>(f, g); impl->dependsOn(ctime, f.impl); impl->dependsOn(ctime, g.impl); return impl; } template class BhvLens : public BhvImpl { public: B get(const BhvCurTime &, const A & x) const override { return A::template lens(x); } }; template template BhvFun BhvFun::lens() const { return *this >> BhvFun(make_shared>()); } template template BhvFun BhvFun::lens() const { return *this >> BhvFun(make_shared>()); } }