Próbowałem rozwiązać problem z możliwością iteracji kilku różnych tablic tekstowych, z których wszystkie są przechowywane w dużej bazie danych rezydentnej pamięci struct.
Poniższe informacje opracowano przy użyciu Visual Studio 2017 Community Edition w aplikacji testowej MFC. Podaję to jako przykład, ponieważ ten post był jednym z kilku, które natknąłem się, które pod warunkiem, że pomoc była jeszcze niewystarczająca na moje potrzeby.
Te structzawierające dane rezydentami pamięci wyglądał mniej więcej tak. Usunąłem większość elementów ze względu na zwięzłość i nie uwzględniłem również używanych definicji Preprocesora (używany zestaw SDK jest przeznaczony zarówno dla C, jak i C ++ i jest stary).
Chciałem mieć iteratory dla różnych WCHARdwuwymiarowych tablic zawierających ciągi tekstowe dla mnemoników.
typedef struct tagUNINTRAM {
// stuff deleted ...
WCHAR ParaTransMnemo[MAX_TRANSM_NO][PARA_TRANSMNEMO_LEN]; /* prog #20 */
WCHAR ParaLeadThru[MAX_LEAD_NO][PARA_LEADTHRU_LEN]; /* prog #21 */
WCHAR ParaReportName[MAX_REPO_NO][PARA_REPORTNAME_LEN]; /* prog #22 */
WCHAR ParaSpeMnemo[MAX_SPEM_NO][PARA_SPEMNEMO_LEN]; /* prog #23 */
WCHAR ParaPCIF[MAX_PCIF_SIZE]; /* prog #39 */
WCHAR ParaAdjMnemo[MAX_ADJM_NO][PARA_ADJMNEMO_LEN]; /* prog #46 */
WCHAR ParaPrtModi[MAX_PRTMODI_NO][PARA_PRTMODI_LEN]; /* prog #47 */
WCHAR ParaMajorDEPT[MAX_MDEPT_NO][PARA_MAJORDEPT_LEN]; /* prog #48 */
// ... stuff deleted
} UNINIRAM;
Obecne podejście polega na użyciu szablonu do zdefiniowania klasy proxy dla każdej z tablic, a następnie posiadania pojedynczej klasy iteratora, której można użyć do iteracji po określonej tablicy za pomocą obiektu proxy reprezentującego tablicę.
Kopia danych rezydentnych pamięci jest przechowywana w obiekcie, który obsługuje odczytywanie i zapisywanie danych rezydentnych pamięci z / na dysk. Ta klasa,CFilePara przedstawiona na matrycy klasy proxy ( MnemonicIteratorDimSizei klasy sub, z którego ona pochodzi, MnemonicIteratorDimSizeBase) oraz klasę iteracyjnej, MnemonicIterator.
Utworzony obiekt proxy jest dołączony do obiektu iteratora, który uzyskuje dostęp do niezbędnych informacji poprzez interfejs opisany przez klasę podstawową, z której pochodzą wszystkie klasy proxy. W rezultacie powstaje jeden typ klasy iteratora, który może być używany z kilkoma różnymi klasami proxy, ponieważ wszystkie różne klasy proxy udostępniają ten sam interfejs, interfejs podstawowej klasy proxy.
Pierwszą rzeczą było stworzenie zestawu identyfikatorów, które byłyby dostarczane do fabryki klas w celu wygenerowania konkretnego obiektu proxy dla tego typu mnemonika. Te identyfikatory są używane jako część interfejsu użytkownika w celu identyfikacji konkretnych danych udostępniania, które użytkownik jest zainteresowany przeglądaniem i ewentualną modyfikacją.
const static DWORD_PTR dwId_TransactionMnemonic = 1;
const static DWORD_PTR dwId_ReportMnemonic = 2;
const static DWORD_PTR dwId_SpecialMnemonic = 3;
const static DWORD_PTR dwId_LeadThroughMnemonic = 4;
Klasa proxy
Templowana klasa proxy i jej klasa podstawowa są następujące. Musiałem pomieścić kilka różnych rodzajów wchar_ttablic tekstowych. Dwuwymiarowe tablice miały różną liczbę mnemoników, w zależności od typu (celu) mnemonika, a różne typy mnemoniki miały różne maksymalne długości, wahające się od pięciu znaków tekstowych do dwudziestu znaków tekstowych. Szablony dla pochodnej klasy proxy były w naturalny sposób dopasowane do szablonu wymagającego maksymalnej liczby znaków w każdym mnemoniku. Po utworzeniu obiektu proxy używamy SetRange()metody do określenia rzeczywistej tablicy mnemonicznej i jej zakresu.
// proxy object which represents a particular subsection of the
// memory resident database each of which is an array of wchar_t
// text arrays though the number of array elements may vary.
class MnemonicIteratorDimSizeBase
{
DWORD_PTR m_Type;
public:
MnemonicIteratorDimSizeBase(DWORD_PTR x) { }
virtual ~MnemonicIteratorDimSizeBase() { }
virtual wchar_t *begin() = 0;
virtual wchar_t *end() = 0;
virtual wchar_t *get(int i) = 0;
virtual int ItemSize() = 0;
virtual int ItemCount() = 0;
virtual DWORD_PTR ItemType() { return m_Type; }
};
template <size_t sDimSize>
class MnemonicIteratorDimSize : public MnemonicIteratorDimSizeBase
{
wchar_t (*m_begin)[sDimSize];
wchar_t (*m_end)[sDimSize];
public:
MnemonicIteratorDimSize(DWORD_PTR x) : MnemonicIteratorDimSizeBase(x), m_begin(0), m_end(0) { }
virtual ~MnemonicIteratorDimSize() { }
virtual wchar_t *begin() { return m_begin[0]; }
virtual wchar_t *end() { return m_end[0]; }
virtual wchar_t *get(int i) { return m_begin[i]; }
virtual int ItemSize() { return sDimSize; }
virtual int ItemCount() { return m_end - m_begin; }
void SetRange(wchar_t (*begin)[sDimSize], wchar_t (*end)[sDimSize]) {
m_begin = begin; m_end = end;
}
};
Klasa Iterator
Sama klasa iteratora wygląda następująco. Ta klasa zapewnia tylko podstawową funkcjonalność iteratora do przodu, która jest wszystkim, co jest potrzebne w tym momencie. Jednak oczekuję, że to się zmieni lub rozszerzy, gdy będę potrzebować czegoś dodatkowego.
class MnemonicIterator
{
private:
MnemonicIteratorDimSizeBase *m_p; // we do not own this pointer. we just use it to access current item.
int m_index; // zero based index of item.
wchar_t *m_item; // value to be returned.
public:
MnemonicIterator(MnemonicIteratorDimSizeBase *p) : m_p(p) { }
~MnemonicIterator() { }
// a ranged for needs begin() and end() to determine the range.
// the range is up to but not including what end() returns.
MnemonicIterator & begin() { m_item = m_p->get(m_index = 0); return *this; } // begining of range of values for ranged for. first item
MnemonicIterator & end() { m_item = m_p->get(m_index = m_p->ItemCount()); return *this; } // end of range of values for ranged for. item after last item.
MnemonicIterator & operator ++ () { m_item = m_p->get(++m_index); return *this; } // prefix increment, ++p
MnemonicIterator & operator ++ (int i) { m_item = m_p->get(m_index++); return *this; } // postfix increment, p++
bool operator != (MnemonicIterator &p) { return **this != *p; } // minimum logical operator is not equal to
wchar_t * operator *() const { return m_item; } // dereference iterator to get what is pointed to
};
Fabryka obiektów proxy określa, który obiekt ma zostać utworzony na podstawie identyfikatora mnemonicznego. Obiekt proxy jest tworzony, a zwracany wskaźnik jest standardowym typem klasy bazowej, aby mieć jednolity interfejs niezależnie od tego, które z różnych sekcji mnemonicznych są dostępne. SetRange()Sposób stosuje się w celu wskazania obiektu proxy poszczególnych elementów tablicy proxy reprezentuje oraz szereg elementów macierzy.
CFilePara::MnemonicIteratorDimSizeBase * CFilePara::MakeIterator(DWORD_PTR x)
{
CFilePara::MnemonicIteratorDimSizeBase *mi = nullptr;
switch (x) {
case dwId_TransactionMnemonic:
{
CFilePara::MnemonicIteratorDimSize<PARA_TRANSMNEMO_LEN> *mk = new CFilePara::MnemonicIteratorDimSize<PARA_TRANSMNEMO_LEN>(x);
mk->SetRange(&m_Para.ParaTransMnemo[0], &m_Para.ParaTransMnemo[MAX_TRANSM_NO]);
mi = mk;
}
break;
case dwId_ReportMnemonic:
{
CFilePara::MnemonicIteratorDimSize<PARA_REPORTNAME_LEN> *mk = new CFilePara::MnemonicIteratorDimSize<PARA_REPORTNAME_LEN>(x);
mk->SetRange(&m_Para.ParaReportName[0], &m_Para.ParaReportName[MAX_REPO_NO]);
mi = mk;
}
break;
case dwId_SpecialMnemonic:
{
CFilePara::MnemonicIteratorDimSize<PARA_SPEMNEMO_LEN> *mk = new CFilePara::MnemonicIteratorDimSize<PARA_SPEMNEMO_LEN>(x);
mk->SetRange(&m_Para.ParaSpeMnemo[0], &m_Para.ParaSpeMnemo[MAX_SPEM_NO]);
mi = mk;
}
break;
case dwId_LeadThroughMnemonic:
{
CFilePara::MnemonicIteratorDimSize<PARA_LEADTHRU_LEN> *mk = new CFilePara::MnemonicIteratorDimSize<PARA_LEADTHRU_LEN>(x);
mk->SetRange(&m_Para.ParaLeadThru[0], &m_Para.ParaLeadThru[MAX_LEAD_NO]);
mi = mk;
}
break;
}
return mi;
}
Korzystanie z klasy proxy i iteratora
Klasa proxy i jej iterator są używane, jak pokazano w poniższej pętli, w celu wypełnienia CListCtrlobiektu listą mnemoników. Używam std::unique_ptrtak, że gdy klasa proxy nie jest już potrzebna, a std::unique_ptrpoza zasięgiem, pamięć zostanie wyczyszczona.
Tym kodem źródłowym jest utworzenie obiektu proxy dla tablicy w obrębie structodpowiadającego podanemu identyfikatorowi mnemonicznemu. Następnie tworzy iterator dla tego obiektu, używa zakresu fordo wypełnienia CListCtrlformantu, a następnie czyści. Są to nieprzetworzone wchar_tciągi tekstowe, które mogą być dokładnie liczbą elementów tablicy, więc kopiujemy ciąg do tymczasowego bufora, aby upewnić się, że tekst jest zerowany.
std::unique_ptr<CFilePara::MnemonicIteratorDimSizeBase> pObj(pFile->MakeIterator(m_IteratorType));
CFilePara::MnemonicIterator pIter(pObj.get()); // provide the raw pointer to the iterator who doesn't own it.
int i = 0; // CListCtrl index for zero based position to insert mnemonic.
for (auto x : pIter)
{
WCHAR szText[32] = { 0 }; // Temporary buffer.
wcsncpy_s(szText, 32, x, pObj->ItemSize());
m_mnemonicList.InsertItem(i, szText); i++;
}