diff options
-rw-r--r-- | README.md | 19 | ||||
-rw-r--r-- | cells-impl.hpp | 63 | ||||
-rw-r--r-- | cells-test.cpp | 37 | ||||
-rw-r--r-- | cells.hpp | 20 |
4 files changed, 69 insertions, 70 deletions
@@ -48,14 +48,13 @@ wherever your compiler can find them and `#include <cells.hpp>`. The API primarily consists of the `cell<T>` and `formula_cell<T>` class templates. Using a `formula_cell<T>` is easy: simply construct -one using `formula_cell<T>::make()` and use the `reset` method to set -a formula to compute the value of the cell: +one and use the `reset` method to set a formula to compute the value +of the cell: typedef formula_cell<double> fcell; - typedef shared_ptr<fcell> sfcell; - sfcell x = fcell::make(); - x->reset([=](){ return 5; }); + fcell x; + x.reset([](){ return 5; }); For convenience, you can also just write something like `x->reset(5);` for setting a constant value. @@ -63,8 +62,8 @@ for setting a constant value. In order to create a dependent cell, simply make use of the other cell's value in the formula: - sfcell double_x = fcell::make(); - double_x->reset([=](){ return 2 * x->get(); }); + fcell double_x; + double_x.reset([&](){ return 2 * x.get(); }); From now on, whenever `x` changes, `double_x` will be updated accordingly. @@ -72,9 +71,9 @@ accordingly. You can create change event observers by writing formulas that make use of an observed cell and return a dummy value: - sfcell simple_observer = fcell::make(); - simple_observer->reset([=]() -> double { - (void)double_x->get(); + fcell simple_observer; + simple_observer.reset([&]() -> double { + (void)double_x.get(); std::cout << "double_x has changed!" << std::endl; return 0; }); diff --git a/cells-impl.hpp b/cells-impl.hpp index 1a9d644..86d7c98 100644 --- a/cells-impl.hpp +++ b/cells-impl.hpp @@ -45,43 +45,45 @@ namespace cells { using namespace dynvars; struct dag_node { - dag_node(std::shared_ptr<observer> item_) : item(item_) { } - std::shared_ptr<observer> item; + dag_node(observer* item_) : item(item_) { } + observer* item; std::unordered_set<dag_node*> incoming_edges; std::unordered_set<dag_node*> outgoing_edges; }; struct transaction { - std::unordered_map<std::shared_ptr<observer>, std::shared_ptr<dag_node>> dag; + std::unordered_map<observer*, dag_node*> dag; }; - static thread_local dynvar<std::forward_list<std::shared_ptr<observer>>> current_dependencies; + static thread_local dynvar<std::forward_list<observer*>> current_dependencies; static thread_local dynvar<transaction> current_transaction; inline void observer::clear_dependencies() { for (auto const& dep : dependencies) { - dep->remove_dependent(this); + std::shared_ptr<observer*> sdep = dep.lock(); + if (sdep) { + (*sdep)->remove_dependent(this); + } } dependencies = {}; } - inline void observer::add_dependent(std::shared_ptr<observer> dependent) { - dependents.push_front(dependent); + inline void observer::add_dependent(observer* dependent) { + dependents.push_front(dependent->self); } inline void observer::remove_dependent(observer* dependent) { - dependents.remove_if([&](std::weak_ptr<observer> const& other) -> bool { - std::shared_ptr<observer> other2 = other.lock(); - // note: this should also work for empty other - return (other2.get() == dependent); + dependents.remove_if([&](std::weak_ptr<observer*> const& other) -> bool { + std::shared_ptr<observer*> other2 = other.lock(); + return (!other2 || *other2 == dependent); }); } - inline void observer::reset_dependencies(std::forward_list<std::shared_ptr<observer>> const& newdeps) { + inline void observer::reset_dependencies(std::forward_list<observer*> const& newdeps) { clear_dependencies(); - dependencies = newdeps; for (auto const& dep : newdeps) { - dep->add_dependent(shared_from_this()); + dependencies.push_front(dep->self); + dep->add_dependent(this); } } @@ -90,20 +92,23 @@ namespace cells { with_transaction([&]() { this->mark(); }); return; } - std::shared_ptr<observer> self = shared_from_this(); - if (current_transaction->dag.find(self) == + if (current_transaction->dag.find(this) == current_transaction->dag.end()) { std::unordered_set<dag_node*> incoming_edges; std::unordered_set<dag_node*> outgoing_edges; - std::shared_ptr<dag_node> node = std::make_shared<dag_node>(self); - current_transaction->dag[self] = std::shared_ptr<dag_node>(node); - for (std::weak_ptr<observer> x : dependents) { - std::shared_ptr<observer> px = x.lock(); + dag_node* node = new dag_node(this); + current_transaction->dag[this] = node; + for (auto e = dependents.cbegin(); e != dependents.end(); e++) { + std::weak_ptr<observer*> const& x = *e; + std::shared_ptr<observer*> px = x.lock(); if (px) { - px->mark(); - std::shared_ptr<dag_node> xn = current_transaction->dag[px]; - xn->incoming_edges.insert(node.get()); - node->outgoing_edges.insert(xn.get()); + (*px)->mark(); + dag_node* xn = current_transaction->dag[*px]; + xn->incoming_edges.insert(node); + node->outgoing_edges.insert(xn); + } else { + // Purge dead nodes. + dependents.erase(e); } } } @@ -120,9 +125,9 @@ namespace cells { template <typename T> void cell<T>::update() { T oldval = current_value; - with<std::forward_list<std::shared_ptr<observer>>, void> + with<std::forward_list<observer*>, void> (current_dependencies, - std::forward_list<std::shared_ptr<observer>>(), + std::forward_list<observer*>(), [=]{ current_value = recompute(current_value); reset_dependencies(*current_dependencies); @@ -132,7 +137,7 @@ namespace cells { template <typename T> T& cell<T>::get() { if (current_dependencies) { - current_dependencies->push_front(shared_from_this()); + current_dependencies->push_front(this); return current_value; } else { return current_value; @@ -188,7 +193,7 @@ namespace cells { thunk(); //cerr << "; number of affected nodes: " << current_transaction->dag.size() << endl; - std::deque<std::shared_ptr<observer>> nodes; + std::deque<observer*> nodes; // topological sort std::forward_list<dag_node*> independent_nodes; @@ -197,7 +202,7 @@ namespace cells { for (auto const& o_and_n : current_transaction->dag) { auto node = o_and_n.second; if (node->incoming_edges.size() == 0) { - independent_nodes.push_front(node.get()); + independent_nodes.push_front(node); } } while (!independent_nodes.empty()) { diff --git a/cells-test.cpp b/cells-test.cpp index 1b9147b..a88fe5b 100644 --- a/cells-test.cpp +++ b/cells-test.cpp @@ -27,35 +27,28 @@ using std::cout; using std::endl; typedef formula_cell<double> fcell; -typedef shared_ptr<fcell> sfcell; typedef formula_cell<unit> ucell; -typedef shared_ptr<ucell> sucell; int main(int argc, char** argv) { - sfcell x0 = fcell::make(); - sfcell x1 = fcell::make(); - sfcell x2 = fcell::make(); - sfcell y = fcell::make(); - sfcell z = fcell::make(); - sucell a = ucell::make(); - sucell b = ucell::make(); + fcell x0, x1, x2, y, z; + ucell a, b; - with_transaction([=](){ - x0->reset(10); - x1->reset([=](){ return *x0 + 5; }); - x2->reset([=](){ return *x0 * 2; }); - y->reset([=](){ return *x1 * *x2; }); - z->reset([=](){ return *x0 * *y; }); - a->reset([=]() -> unit { cout << "z is now " << *z << "." << endl; return unit(); }); - b->reset([=]() -> unit { cout << "x2 is now " << *x2 << "." << endl; return unit(); }); + with_transaction([&](){ + x0.reset(10); + x1.reset([&](){ return *x0 + 5; }); + x2.reset([&](){ return *x0 * 2; }); + y.reset([&](){ return *x1 * *x2; }); + z.reset([&](){ return *x0 * *y; }); + a.reset([&]() -> unit { cout << "z is now " << *z << "." << endl; return unit(); }); + b.reset([&]() -> unit { cout << "x2 is now " << *x2 << "." << endl; return unit(); }); }); - x0->reset(15); - x0->reset(-20); - y->reset(-3); - x1->reset([=]() { return (double)*x0; }); - y->reset([=]() { return *x1 + *x2; }); + x0.reset(15); + x0.reset(-20); + y.reset(-3); + x1.reset([&]() { return (double)*x0; }); + y.reset([&]() { return *x1 + *x2; }); return EXIT_SUCCESS; } @@ -22,14 +22,16 @@ #include <functional> #include <memory> #include <forward_list> +#include <list> #include <algorithm> namespace cells { - class observer : public virtual std::enable_shared_from_this<observer> { + class observer { private: - std::forward_list<std::weak_ptr<observer>> dependents; - std::forward_list<std::shared_ptr<observer>> dependencies; + std::shared_ptr<observer*> self; + std::list<std::weak_ptr<observer*>> dependents; + std::forward_list<std::weak_ptr<observer*>> dependencies; void clear_dependencies(); void mark_dependents(); @@ -38,9 +40,11 @@ namespace cells { void mark(); public: - void add_dependent(std::shared_ptr<observer> dependent); + observer() : self(std::make_shared<observer*>(this)) { }; + + void add_dependent(observer* dependent); void remove_dependent(observer* dependent); - void reset_dependencies(std::forward_list<std::shared_ptr<observer>> const& newdeps); + void reset_dependencies(std::forward_list<observer*> const& newdeps); virtual void update() = 0; @@ -63,7 +67,7 @@ namespace cells { T& get(); T& operator *() { return get(); } //T operator ()() { return get(); } - operator T() { return get(); } + //operator T() { return get(); } virtual ~cell(); }; @@ -78,13 +82,11 @@ namespace cells { std::function<T (T old)> alt_formula; protected: - formula_cell() { } virtual T recompute(T); virtual T init(); public: - //static std::shared_ptr<formula_cell<T>> make() { return std::make_shared<formula_cell<T>>(); } - static std::shared_ptr<formula_cell<T>> make() { return std::shared_ptr<formula_cell<T>>(new formula_cell()); } + formula_cell() { } void reset(T value); void reset(std::function<T ()>); |