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 struct
zawierają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 WCHAR
dwuwymiarowych 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 ( MnemonicIteratorDimSize
i 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_t
tablic 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 CListCtrl
obiektu listą mnemoników. Używam std::unique_ptr
tak, że gdy klasa proxy nie jest już potrzebna, a std::unique_ptr
poza zasięgiem, pamięć zostanie wyczyszczona.
Tym kodem źródłowym jest utworzenie obiektu proxy dla tablicy w obrębie struct
odpowiadającego podanemu identyfikatorowi mnemonicznemu. Następnie tworzy iterator dla tego obiektu, używa zakresu for
do wypełnienia CListCtrl
formantu, a następnie czyści. Są to nieprzetworzone wchar_t
cią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++;
}