C++-Programmierung/ Eine Matrix-Bibliothek – mitrax/ proxy.hpp

#ifndef _mitrax_mitrax_proxy_hpp_INCLUDED_
#define _mitrax_mitrax_proxy_hpp_INCLUDED_
/// \file proxy.hpp
///
/// \brief Proxyklassen für Zeilen und Spalten eines Matrixobjekts
///
/// In dieser Datei werden die vier Proxyklassen definiert, welche jeweils ein Zeile oder Spalte
/// einer konstante oder nicht-konstanten Matrix repräsentieren. Weiterhin steht hier die Funktion
/// <code>element_swap()</code> welche alle Elemente zweier Zeilen oder Spalten miteinander
/// vertauscht.

#include "detail/proxy_iterator.hpp"
#include "exception.hpp"

namespace mitrax{

// Forward-Deklarationen
template < typename Matrix > class row_proxy;
template < typename Matrix > class column_proxy;

namespace detail{

/// \brief Private Basisklasse für alle Proxys
///
/// Diese Klasse enthält die Datenmember der Proxyklassen. Alle vier Proxyklassen werden direkt
/// oder indirekt private von dieser Klasse angeleitet. Es ist nicht möglich Objekte von dieser
/// Klasse zu erzeugen. Die Klasse ist eine Helferklasse mit der ein Benutzer keinen direkten
/// Kontakt haben sollte.
///
/// Compilergenrierte Methoden:
///   - Kopierkonstruktor
///   - Kopierzuweisung
///   - Destruktor
template < typename Matrix >
class line_const_proxy{
protected:
    /// \brief Die Klasseninstanz des Klassentemplats matrix zu der der Proxy gehört
    typedef Matrix                      matrix_type;
    /// \brief Typ der Elemente der Matrix
    typedef typename Matrix::value_type value_type;
    /// \brief Typ der Dimensionselemente der Matrix
    typedef typename Matrix::size_type  size_type;

    /// \brief Konstruktor zur Objekterzeugung aus einem Verweis auf ein konstantes
    ///        <code>matrix</code>-Objekt und einer Zeilen-/Spaltennummer
    line_const_proxy(matrix_type const& matrix, size_type const& pos):
        matrix_(&matrix), pos_(pos){}

    /// \brief Lesezugriff auf die verwaltete Zeilen-/Spaltennummer
    size_type const pos()const{ return pos_; }

    /// \brief Lesezugriff auf die referenzierte Matrix
    matrix_type const& matrix_const()const{ return *matrix_; }

private:
    /// \brief Die referenzierte Matrix
    matrix_type const* matrix_;
    /// \brief Die verwaltete Zeile/Spalte aus der Matrix
    size_type          pos_;
};

/// \brief Private Basisklasse für alle Proxys für nicht-konstante Matrizen
///
/// Diese Klasse stellt das Matrixobjekt als nicht-konstante Referenz zu Verfügung. Alle
/// Proxyklassen für nicht-konstante Matrizen werden private von dieser Klasse angeleitet. Es ist
/// nicht möglich Objekte von dieser Klasse zu erzeugen. Die Klasse ist eine Helferklasse mit der
/// ein Benutzer keinen direkten Kontakt haben sollte.
///
/// Compilergenerierte Methoden:
///   - Kopierkonstruktor
///   - Kopierzuweisung
///   - Destruktor
template < typename Matrix >
class line_proxy: protected line_const_proxy< Matrix >{
protected:
    /// \brief Die Klasseninstanz des Klassentemplats matrix zu der der Proxy gehört
    typedef Matrix                      matrix_type;
    /// \brief Typ der Elemente der Matrix
    typedef typename Matrix::value_type value_type;
    /// \brief Typ der Dimensionselemente der Matrix
    typedef typename Matrix::size_type  size_type;

    /// \brief Konstruktor zur Objekterzeugung aus einem Verweis auf <code>matrix</code>-Objekt und
    ///        einer Zeilen-/Spaltennummer
    line_proxy(matrix_type& matrix, size_type const& pos):
        detail::line_const_proxy< matrix_type >(matrix, pos){}

