Skip to content

Instantly share code, notes, and snippets.

@mpusz
Created December 14, 2012 11:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mpusz/4284837 to your computer and use it in GitHub Desktop.
Save mpusz/4284837 to your computer and use it in GitHub Desktop.
[OOD] Interpreter design pattern used to build Spreadsheet
//
// 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