From 53de92ca887e4386d8d6acb462be4e4a7978a90b Mon Sep 17 00:00:00 2001 From: Matthias Andreas Benkard Date: Sun, 9 Dec 2012 09:34:27 +0100 Subject: Initial check-in. --- cells-impl.hpp | 212 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ cells-test.cpp | 63 +++++++++++++++++ cells.hpp | 87 +++++++++++++++++++++++ dynvars-impl.hpp | 80 +++++++++++++++++++++ dynvars-test.cpp | 39 ++++++++++ dynvars.hpp | 46 ++++++++++++ makefile | 19 +++++ 7 files changed, 546 insertions(+) create mode 100644 cells-impl.hpp create mode 100644 cells-test.cpp create mode 100644 cells.hpp create mode 100644 dynvars-impl.hpp create mode 100644 dynvars-test.cpp create mode 100644 dynvars.hpp create mode 100644 makefile diff --git a/cells-impl.hpp b/cells-impl.hpp new file mode 100644 index 0000000..893d51c --- /dev/null +++ b/cells-impl.hpp @@ -0,0 +1,212 @@ +// Copyright 2012, Matthias Andreas Benkard. + +#pragma once + +#ifndef CELLS_IMPL_HPP +#define CELLS_IMPL_HPP + +#include "cells.hpp" + +#include "dynvars.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef thread_local +//#define thread_local __thread +#define thread_local +#endif + +namespace cells { + using std::cout; + using std::cerr; + using std::endl; + + using namespace dynvars; + + struct dag_node { + dag_node(std::shared_ptr item_) : item(item_) { } + std::shared_ptr item; + std::unordered_set incoming_edges; + std::unordered_set outgoing_edges; + }; + + struct transaction { + std::unordered_map, std::shared_ptr> dag; + }; + + static thread_local dynvar>> current_dependencies; + static thread_local dynvar current_transaction; + + void observer::clear_dependencies() { + for (auto const& dep : dependencies) { + dep->remove_dependent(this); + } + dependencies = {}; + } + + void observer::add_dependent(std::shared_ptr dependent) { + dependents.push_front(dependent); + } + + void observer::remove_dependent(observer* dependent) { + dependents.remove_if([&](std::weak_ptr const& other) -> bool { + std::shared_ptr other2 = other.lock(); + // note: this should also work for empty other + return (other2.get() == dependent); + }); + } + + void observer::reset_dependencies(std::forward_list> const& newdeps) { + clear_dependencies(); + dependencies = newdeps; + for (auto const& dep : newdeps) { + dep->add_dependent(shared_from_this()); + } + } + + void observer::mark() { + if (!current_transaction) { + with_transaction([&]() { this->mark(); }); + return; + } + std::shared_ptr self = shared_from_this(); + if (current_transaction->dag.find(self) == + current_transaction->dag.end()) { + std::unordered_set incoming_edges; + std::unordered_set outgoing_edges; + std::shared_ptr node = std::make_shared(self); + current_transaction->dag[self] = std::shared_ptr(node); + for (std::weak_ptr x : dependents) { + std::shared_ptr px = x.lock(); + if (px) { + px->mark(); + std::shared_ptr xn = current_transaction->dag[px]; + xn->incoming_edges.insert(node.get()); + node->outgoing_edges.insert(xn.get()); + } + } + } + } + + observer::~observer() { + clear_dependencies(); + } + + template + cell::cell() { + } + + template + void cell::update() { + T oldval = current_value; + with>, void> + (current_dependencies, + std::forward_list>(), + [=]{ + current_value = recompute(current_value); + reset_dependencies(*current_dependencies); + }); + } + + template + T& cell::get() { + if (current_dependencies) { + current_dependencies->push_front(shared_from_this()); + return current_value; + } else { + return current_value; + } + } + + template + cell::~cell() { + } + + template + T formula_cell::recompute(T old) { + return alt_formula(old); + } + + template + T formula_cell::init() { + return formula(); + } + + template + void formula_cell::reset(T value) { + this->alt_formula = ([=](T) { return value; }); + this->formula = ([=]() { return value; }); + this->mark(); + } + + template + void formula_cell::reset(std::function formula) { + this->formula = formula; + this->alt_formula = ([=](T old) { return formula(); }); + this->mark(); + } + + template + void formula_cell::reset(std::function formula, + std::function alt_formula) { + this->formula = formula; + this->alt_formula = alt_formula; + this->mark(); + } + + template + formula_cell::~formula_cell() { + } + + void with_transaction(std::function thunk) { + using std::cout; + using std::cerr; + using std::endl; + with(current_transaction, transaction(), [&]() -> void { + //cerr << "; transaction." << endl; + thunk(); + + //cerr << "; affected nodes: " << current_transaction->dag.size() << endl; + std::deque> nodes; + + // topological sort + std::forward_list independent_nodes; + auto left = current_transaction->dag.size(); + + 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()); + } + } + while (!independent_nodes.empty()) { + left--; + auto node = independent_nodes.front(); + independent_nodes.pop_front(); + nodes.push_back(node->item); + for (dag_node* other : node->outgoing_edges) { + other->incoming_edges.erase(node); + if (other->incoming_edges.size() == 0) { + independent_nodes.push_front(other); + } + } + } + if (left != 0) { + throw std::logic_error("Cell cycle detected"); + } + + for (auto const& node : nodes) { + node->update(); + } + }); + } +} + +#endif //CELLS_IMPL_HPP diff --git a/cells-test.cpp b/cells-test.cpp new file mode 100644 index 0000000..6905054 --- /dev/null +++ b/cells-test.cpp @@ -0,0 +1,63 @@ +// Copyright 2012, Matthias Andreas Benkard. + +#include "cells.hpp" + +#include +#include + +struct unit { }; + +using namespace cells; +using std::cout; +using std::endl; + +typedef formula_cell fcell; +typedef shared_ptr sfcell; +typedef formula_cell ucell; +typedef shared_ptr 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(); + + 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; }); +} + + +/* + * z + * |\ + * | \ + * | \ + * | \ + * | \ + * | \ + * | \ + * | y + * | /| + * | / | + * | / | + * | x1 x2 + * | / | + * | / | + * x0------+ + */ diff --git a/cells.hpp b/cells.hpp new file mode 100644 index 0000000..27b3633 --- /dev/null +++ b/cells.hpp @@ -0,0 +1,87 @@ +// Copyright 2012, Matthias Andreas Benkard. + +#pragma once + +#ifndef CELLS_HPP +#define CELLS_HPP + +#include +#include +#include +#include + +namespace cells { + + class observer : public virtual std::enable_shared_from_this { + private: + std::forward_list> dependents; + std::forward_list> dependencies; + + void clear_dependencies(); + void mark_dependents(); + + protected: + void mark(); + + public: + void add_dependent(std::shared_ptr dependent); + void remove_dependent(observer* dependent); + void reset_dependencies(std::forward_list> const& newdeps); + + virtual void update() = 0; + + virtual ~observer(); + }; + + + template + class cell : public observer { + private: + T current_value; + + protected: + cell(); + virtual void update(); + virtual T recompute(T) = 0; + virtual T init() = 0; + + public: + T& get(); + T& operator *() { return get(); } + //T operator ()() { return get(); } + operator T() { return get(); } + + virtual ~cell(); + }; + + + template + class formula_cell : public cell { + //friend class std::shared_ptr>; + //friend std::shared_ptr> std::make_shared>(); + private: + std::function formula; + std::function alt_formula; + + protected: + formula_cell() { } + virtual T recompute(T); + virtual T init(); + + public: + //static std::shared_ptr> make() { return std::make_shared>(); } + static std::shared_ptr> make() { return std::shared_ptr>(new formula_cell()); } + + void reset(T value); + void reset(std::function); + void reset(std::function, std::function); + + virtual ~formula_cell(); + }; + + void with_transaction(std::function); +} + +#endif //CELLS_HPP + +#include "cells-impl.hpp" diff --git a/dynvars-impl.hpp b/dynvars-impl.hpp new file mode 100644 index 0000000..5890888 --- /dev/null +++ b/dynvars-impl.hpp @@ -0,0 +1,80 @@ +// Copyright 2012, Matthias Andreas Benkard. + +#pragma once + +#ifndef DYNVARS_IMPL_HPP +#define DYNVARS_IMPL_HPP + +#include +#include +#include +#include +#include +#include +#include + +#include "dynvars.hpp" + +namespace dynvars { + +using namespace ::std; + +template +dynvar::dynvar(T val) { + this->push(val); +} + +template +dynvar& dynvar::operator =(T val) { + if (!value_stack.empty()) { + this->pop(); + } + this->push(val); + return *this; +} + +template +void dynvar::push(T val) { + value_stack.push_front(val); +} + +template +void dynvar::pop() { + value_stack.pop_front(); +} + +template +dynvar::operator bool() const { + return !value_stack.empty(); +} + +template +T& dynvar::operator *() { + return value_stack.front(); +} + +template +T* dynvar::operator ->() { + return &value_stack.front(); +} + +template +dyn::dyn(dynvar& var, R val) : myvar(var) { + myvar.push(val); +} + +template +dyn::~dyn() { + myvar.pop(); +} + +template +T +with(dynvar& var, R val, function f) { + dyn d_(var, val); + return f(); +} + +} + +#endif //DYNVARS_IMPL_HPP diff --git a/dynvars-test.cpp b/dynvars-test.cpp new file mode 100644 index 0000000..c80a7ca --- /dev/null +++ b/dynvars-test.cpp @@ -0,0 +1,39 @@ +#include +#include +#include +#include +#include +#include + +#include "dynvars.hpp" + +#include + +using namespace ::std; +using namespace ::boost::signals2; +using namespace ::dynvars; + +#define thread_local __thread +thread_local dynvar greetee; + +function +make_greeter(const string& greeting) { + return [=]() { + cout << greeting << " " << *greetee << "!" << endl; + }; +}; + +int +main(int argc, char **argv) { + greetee = "nobody"; + signal sig; + + sig.connect(make_greeter("Hi")); + + sig(); + with(greetee, "luser", [&](){ sig(); }); + with(greetee, "geek", [&](){ sig(); }); + sig(); + + return EXIT_SUCCESS; +} diff --git a/dynvars.hpp b/dynvars.hpp new file mode 100644 index 0000000..fbe852a --- /dev/null +++ b/dynvars.hpp @@ -0,0 +1,46 @@ +// Copyright 2012, Matthias Andreas Benkard. + +#pragma once + +#ifndef DYNVARS_HPP +#define DYNVARS_HPP + +#include +#include + +#define thread_local __thread + +namespace dynvars { + +template +class dynvar { +private: + std::list value_stack; +public: + dynvar() { }; + dynvar(T val); + dynvar& operator =(T val); + void push(T val); + void pop(); + T& operator *(); + T* operator ->(); + operator bool() const; +}; + +template +class dyn { + dynvar& myvar; +public: + dyn(dynvar& var, R val); + ~dyn(); +}; + +template +T +with(dynvar& var, R val, std::function f); + +} + +#endif //DYNVARS_HPP + +#include "dynvars-impl.hpp" diff --git a/makefile b/makefile new file mode 100644 index 0000000..9848e0c --- /dev/null +++ b/makefile @@ -0,0 +1,19 @@ +CXX = clang++ +CXXFLAGS = -g -O0 -stdlib=libc++ -std=c++11 + +.PHONY: all clean distclean + +all: cells-test dynvars-test + +dynvars-test: dynvars-test.cpp dynvars.hpp dynvars-impl.hpp + $(CXX) $(CXXFLAGS) dynvars-test.cpp -o dynvars-test + +cells-test: cells-test.cpp dynvars.hpp dynvars-impl.hpp cells.hpp cells-impl.hpp + $(CXX) $(CXXFLAGS) cells-test.cpp -o cells-test + +clean: + rm -f dynvars-test cells-test + rm -rf dynvars-test.dSYM cells-test.dSYM + +distclean: clean + rm -f *~ -- cgit v1.2.3