    /// \brief Schreibzugriff auf die referenzierte Matrix
    matrix_type& matrix()const{ return const_cast< matrix_type& >(this->matrix_const()); }
};

/// \brief Helfer zu Iteratorerzeugung in Zeilenproxys (<code>begin()</code>)
template < typename Matrix, typename Iterator >
inline
Iterator const
create_begin_row_iterator(
    Matrix& matrix,
    typename Matrix::size_type const& row
){
    return Iterator(
        matrix.begin() + (matrix.columns() * row)
    );
}

/// \brief Helfer zu Iteratorerzeugung in Zeilenproxys (<code>end()</code>)
template < typename Matrix, typename Iterator >
inline
Iterator const
create_end_row_iterator(
    Matrix& matrix,
    typename Matrix::size_type const& row
){
    return Iterator(
        matrix.begin() + (matrix.columns() * (row + 1))
    );
}


/// \brief Helfer zu Iteratorerzeugung in Spaltenproxys (<code>begin()</code>)
template < typename Matrix, typename Iterator >
inline
Iterator const
create_begin_column_iterator(
    Matrix& matrix,
    typename Matrix::size_type const& column
){
    return Iterator(
        matrix,
        column
    );
}

/// \brief Helfer zu Iteratorerzeugung in Spaltenproxys (<code>end()</code>)
template < typename Matrix, typename Iterator >
inline
Iterator const
create_end_column_iterator(
    Matrix& matrix,
    typename Matrix::size_type const& column
){
    return Iterator(
        matrix,
        column + matrix.columns() * matrix.rows()
    );
}

}

/// \brief Proxyklasse die eine Zeile eines konstanten <code>matrix</code>-Objekts repräsentiert
template < typename Matrix >
class row_const_proxy: private detail::line_const_proxy< Matrix >{
public:
    /// \brief Die Klasseninstanz des Klassentemplats <code>matrix</code> zu der der Proxy gehört
    typedef Matrix                                    matrix_type;
    /// \brief Typ der Elemente der Matrix
    typedef typename Matrix::value_type               value_type;
    /// \brief Iteratoren für Lesezugriff
    typedef detail::row_const_iterator< matrix_type > const_iterator;
    /// \brief Iteratoren für Lesezugriff
    typedef const_iterator                            iterator;
    /// \brief Typ der Dimensionselemente der Matrix
    typedef typename Matrix::size_type                size_type;
    /// \brief <code>difference_type</code> der Iteratoren
    typedef typename const_iterator::difference_type  difference_type;

    /// \brief Objekterzeugung aus einem Verweis auf ein konstantes <code>matrix</code>-Objekt und
    ///        einer Zeilennummer
    row_const_proxy(matrix_type const& matrix, size_type const& row):
        detail::line_const_proxy< matrix_type >(matrix, row){}

    /// \brief Lesezugriff auf das Element <code>column_number</code>
    ///
    /// siehe <code>column()</code>
    value_type const&
    operator[](size_type const& column_number)const{ return column(column_number); }
    /// \brief Lesezugriff auf das Element number
    value_type const& column(size_type const& number)const{ return *(begin() + number); }

    /// \brief Anzahl der Spalten
    size_type const columns()const{ return this->matrix_const().columns(); }
    /// \brief Anzahl der Spalten
    ///
    /// siehe <code>columns()</code>
    size_type const size()const{ return columns(); }

    /// \brief Zeile die das Objekt repräsentiert
    size_type const pos()const{ return detail::line_const_proxy< matrix_type >::pos(); }

    /// \brief Leseiterator auf das erste Element
    const_iterator const begin()const{ return cbegin(); }

    /// \brief Leseiterator hinter das letzte Element
    const_iterator const end()const{ return cend(); }

    /// \brief Leseiterator auf das erste Element
    const_iterator const cbegin()const{
        return detail::create_begin_row_iterator< matrix_type const, const_iterator >(
            this->matrix_const(), pos()
        );
    }

    /// \brief Leseiterator hinter das letzte Element
    const_iterator const cend()const{
        return detail::create_end_row_iterator< matrix_type const, const_iterator >(
            this->matrix_const(), pos()
        );
    }
};

/// \brief Proxyklasse die eine Spalte eines konstanten <code>matrix</code>-Objekts repräsentiert
template < typename Matrix >
class column_const_proxy: private detail::line_const_proxy< Matrix >{
public:
    /// \brief Die Klasseninstanz des Klassentemplats <code>matrix</code> zu der der Proxy gehört
    typedef Matrix                                       matrix_type;
    /// \brief Typ der Elemente der Matrix
    typedef typename Matrix::value_type                  value_type;
    /// \brief Iteratoren für Lesezugriff
    typedef detail::column_const_iterator< matrix_type > const_iterator;
    /// \brief Iteratoren für Lesezugriff
    typedef const_iterator                               iterator;
    /// \brief Typ der Dimensionselemente der Matrix
    typedef typename Matrix::size_type                   size_type;
    /// \brief <code>difference_type</code> der Iteratoren
    typedef typename const_iterator::difference_type     difference_type;

