Created
December 15, 2012 19:07
Revisions
-
mpusz revised this gist
Dec 16, 2012 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -118,7 +118,7 @@ namespace spreadsheet { // Proposed interface hides not only visitors dispatching but also whole visitors usage from the user. // It is done like that because if a new expression type will be provided in the future visitor abstract // class interface will have to change and eventual children implemented by clients will stop to compile. // BTW dispatching is slightly different for 2 implemented cases to show the power of that solution template<typename T> class CVisitor : CNonCopyable { -
mpusz revised this gist
Dec 16, 2012 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -123,7 +123,7 @@ namespace spreadsheet { template<typename T> class CVisitor : CNonCopyable { public: void Run(const CExpression<T> &) = delete; // visitor dispatching failed virtual void Run(const CExpressionConstant<T> &expr) = 0; virtual void Run(const CExpressionAdd<T> &expr) = 0; virtual void Run(const CExpressionSubtract<T> &expr) = 0; -
mpusz revised this gist
Dec 15, 2012 . 1 changed file with 4 additions and 12 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -138,22 +138,14 @@ namespace spreadsheet { template<typename T> class CVisitorValue : public CVisitor<T> { T _value; public: CVisitorValue(const CExpression<T> &expr) { expr.Process(*this); } operator T() const { return _value; } void Run(const CExpressionConstant<T> &expr) override { _value = expr.value; } void Run(const CExpressionAdd<T> &expr) override { _value = CVisitorValue<T>(*expr.left) + CVisitorValue<T>(*expr.right); } void Run(const CExpressionSubtract<T> &expr) override { _value = CVisitorValue<T>(*expr.left) - CVisitorValue<T>(*expr.right); } void Run(const CExpressionDivide<T> &expr) override { _value = CVisitorValue<T>(*expr.left) / CVisitorValue<T>(*expr.right); } void Run(const CExpressionMultiply<T> &expr) override { _value = CVisitorValue<T>(*expr.left) * CVisitorValue<T>(*expr.right); } void Run(const CExpressionCell<T> &expr) override { _value = expr.spreadsheet(expr.row, expr.col); } }; -
mpusz revised this gist
Dec 15, 2012 . 1 changed file with 2 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -168,9 +168,11 @@ namespace spreadsheet { template<int Type, typename OPER> void Run(const CExpressionBinary<T, Type> &expr, OPER &&op) { _stream << "("; expr.left->Process(*this); _stream << std::forward<OPER>(op); expr.right->Process(*this); _stream << ")"; } public: -
mpusz revised this gist
Dec 15, 2012 . 1 changed file with 94 additions and 74 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -8,7 +8,6 @@ #include <iostream> #include <cassert> #include <stdexcept> // -------------------- TOOLS --------------------- @@ -19,26 +18,37 @@ inline std::unique_ptr<T> make_unique(Args&&... args) return std::unique_ptr<T>(new T{std::forward<Args>(args)...}); } class CNonCopyable { public: CNonCopyable() = default; CNonCopyable(const CNonCopyable &) = delete; CNonCopyable &operator=(const CNonCopyable &) = delete; }; // -------------------- FRAMEWORK --------------------- namespace spreadsheet { template<typename T> class CSpreadsheet; namespace detail { template<typename T> class CVisitor; } // -------------------- EXPRESSIONS --------------------- template<typename T> struct CExpression : private CNonCopyable { virtual ~CExpression() = default; virtual void Process(detail::CVisitor<T> &visitor) const = 0; }; template<typename T, class Child> struct CExpressionCRTP : CExpression<T> { void Process(detail::CVisitor<T> &visitor) const override; }; template<typename T> @@ -101,90 +111,100 @@ namespace spreadsheet { void Print() const; }; namespace detail { // -------------------- VISITORS --------------------- // Proposed interface hides not only visitors dispatching but also whole visitors usage from the user. // It is done like that because if a new expression type will be provided in the future visitor abstract // class interface will have to change and eventual children implemented by clients will start to throw. // BTW dispatching is slightly different for 2 implemented cases to show the power of that solution template<typename T> class CVisitor : CNonCopyable { public: void Run(const CExpression<T> &) { throw std::runtime_error("Visitor dispatching failed"); } virtual void Run(const CExpressionConstant<T> &expr) = 0; virtual void Run(const CExpressionAdd<T> &expr) = 0; virtual void Run(const CExpressionSubtract<T> &expr) = 0; virtual void Run(const CExpressionDivide<T> &expr) = 0; virtual void Run(const CExpressionMultiply<T> &expr) = 0; virtual void Run(const CExpressionCell<T> &expr) = 0; }; // Obtains the value of the expression // To use that visitor just call Value(CExpression<T>) template<typename T> class CVisitorValue : public CVisitor<T> { T _value; // so templates cannot be virtual methods but they can use templates inside :-) template<int Type, typename OPER> void Run(const CExpressionBinary<T, Type> &expr, OPER op) { _value = op(CVisitorValue<T>(*expr.left), CVisitorValue<T>(*expr.right)); } public: CVisitorValue(const CExpression<T> &expr) { expr.Process(*this); } operator T() const { return _value; } void Run(const CExpressionConstant<T> &expr) override { _value = expr.value; } void Run(const CExpressionAdd<T> &expr) override { Run(expr, std::plus<T>()); } void Run(const CExpressionSubtract<T> &expr) override { Run(expr, std::minus<T>()); } void Run(const CExpressionDivide<T> &expr) override { Run(expr, std::divides<T>()); } void Run(const CExpressionMultiply<T> &expr) override { Run(expr, std::multiplies<T>()); } void Run(const CExpressionCell<T> &expr) override { _value = expr.spreadsheet(expr.row, expr.col); } }; // Dumps expression to any output stream // To use that visitor just pass CExpression<T> to a stream with << operator template<typename T, typename Stream> class CVisitorOStream : public CVisitor<T> { Stream &_stream; // so templates cannot be virtual methods but they can use templates inside :-) template<int Type, typename OPER> void Run(const CExpressionBinary<T, Type> &expr, OPER &&op) { expr.left->Process(*this); _stream << std::forward<OPER>(op); expr.right->Process(*this); } public: CVisitorOStream(const CExpression<T> &expr, Stream &stream): _stream(stream) { expr.Process(*this); } operator Stream&() const { return _stream; } void Run(const CExpressionConstant<T> &expr) override { _stream << expr.value; } void Run(const CExpressionAdd<T> &expr) override { Run(expr, "+"); } void Run(const CExpressionSubtract<T> &expr) override { Run(expr, "-"); } void Run(const CExpressionDivide<T> &expr) override { Run(expr, "/"); } void Run(const CExpressionMultiply<T> &expr) override { Run(expr, "*"); } void Run(const CExpressionCell<T> &expr) override { _stream << "[" << expr.row << "," << expr.col << "]"; } }; } // User interface for expressions wrapping visitors usage template<typename T> inline T Value(const CExpression<T> &expr) { return detail::CVisitorValue<T>(expr); } template<typename Stream, typename T> inline Stream &operator<<(Stream &stream, const CExpression<T> &expr) { return detail::CVisitorOStream<T, Stream>(expr, stream); } // -------------------- OTHER --------------------- // implementation that couldn't be provided before because of type dependencies template<typename T, class Child> inline void CExpressionCRTP<T, Child>::Process(detail::CVisitor<T> &visitor) const { visitor.Run(static_cast<const Child&>(*this)); } -
mpusz created this gist
Dec 15, 2012 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,265 @@ // // author: Mateusz Pusz // #include <functional> #include <memory> #include <vector> #include <iostream> #include <cassert> #include <stdexcept> #include <boost/noncopyable.hpp> // -------------------- TOOLS --------------------- template<typename T, typename ...Args> inline std::unique_ptr<T> make_unique(Args&&... args) { return std::unique_ptr<T>(new T{std::forward<Args>(args)...}); } // -------------------- FRAMEWORK --------------------- namespace spreadsheet { template<typename T> class CSpreadsheet; template<typename T> class CVisitor; // -------------------- EXPRESSIONS --------------------- template<typename T> struct CExpression : private boost::noncopyable { virtual ~CExpression() = default; virtual void Process(CVisitor<T> &visitor) const = 0; }; template<typename T, class Child> struct CExpressionCRTP : CExpression<T> { void Process(CVisitor<T> &visitor) const override; }; template<typename T> struct CExpressionConstant : CExpressionCRTP<T, CExpressionConstant<T> > { const T value; CExpressionConstant(T value): value(std::move(value)) {} }; template<typename T, int Type> struct CExpressionBinary : CExpressionCRTP<T, CExpressionBinary<T, Type> > { const std::unique_ptr<CExpression<T> > left; const std::unique_ptr<CExpression<T> > right; CExpressionBinary(std::unique_ptr<CExpression<T> > left, std::unique_ptr<CExpression<T> > right): left(move(left)), right(move(right)) {} }; // I am too lazy to inherit that manually and have to rewrite all constructors and Process() later on :-) enum TExpressionBinaryType { ADD, SUBTRACT, MULTIPLY, DIVIDE }; template<typename T> using CExpressionAdd = CExpressionBinary<T, TExpressionBinaryType::ADD>; template<typename T> using CExpressionSubtract = CExpressionBinary<T, TExpressionBinaryType::SUBTRACT>; template<typename T> using CExpressionMultiply = CExpressionBinary<T, TExpressionBinaryType::MULTIPLY>; template<typename T> using CExpressionDivide = CExpressionBinary<T, TExpressionBinaryType::DIVIDE>; template<typename T> struct CExpressionCell : CExpressionCRTP<T, CExpressionCell<T> > { const CSpreadsheet<T> &spreadsheet; const std::size_t row; const std::size_t col; CExpressionCell(const CSpreadsheet<T> &spreadsheet, std::size_t row, std::size_t col): spreadsheet(spreadsheet), row(row), col(col) {} }; // -------------------- SPREADSHEET --------------------- template<typename T> class CSpreadsheet { std::vector<std::vector<std::unique_ptr<CExpression<T> > > > _cells; public: CSpreadsheet(std::size_t rows, std::size_t cols): _cells(rows) { for(auto &c : _cells) c.resize(cols); } void Expression(std::size_t row, std::size_t col, std::unique_ptr<CExpression<T> > expr) { _cells.at(row).at(col) = std::move(expr); } const CExpression<T> &Expression(std::size_t row, std::size_t col) const { auto &cell = _cells.at(row).at(col); if(cell) return *cell; throw std::runtime_error("No expression in cell(row: " + std::to_string(row) + ", " + std::to_string(col) + ")"); } T operator()(std::size_t row, std::size_t col) const; void Print() const; }; // -------------------- VISITORS --------------------- // Proposed interface hides not only dispatching but also visitors usage from the user. // BTW dispatching is slightly different for 2 implemented cases to show power of that solution template<typename T> class CVisitor : boost::noncopyable { public: void Run(const CExpression<T> &) { throw std::runtime_error("Visitor dispatching failed"); } virtual void Run(const CExpressionConstant<T> &expr) = 0; virtual void Run(const CExpressionAdd<T> &expr) = 0; virtual void Run(const CExpressionSubtract<T> &expr) = 0; virtual void Run(const CExpressionDivide<T> &expr) = 0; virtual void Run(const CExpressionMultiply<T> &expr) = 0; virtual void Run(const CExpressionCell<T> &expr) = 0; }; // Obtains the value of the expression // To use that visitor just call Value(CExpression<T>) template<typename T> class CVisitorValue : public CVisitor<T> { T _value; // so templates cannot be virtual methods but they can use templates inside :-) template<int Type, typename OPER> void Run(const CExpressionBinary<T, Type> &expr, OPER op) { _value = op(CVisitorValue<T>(*expr.left), CVisitorValue<T>(*expr.right)); } public: CVisitorValue(const CExpression<T> &expr) { expr.Process(*this); } operator T() const { return _value; } void Run(const CExpressionConstant<T> &expr) override { _value = expr.value; } void Run(const CExpressionAdd<T> &expr) override { Run(expr, std::plus<T>()); } void Run(const CExpressionSubtract<T> &expr) override { Run(expr, std::minus<T>()); } void Run(const CExpressionDivide<T> &expr) override { Run(expr, std::divides<T>()); } void Run(const CExpressionMultiply<T> &expr) override { Run(expr, std::multiplies<T>()); } void Run(const CExpressionCell<T> &expr) override { _value = expr.spreadsheet(expr.row, expr.col); } }; template<typename T> inline T Value(const CExpression<T> &expr) { return CVisitorValue<T>(expr); } // Dumps expression to any output stream // To use that visitor just pass CExpression<T> to a stream with << operator template<typename T, typename Stream> class CVisitorOStream : public CVisitor<T> { Stream &_stream; // so templates cannot be virtual methods but they can use templates inside :-) template<int Type, typename OPER> void Run(const CExpressionBinary<T, Type> &expr, OPER &&op) { expr.left->Process(*this); _stream << std::forward<OPER>(op); expr.right->Process(*this); } public: CVisitorOStream(const CExpression<T> &expr, Stream &stream): _stream(stream) { expr.Process(*this); } operator Stream&() const { return _stream; } void Run(const CExpressionConstant<T> &expr) override { _stream << expr.value; } void Run(const CExpressionAdd<T> &expr) override { Run(expr, "+"); } void Run(const CExpressionSubtract<T> &expr) override { Run(expr, "-"); } void Run(const CExpressionDivide<T> &expr) override { Run(expr, "/"); } void Run(const CExpressionMultiply<T> &expr) override { Run(expr, "*"); } void Run(const CExpressionCell<T> &expr) override { _stream << "[" << expr.row << "," << expr.col << "]"; } }; template<typename Stream, typename T> inline Stream &operator<<(Stream &stream, const CExpression<T> &expr) { return CVisitorOStream<T, Stream>(expr, stream); } // -------------------- DETAILS --------------------- // implementation that couldn't be provided before because of type dependencies template<typename T, class Child> inline void CExpressionCRTP<T, Child>::Process(CVisitor<T> &visitor) const { visitor.Run(static_cast<const Child&>(*this)); } // CVisitorValue usage template<typename T> inline T CSpreadsheet<T>::operator()(std::size_t row, std::size_t col) const { return Value(Expression(row, col)); } // CVisitorOStream usage template<typename T> void CSpreadsheet<T>::Print() const { for(size_t row=0; row<_cells.size(); ++row) for(size_t col=0; col<_cells[row].size(); ++col) if(_cells[row][col]) std::cout << "[" << row << "," << col << "] -> " << *_cells[row][col] << " = " << operator()(row, col) << "\n"; } } // -------------------- USAGE --------------------- using Value = double; constexpr std::size_t ROWS = 10; constexpr std::size_t COLS = 10; using CSpreadsheet = spreadsheet::CSpreadsheet<Value>; using CExpressionConstant = spreadsheet::CExpressionConstant<Value>; using CExpressionAdd = spreadsheet::CExpressionAdd<Value>; using CExpressionSubtract = spreadsheet::CExpressionSubtract<Value>; using CExpressionMultiply = spreadsheet::CExpressionMultiply<Value>; using CExpressionDivide = spreadsheet::CExpressionDivide<Value>; using CExpressionCell = spreadsheet::CExpressionCell<Value>; int main() { try { CSpreadsheet sheet(ROWS, COLS); // sheet(ROWS, COLS); // should throw // sheet(1, 1); // should throw sheet.Expression(0, 0, make_unique<CExpressionConstant>(5.0)); assert(sheet(0, 0) == 5); sheet.Expression(0, 1, make_unique<CExpressionAdd>(make_unique<CExpressionConstant>(10.0), make_unique<CExpressionCell>(sheet, 0, 0))); assert(sheet(0, 1) == 15); sheet.Expression(0, 2, make_unique<CExpressionDivide>(make_unique<CExpressionCell>(sheet, 0, 1), make_unique<CExpressionCell>(sheet, 0, 0))); assert(sheet(0, 2) == 3); // override last cell and set an expression for not filled one sheet.Expression(0, 2, make_unique<CExpressionCell>(sheet, 0, 3)); // sheet(0, 2); // should throw sheet.Expression(0, 3, make_unique<CExpressionMultiply>(make_unique<CExpressionConstant>(2.0), make_unique<CExpressionSubtract>(make_unique<CExpressionCell>(sheet, 0, 1), make_unique<CExpressionCell>(sheet, 0, 0)))); assert(sheet(0, 3) == 20); // check now assert(sheet(0, 2) == sheet(0, 3)); // print expressions sheet.Print(); } catch(const std::exception &ex) { std::cerr << "ERROR: " << ex.what() << "\n"; } }