Domanda

Sto scrivendo una libreria che utilizza modelli di espressione con CRTP. I file di origine possono essere trovati qui: https://github.com/msPraggs / Pyqcd / albero / master / lib / include / base

I modelli di espressione si basano sull'esempio indicato nell'articolo di Wikipedia sull'argomento. Elenco il codice qui nel caso in cui l'articolo Wiki cambia in futuro:

#include <vector>
#include <cassert>

template <typename E>
// A CRTP base class for Vecs with a size and indexing:
class VecExpression {
public:
  typedef std::vector<double>         container_type;
  typedef container_type::size_type   size_type;
  typedef container_type::value_type  value_type;
  typedef container_type::reference   reference;

  size_type  size()                  const { return static_cast<E const&>(*this).size(); }
  value_type operator[](size_type i) const { return static_cast<E const&>(*this)[i];     }

  operator E&()             { return static_cast<      E&>(*this); }
  operator E const&() const { return static_cast<const E&>(*this); }
};

// The actual Vec class:
class Vec : public VecExpression<Vec> {
  container_type _data;
public:
  reference  operator[](size_type i)       { return _data[i]; }
  value_type operator[](size_type i) const { return _data[i]; }
  size_type  size()                  const { return _data.size(); }

  Vec(size_type n) : _data(n) {} // Construct a given size:

  // Construct from any VecExpression:
  template <typename E>
  Vec(VecExpression<E> const& vec) {
    E const& v = vec;
    _data.resize(v.size());
    for (size_type i = 0; i != v.size(); ++i) {
      _data[i] = v[i];
    }
  }
};

template <typename E1, typename E2>
class VecDifference : public VecExpression<VecDifference<E1, E2> > {
  E1 const& _u;
  E2 const& _v;
public:
  typedef Vec::size_type size_type;
  typedef Vec::value_type value_type;
  VecDifference(VecExpression<E1> const& u, VecExpression<E2> const& v) : _u(u), _v(v) {
    assert(u.size() == v.size());
  }
  size_type size() const { return _v.size(); }
  value_type operator[](Vec::size_type i) const { return _u[i] - _v[i]; }
};

template <typename E>
class VecScaled : public VecExpression<VecScaled<E> > {
  double _alpha; 
  E const& _v;
public:
  VecScaled(double alpha, VecExpression<E> const& v) : _alpha(alpha), _v(v) {}
  Vec::size_type size() const { return _v.size(); }
  Vec::value_type operator[](Vec::size_type i) const { return _alpha * _v[i]; }
};

// Now we can overload operators:

template <typename E1, typename E2>
VecDifference<E1,E2> const
operator-(VecExpression<E1> const& u, VecExpression<E2> const& v) {
  return VecDifference<E1,E2>(u,v);
}

template <typename E>
VecScaled<E> const
operator*(double alpha, VecExpression<E> const& v) {
  return VecScaled<E>(alpha,v);
}
.

Quello che voglio fare è aggiungere un altro modello di espressione che consente l'assegnazione di parte dell'oggetto modello originale (la classe VEC del codice sopra e la classe Latticebase nel codice che ho collegato). Utilizzo possibile:

Vec myvector(10);
Vec another_vector(5);
myvector.head(5) = another_vector; // Assign first 5 elements on myvector
myvector.head(2) = another_vector.head(2); // EDIT
.

Quindi creerei una nuova funzione VEC :: Testa che sarebbe un ritorno un modello di espressione per una porzione dell'oggetto VEC. Non so come si adatti al quadro che attualmente ho. In particolare ho le seguenti domande / commenti:

    .
  1. Ho visto esempi di ciò che voglio ottenere nei modelli di espressione che non usano CRTP. Cosa guadagno usando CRTP in questo caso? C'è qualche punto? Dovrei fosserlo e seguire gli altri esempi che ho trovato?
  2. Nel quadro corrente, l'assegnazione all'elemento _Data nella classe VEC è gestito da un costruttore di copia nella classe VEC. Questo non funzionerà se voglio utilizzare il modello di espressione restituito da VEC :: Head, dal momento che l'assegnazione avviene all'interno della classe che contiene i dati, non il modello di espressione.
  3. Ho provato a creare un operatore di assegnazione all'interno del modello di nuova espressione, ma che non funzionerà con il codice sopra come tutti i membri del modello di espressione sono referenti const, e quindi l'operatore di assegnazione viene eliminato a tempo di compilazione. Posso semplicemente cambiare i membri per essere valori invece di riferimenti? Questo impatto sulla performance se è necessario un ulteriore spazio di archiviazione? Questo funziona anche (se cambio una copia memorizzata dell'espressione piuttosto che l'espressione stessa)?
  4. Nel complesso sono confuso su come effettuare l'aggiunta di un modello di espressione che può essere utilizzato come lvalue nel codice sopra. Qualsiasi orientamento su questo sarebbe molto apprezzato.

È stato utile?

Soluzione

Prova questo:

#include <vector>
#include <cassert>

template <typename E>
// A CRTP base class for Vecs with a size and indexing:
class VecExpression {
public:
    typedef std::vector<double>         container_type;
    typedef container_type::size_type   size_type;
    typedef container_type::value_type  value_type;
    typedef container_type::reference   reference;

    size_type  size()                  const { return static_cast<E const&>(*this).size(); }
    value_type operator[](size_type i) const { return static_cast<E const&>(*this)[i]; }

    operator E&()             { return static_cast<E&>(*this); }
    operator E const&() const { return static_cast<const E&>(*this); }
};

class VecHead;

// The actual Vec class:
class Vec : public VecExpression<Vec> {
    container_type _data;
public:
    reference  operator[](size_type i)       { return _data[i]; }
    value_type operator[](size_type i) const { return _data[i]; }
    size_type  size()                  const { return _data.size(); }

    Vec(size_type n) : _data(n) {} // Construct a given size:

    // Construct from any VecExpression:
    template <typename E>
    Vec(VecExpression<E> const& vec) {
        E const& v = vec;
        _data.resize(v.size());
        for (size_type i = 0; i != v.size(); ++i) {
            _data[i] = v[i];
        }
    }

    VecHead head(size_type s);
};

class VecHead : public VecExpression< VecHead >
{
    Vec::size_type _s;
    Vec& _e;
public:

    typedef Vec::size_type size_type;
    typedef Vec::value_type value_type;
    VecHead(std::size_t s, Vec& e)
        : _s(s)
        , _e(e)
    {
        assert(_e.size() >= _s);
    }

    size_type size() const { return _s; }
    value_type operator[](Vec::size_type i) const { assert(i < _s);  return _e[i]; }

    VecHead& operator = (const VecHead& rhs)
    {
        return operator=(static_cast<const VecExpression<VecHead>&>(rhs));
    }

    template <typename E>
    VecHead& operator = (const VecExpression<E>& rhs)
    {
        assert(rhs.size() >= _s);
        for (size_type i = 0; i < _s && i < rhs.size(); ++i)
            _e[i] = rhs[i];
        return *this;
    }
};

VecHead Vec::head(size_type s)
{
    VecHead aHead(s, *this);
    return aHead;
}

template <typename E1, typename E2>
class VecDifference : public VecExpression<VecDifference<E1, E2> > {
    E1 const& _u;
    E2 const& _v;
public:
    typedef Vec::size_type size_type;
    typedef Vec::value_type value_type;
    VecDifference(VecExpression<E1> const& u, VecExpression<E2> const& v) : _u(u), _v(v) {
        assert(u.size() == v.size());
    }
    size_type size() const { return _v.size(); }
    value_type operator[](Vec::size_type i) const { return _u[i] - _v[i]; }
};

template <typename E>
class VecScaled : public VecExpression<VecScaled<E> > {
    double _alpha;
    E const& _v;
public:
    VecScaled(double alpha, VecExpression<E> const& v) : _alpha(alpha), _v(v) {}
    Vec::size_type size() const { return _v.size(); }
    Vec::value_type operator[](Vec::size_type i) const { return _alpha * _v[i]; }
};

// Now we can overload operators:

template <typename E1, typename E2>
VecDifference<E1, E2> const
    operator-(VecExpression<E1> const& u, VecExpression<E2> const& v) {
        return VecDifference<E1, E2>(u, v);
}

template <typename E>
VecScaled<E> const
    operator*(double alpha, VecExpression<E> const& v) {
        return VecScaled<E>(alpha, v);
}

int main()
{
    Vec myvector(10);
    Vec another_vector(5);
    for (int i = 0; i < 5; ++i)
        another_vector[i] = i;

    myvector.head(5) = another_vector; // Assign first 5 elements on myvector
    assert(myvector.head(5).size() == 5);
    for (int i = 0; i < 10; ++i)
    {
        assert(myvector[i] == (i < 5 ? static_cast<double>(i) : 0.));
    }

    //! Added test due to comment vec1.head(2) = vec2.head(2) doesn't work.
    Vec vec1(10), vec2(10);
    for (int i = 0; i < 10; ++i)
        vec2[i] = 2 * (vec1[i] = i);

    vec1.head(2) = vec2.head(2);
    for (int i = 0; i < 10; ++i)
    {
        if (i < 2)
        {
            assert(vec1[i] == vec2[i]);
        }
        else
        {
            assert(vec1[i] != vec2[i]);
        }
    }

    return 0;
}
.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top