    /// \brief Objekterzeugung aus einem Verweis auf ein konstantes <code>matrix</code>-Objekt und
    ///        einer Spaltennummer
    column_const_proxy(matrix_type const& matrix, size_type const& column):
        detail::line_const_proxy< matrix_type >(matrix, column){}

    /// \brief Lesezugriff auf das Element <code>row_number</code>
    ///
    /// siehe <code>row()</code>
    value_type const& operator[](size_type const& row_number)const{ return row(row_number); }
    /// \brief Lesezugriff auf das Element number
    value_type const& row(size_type const& number)const{ return *(begin() + number); }

    /// \brief Anzahl der Zeilen
    size_type const rows()const{ return this->matrix_const().rows(); }
    /// \brief Anzahl der Zeilen
    ///
    /// siehe <code>rows()</code>
    size_type const size()const{ return rows(); }

    /// \brief Spalte die das Objekt repräsentiert
    size_type const pos()const{ return detail::line_const_proxy< matrix_type >::pos(); }

    /// \brief Leseiterator auf das erste Element
    const_iterator const begin()const{ return cbegin(); }

    /// \brief Leseiterator hinter das letzte Element
    const_iterator const end()const{ return cend(); }

    /// \brief Leseiterator auf das erste Element
    const_iterator const cbegin()const{
        return detail::create_begin_column_iterator< matrix_type const, const_iterator >(
            this->matrix_const(), pos()
        );
    }

    /// \brief Leseiterator hinter das letzte Element
    const_iterator const cend()const{
        return detail::create_end_column_iterator< matrix_type const, const_iterator >(
            this->matrix_const(), pos()
        );
    }
};


/// \brief Proxyklasse die eine Zeile eines <code>matrix</code>-Objekts repräsentiert
template < typename Matrix >
class row_proxy: private detail::line_proxy< Matrix >{
public:
    /// \brief Die Klasseninstanz des Klassentemplats <code>matrix</code> zu der der Proxy gehört
    typedef Matrix                                                   matrix_type;
    /// \brief Typ der Elemente der Matrix
    typedef typename Matrix::value_type                              value_type;
    /// \brief Iteratoren für Lesezugriff
    typedef typename row_const_proxy< matrix_type >::const_iterator  const_iterator;
    /// \brief Iteratoren für Schreibzugriff
    typedef detail::row_iterator< matrix_type >                      iterator;
    /// \brief Typ der Dimensionselemente der Matrix
    typedef typename Matrix::size_type                               size_type;
    /// \brief <code>difference_type</code> der Iteratoren
    typedef typename row_const_proxy< matrix_type >::difference_type difference_type;

    /// \brief Objekterzeugung aus einem Verweis auf ein <code>matrix</code>-Objekt und einer
    ///        Zeilennummer
    row_proxy(matrix_type& matrix, size_type const& row):
        detail::line_proxy< matrix_type >(matrix, row){}

    /// \brief Schreibzugriff auf das Element <code>column_number</code>
    ///
    /// siehe <code>column()</code>
    value_type& operator[](size_type const& column_number)const{ return column(column_number); }
    /// \brief Schreibzugriff auf das Element <code>number</code>
    value_type& column(size_type const& number)const{ return *(begin() + number); }

    /// \brief Anzahl der Spalten
    size_type const columns()const{ return this->matrix_const().columns(); }
    /// \brief Anzahl der Spalten
    ///
    /// siehe <code>columns()</code>
    size_type const size()const{ return columns(); }

    /// \brief Zeile die das Objekt repräsentiert
    size_type const pos()const{ return detail::line_proxy< matrix_type >::pos(); }


    /// \brief Schreibiterator auf das erste Element
    iterator const begin()const{
        return detail::create_begin_row_iterator< matrix_type, iterator >(this->matrix(), pos());
    }

    /// \brief Schreibiterator hinter das letzte Element
    iterator const end()const{
        return detail::create_end_row_iterator< matrix_type, iterator >(this->matrix(), pos());
    }

    /// \brief Leseiterator auf das erste Element
    const_iterator const cbegin()const{
        return detail::create_begin_row_iterator< matrix_type const, const_iterator >(
            this->matrix_const(), pos()
        );
    }

    /// \brief Leseiterator hinter das letzte Element
    const_iterator const cend()const{
        return detail::create_end_row_iterator< matrix_type const, const_iterator >(
            this->matrix_const(), pos()
        );
    }

