Skip to content

Instantly share code, notes, and snippets.

@mpusz
Created December 15, 2012 19:07

Revisions

  1. mpusz revised this gist Dec 16, 2012. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion spreadsheetVisitor.cpp
    Original 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 start to throw.
    // 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 {
  2. mpusz revised this gist Dec 16, 2012. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion spreadsheetVisitor.cpp
    Original 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> &) { throw std::runtime_error("Visitor dispatching failed"); }
    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;
  3. mpusz revised this gist Dec 15, 2012. 1 changed file with 4 additions and 12 deletions.
    16 changes: 4 additions & 12 deletions spreadsheetVisitor.cpp
    Original 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;

    // 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 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); }
    };

  4. mpusz revised this gist Dec 15, 2012. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions spreadsheetVisitor.cpp
    Original 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:
  5. mpusz revised this gist Dec 15, 2012. 1 changed file with 94 additions and 74 deletions.
    168 changes: 94 additions & 74 deletions spreadsheetVisitor.cpp
    Original file line number Diff line number Diff line change
    @@ -8,7 +8,6 @@
    #include <iostream>
    #include <cassert>
    #include <stdexcept>
    #include <boost/noncopyable.hpp>


    // -------------------- 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;
    template<typename T> class CVisitor;

    namespace detail {

    template<typename T> class CVisitor;

    }

    // -------------------- EXPRESSIONS ---------------------

    template<typename T>
    struct CExpression : private boost::noncopyable {
    struct CExpression : private CNonCopyable {
    virtual ~CExpression() = default;
    virtual void Process(CVisitor<T> &visitor) const = 0;
    virtual void Process(detail::CVisitor<T> &visitor) const = 0;
    };

    template<typename T, class Child>
    struct CExpressionCRTP : CExpression<T> {
    void Process(CVisitor<T> &visitor) const override;
    void Process(detail::CVisitor<T> &visitor) const override;
    };

    template<typename T>
    @@ -101,90 +111,100 @@ namespace spreadsheet {
    void Print() const;
    };


    namespace detail {

    // -------------------- VISITORS ---------------------

    // -------------------- 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;
    // 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;
    };

    // 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;
    // 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); }
    };

    // 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 << "]"; }
    };
    // 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 CVisitorOStream<T, Stream>(expr, stream);
    return detail::CVisitorOStream<T, Stream>(expr, stream);
    }


    // -------------------- DETAILS ---------------------
    // -------------------- OTHER ---------------------
    // 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
    inline void CExpressionCRTP<T, Child>::Process(detail::CVisitor<T> &visitor) const
    {
    visitor.Run(static_cast<const Child&>(*this));
    }
  6. mpusz created this gist Dec 15, 2012.
    265 changes: 265 additions & 0 deletions spreadsheetVisitor.cpp
    Original 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";
    }
    }