Ich habe versucht, das Problem zu lösen, dass mehrere verschiedene Textarrays durchlaufen werden können, die alle in einer großen speicherresidenten Datenbank gespeichert sind struct
.
Das Folgende wurde mit Visual Studio 2017 Community Edition in einer MFC-Testanwendung erarbeitet. Ich füge dies als Beispiel hinzu, da dieses Posting eines von mehreren war, auf die ich gestoßen bin und die Hilfe lieferten, die aber für meine Bedürfnisse immer noch nicht ausreichten.
Die struct
Daten, die die speicherresidenten Daten enthielten, sahen ungefähr so aus. Ich habe die meisten Elemente der Kürze halber entfernt und auch die verwendeten Präprozessordefinitionen nicht berücksichtigt (das verwendete SDK ist sowohl für C als auch für C ++ und alt).
Ich war daran interessiert, Iteratoren für die verschiedenen WCHAR
zweidimensionalen Arrays zu haben, die Textzeichenfolgen für die Mnemonik enthielten.
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;
Der aktuelle Ansatz besteht darin, eine Vorlage zu verwenden, um eine Proxy-Klasse für jedes der Arrays zu definieren und dann eine einzelne Iterator-Klasse zu haben, die zum Iterieren über ein bestimmtes Array verwendet werden kann, indem ein Proxy-Objekt verwendet wird, das das Array darstellt.
Eine Kopie der speicherresidenten Daten wird in einem Objekt gespeichert, das das Lesen und Schreiben der speicherresidenten Daten von / auf die Festplatte übernimmt. Diese Klasse CFilePara
enthält die Proxy-Klasse mit Vorlagen ( MnemonicIteratorDimSize
und die Unterklasse, von der sie abgeleitet ist MnemonicIteratorDimSizeBase
) und die Iterator-Klasse MnemonicIterator
.
Das erstellte Proxy-Objekt wird an ein Iterator-Objekt angehängt, das über eine von einer Basisklasse beschriebene Schnittstelle, von der alle Proxy-Klassen abgeleitet sind, auf die erforderlichen Informationen zugreift. Das Ergebnis ist ein einzelner Typ von Iteratorklasse, der mit mehreren verschiedenen Proxy-Klassen verwendet werden kann, da die verschiedenen Proxy-Klassen alle dieselbe Schnittstelle, die Schnittstelle der Proxy-Basisklasse, verfügbar machen.
Das erste war, eine Reihe von Bezeichnern zu erstellen, die einer Klassenfactory zur Verfügung gestellt wurden, um das spezifische Proxy-Objekt für diesen Mnemoniktyp zu generieren. Diese Kennungen werden als Teil der Benutzeroberfläche verwendet, um die bestimmten Bereitstellungsdaten zu identifizieren, die der Benutzer sehen und möglicherweise ändern möchte.
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;
Die Proxy-Klasse
Die Proxy-Klasse mit Vorlagen und ihre Basisklasse lauten wie folgt. Ich musste verschiedene Arten von wchar_t
Text-String-Arrays unterbringen . Die zweidimensionalen Arrays hatten je nach Art (Zweck) der Mnemonik unterschiedliche Anzahlen von Mnemoniken, und die verschiedenen Arten von Mnemoniken hatten unterschiedliche maximale Längen und variierten zwischen fünf Textzeichen und zwanzig Textzeichen. Vorlagen für die abgeleitete Proxy-Klasse passten natürlich zu der Vorlage, die die maximale Anzahl von Zeichen in jeder Mnemonik erfordert. Nachdem das Proxy-Objekt erstellt wurde, verwenden wir die SetRange()
Methode, um das tatsächliche Mnemonik-Array und seinen Bereich anzugeben.
// 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;
}
};
Die Iterator-Klasse
Die Iteratorklasse selbst lautet wie folgt. Diese Klasse bietet nur grundlegende Forward-Iterator-Funktionen, die derzeit nur benötigt werden. Ich gehe jedoch davon aus, dass sich dies ändern oder erweitern wird, wenn ich etwas Zusätzliches benötige.
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
};
Die Proxy-Objekt-Factory bestimmt anhand der Mnemonik-ID, welches Objekt erstellt werden soll. Das Proxy-Objekt wird erstellt und der zurückgegebene Zeiger ist der Standard-Basisklassentyp, um eine einheitliche Schnittstelle zu erhalten, unabhängig davon, auf welche der verschiedenen Mnemonikabschnitte zugegriffen wird. Die SetRange()
Methode wird verwendet, um dem Proxy-Objekt die spezifischen Array-Elemente anzugeben, die der Proxy darstellt, und den Bereich der Array-Elemente.
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;
}
Verwenden der Proxy-Klasse und des Iterators
Die Proxy-Klasse und ihr Iterator werden wie in der folgenden Schleife gezeigt verwendet, um ein CListCtrl
Objekt mit einer Liste von Mnemonics zu füllen . Ich benutze es, std::unique_ptr
damit std::unique_ptr
der Speicher bereinigt wird , wenn die Proxy-Klasse, die ich nicht mehr benötige und die nicht mehr funktioniert.
Mit diesem Quellcode wird ein Proxy-Objekt für das Array erstellt, das struct
dem angegebenen Mnemonik-Bezeichner entspricht. Anschließend wird ein Iterator für dieses Objekt erstellt, ein Bereich for
zum Ausfüllen des CListCtrl
Steuerelements verwendet und anschließend bereinigt. Dies sind alles wchar_t
Rohtextzeichenfolgen, die genau der Anzahl der Array-Elemente entsprechen können. Daher kopieren wir die Zeichenfolge in einen temporären Puffer, um sicherzustellen, dass der Text mit Null abgeschlossen ist.
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++;
}