    /// \brief Typumwandlung in <code>row_const_proxy< matrix_type ></code>
    operator row_const_proxy< matrix_type >()const{
        return row_const_proxy< matrix_type >(this->matrix_const(), this->pos());
    }
};

/// \brief Proxyklasse die eine Spalte eines konstanten <code>matrix</code>-Objekts repräsentiert
template < typename Matrix >
class column_proxy: private detail::line_proxy< Matrix >{
public:
    /// \brief Die Klasseninstanz des Klassentemplats <code>matrix</code> zu der der Proxy gehört
    typedef Matrix                                                      matrix_type;
    /// \brief Typ der Elemente der Matrix
    typedef typename Matrix::value_type                                 value_type;
    /// \brief Iteratoren für Lesezugriff
    typedef typename column_const_proxy< matrix_type >::const_iterator  const_iterator;
    /// \brief Iteratoren für Schreibzugriff
    typedef detail::column_iterator< matrix_type >                      iterator;
    /// \brief Typ der Dimensionselemente der Matrix
    typedef typename Matrix::size_type                                  size_type;
    /// \brief <code>difference_type</code> der Iteratoren
    typedef typename column_const_proxy< matrix_type >::difference_type difference_type;

    /// \brief Objekterzeugung aus einem Verweis auf ein <code>matrix</code>-Objekt und einer
    ///        Spaltennummer
    column_proxy(matrix_type& matrix, size_type const& column):
        detail::line_proxy< matrix_type >(matrix, column){}

    /// \brief Schreibzugriff auf das Element <code>row_number</code>
    ///
    /// siehe <code>row()</code>
    value_type& operator[](size_type const& row_number)const{ return row(row_number); }
    /// \brief Schreibzugriff auf das Element number
    value_type& row(size_type const& number)const{ return *(begin() + number); }

    /// \brief Anzahl der Zeilen
    size_type const rows()const{ return this->matrix_const().rows(); }
    /// \brief Anzahl der Zeilen
    ///
    /// siehe <code>rows()</code>
    size_type const size()const{ return rows(); }

    /// \brief Spalte die das Objekt repräsentiert
    size_type const pos()const{ return detail::line_proxy< matrix_type >::pos(); }

    /// \brief Schreibiterator auf das erste Element
    iterator const begin()const{
        return detail::create_begin_column_iterator< matrix_type, iterator >(
            this->matrix(), pos()
        );
    }

    /// \brief Schreibiterator hinter das letzte Element
    iterator const end()const{
        return detail::create_end_column_iterator< matrix_type, iterator >(this->matrix(), pos());
    }

    /// \brief Leseiterator auf das erste Element
    const_iterator const cbegin()const{
        return detail::create_begin_column_iterator< matrix_type const, const_iterator >(
            this->matrix_const(), pos()
        );
    }

    /// \brief Leseiterator hinter das letzte Element
    const_iterator const cend()const{
        return detail::create_end_column_iterator< matrix_type const, const_iterator >(
            this->matrix_const(), pos()
        );
    }

    /// \brief Typumwandlung in <code>column_const_proxy< matrix_type ></code>
    operator column_const_proxy< matrix_type >()const{
        return column_const_proxy< matrix_type >(this->matrix_const(), this->pos());
    }
};



namespace detail{

/// \brief Helferfunktion für <code>element_swap()</code>
template < typename Proxy >
inline
void
element_swap_template(
    Proxy const& lhs,
    Proxy const& rhs
){
    typedef typename Proxy::iterator iterator;
    using std::swap;

    // kompatibilität prüfen
    if(lhs.size() != rhs.size()){
        throw error::size_unequal("mitrax::element_swap_template<>()", lhs.size(), rhs.size());
    }

    // Elementweise tauschen
    for(
        iterator i = lhs.begin(), j = rhs.begin();
        i != lhs.end();
        ++i, ++j
    ){
        swap(*i, *j);
    }
}

}

/// \brief Vertauscht Elemente zweier Zeilen
template < typename Matrix >
inline
void
element_swap(row_proxy< Matrix > const& lhs, row_proxy< Matrix > const& rhs){
    detail::element_swap_template(lhs, rhs);
}

/// \brief Vertauscht Elemente zweier Spalten
template < typename Matrix >
inline
void
element_swap(column_proxy< Matrix > const& lhs, column_proxy< Matrix > const& rhs){
    detail::element_swap_template(lhs, rhs);
}


}

#endif