2

私は C++ でいくつかの線形代数ライブラリのラッパー コードを書いています。これらのライブラリは、配列をいくつかの形式のいずれかに格納できます。最も一般的な形式は、密な列優先、密な行優先、圧縮された疎な列、および圧縮された疎な行です。基本ライブラリに必要な基になるメモリ順序を維持しながら、これらの配列の要素に普遍的な順序でアクセスする配列ラッパー クラスを作成したいと思います。つまり、行ベースのインデックスを使用して、ラップされた列優先の密集配列とラップされた行優先の密集配列の両方の要素に同じ方法でアクセスできるようにしたいと考えています。外部ライブラリの機能に干渉せずに基になる配列を並べ替えることはできません。並べ替えには、配列の巨大なサイズを考えると、とにかくかなりの計算コストがかかります。これが私が言いたいことです。

T * data;
// Initialize values of data

rowMajorArray R(data); // Stored row-major, with reordering of data if necessary
columnMajorArray C(data); // Stored column-major, with reordering of data if necessary

wrapperArray wrapperR(R); // DOES NOT reorder data
wrapperArray wrapperC(C); // DOES NOT reorder data

assert(wrapperR[3] == wrapperC[3]); // I want this to be true, i.e. transparent row indexing
assert(wrapperR[3][4] == wrapperC[3][4]); // I want this to be true, i.e. transparent element indexing


T * rowPointerR = wrapperR[0]; // Points to first row; should this be a reference: &(wrapperR[0]) ?
T * rowPointerC = wrapperC[0]; // Points to first row, even though stored column-major

assert( *(rowPointerR + 2) == *(rowPointerC + 2) ) // I want this to be true, i.e. transparent row pointers

T * elementPointerR = &(wrapperR[0][0]); // Points to individual element
T * elementPointerC = &(wrapperC[0][0]); // Points to individual element

assert( *(elementPointerR + 2) == *(elementPointerC + 2) ) // I want this to be true, i.e. transparent pointer arithmetic

基本的に私の目標は、基礎となるライブラリがネイティブに使用するのと同じメモリ順序を使用してラップされた配列にアクセスできるようにすることですが、ラッパー コードは基礎となるメモリの順序を気にせずに透過的にデータにアクセスできるようにすることです。行と要素の両方に正しくアクセスできる限り、必要なことはすべて行うことができるので、ポインターまたはインデックス構文のどちらを使用するかは気にしません。ポインターを直接実装することしかできない場合は、とにかくインデックス演算子をオーバーロードして、他の外部演算子オーバーロード ライブラリと簡単に統合できるようにします。

皆様、ご協力ありがとうございました。

4

2 に答える 2

0

多次元配列と中置演算子を使用しないことをお勧めします。

素朴な出発点は、このような単純なインターフェースです。

class Table {
public:
  virtual ~Table() {}

  virtual double get(size_t row, size_t col) const = 0;
  virtual void getRow(size_t row, std::vector<double> &row_vals) const = 0;
  virtual void getCol(size_t col, std::vector<double> &col_vals) const = 0;

  virtual void set(size_t row, size_t col, double val) = 0;
  virtual void setRow(size_t row, const std::vector<double> &row_vals) = 0;
  virtual void setCol(size_t col, const std::vector<double> &col_vals) = 0;
};

ストレージ戦略ごとにインターフェースを実装します。これにより、行/列メジャーが優れている場所を最適化できます。

于 2012-08-17T21:51:08.700 に答える
0

インデックス演算子をオーバーロードし、その演算子内で正しいオフセットを計算するだけです。ラッパーは、ラップするデータ構造の種類を知る必要があります。これらのタイプのいずれかのインスタンスを作成するときに、いくつかのメンバー変数を設定するのと同じくらい簡単かもしれませm_rowOffsetm_columnOffset

ただし、ポインターの動作については何もできません。ポインターは単純で愚かな生き物であり、目的に合わせて異なる動作をさせることはできません。ポインタに追加1すると、単純に次のオブジェクトに進みます (つまり、n * sizeof(T)バイトを進めます)。とにかくもっと自然です。

于 2012-08-17T21:36:30.437 に答える