Created
December 14, 2012 11:43
-
-
Save mpusz/4284837 to your computer and use it in GitHub Desktop.
[OOD] Interpreter design pattern used to build Spreadsheet
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 characters
// | |
// author: Mateusz Pusz | |
// | |
#include <functional> | |
#include <memory> | |
#include <array> | |
#include <iostream> | |
#include <cassert> | |
// -------------------- 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)...}); | |
} | |
class CNonCopyable { | |
public: | |
CNonCopyable() = default; | |
CNonCopyable(const CNonCopyable &) = delete; | |
CNonCopyable &operator=(const CNonCopyable &) = delete; | |
}; | |
// -------------------- FRAMEWORK --------------------- | |
namespace spreadsheet { | |
template<typename T> class CExpression; | |
template<typename T, std::size_t ROWS, std::size_t COLS> | |
class CSpreadsheet { | |
std::array<std::array<std::unique_ptr<CExpression<T> >, COLS>, ROWS> _cells; | |
public: | |
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) | |
throw std::runtime_error("No expression in cell(row: " + std::to_string(row) + ", " + std::to_string(col) + ")"); | |
return *cell; | |
} | |
T operator()(std::size_t row, std::size_t col) const | |
{ | |
return Expression(row, col).Value(); | |
} | |
}; | |
template<typename T> | |
class CExpression : CNonCopyable { | |
public: | |
virtual ~CExpression() = default; | |
virtual T Value() const = 0; | |
}; | |
template<typename T> | |
class CExpressionConstant : public CExpression<T> { | |
T _value; | |
public: | |
CExpressionConstant(T value): _value(std::move(value)) {} | |
T Value() const override { return _value; } | |
}; | |
// note: template template used here :-) | |
template<typename T, template<class> class EXPR> | |
class CExpressionBinary : public CExpression<T> { | |
std::unique_ptr<CExpression<T> > _left; | |
std::unique_ptr<CExpression<T> > _right; | |
public: | |
CExpressionBinary(std::unique_ptr<CExpression<T> > left, std::unique_ptr<CExpression<T> > right): | |
_left(move(left)), _right(move(right)) {} | |
T Value() const override { return EXPR<T>()(_left->Value(), _right->Value()); } | |
}; | |
template<typename T> using CExpressionAdd = CExpressionBinary<T, std::plus>; | |
template<typename T> using CExpressionSubtract = CExpressionBinary<T, std::minus>; | |
template<typename T> using CExpressionMultiply = CExpressionBinary<T, std::multiplies>; | |
template<typename T> using CExpressionDivide = CExpressionBinary<T, std::divides>; | |
template<typename T, std::size_t ROWS, std::size_t COLS> | |
class CExpressionCell : public CExpression<T> { | |
const CSpreadsheet<T, ROWS, COLS> &_spreadsheet; | |
const std::size_t _row; | |
const std::size_t _col; | |
public: | |
CExpressionCell(const CSpreadsheet<T, ROWS, COLS> &spreadsheet, std::size_t row, std::size_t col): | |
_spreadsheet(spreadsheet), _row(row), _col(col) {} | |
T Value() const override { return _spreadsheet(_row, _col); } | |
}; | |
} | |
// -------------------- USAGE --------------------- | |
using Value = double; | |
constexpr std::size_t ROWS = 10; | |
constexpr std::size_t COLS = 10; | |
using CSpreadsheet = spreadsheet::CSpreadsheet<Value, ROWS, COLS>; | |
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, ROWS, COLS>; | |
int main() | |
{ | |
try { | |
CSpreadsheet sheet; | |
// 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)); | |
} | |
catch(const std::exception &ex) { | |
std::cerr << "ERROR: " << ex.what() << "\n"; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment