Ich habe versucht, das Problem zu lösen, dass ich in der Lage bin, über mehrere verschiedene Textfelder zu iterieren, die alle in einer speicherresidenten Datenbank gespeichert sind, die eine große struct
.
Das Folgende wurde mit Visual Studio 2017 Community Edition auf einer MFC-Testanwendung ausgearbeitet. Ich füge dies als Beispiel ein, da dieses Posting eines von mehreren war, auf die ich gestoßen bin und die mir zwar eine gewisse Hilfe boten, aber für meine Bedürfnisse immer noch unzureichend waren.
En struct
mit den speicherresidenten Daten sah etwa wie folgt aus. Der Kürze halber habe ich die meisten Elemente entfernt und auch die verwendeten Präprozessor-Definitionen nicht aufgenommen (das verwendete SDK ist sowohl für C als auch für C++ und ist alt).
Was ich tun wollte, ist, Iteratoren für die verschiedenen WCHAR
zweidimensionale Arrays, die Textstrings für Mnemonics 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 derzeitige 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 verwendet werden kann, um über ein bestimmtes Array zu iterieren, indem ein Proxy-Objekt verwendet wird, das das Array repräsentiert.
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 Vorlage für die Proxy-Klasse ( 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 auf die erforderlichen Informationen über eine Schnittstelle zugreift, die von einer Basisklasse beschrieben wird, von der alle Proxy-Klassen abgeleitet sind. Das Ergebnis ist ein einziger Typ von Iterator-Klasse, der mit mehreren verschiedenen Proxy-Klassen verwendet werden kann, da die verschiedenen Proxy-Klassen alle dieselbe Schnittstelle, die Schnittstelle der Proxy-Basisklasse, zur Verfügung stellen.
Als erstes wurde eine Reihe von Bezeichnern erstellt, die einer Klassenfabrik zur Verfügung gestellt werden, um das spezifische Proxy-Objekt für diesen Typ von Mnemonik zu erzeugen. Diese Bezeichner werden als Teil der Benutzeroberfläche verwendet, um die speziellen 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 Templated-Proxy-Klasse und ihre Basisklasse sind wie folgt. Ich musste mehrere verschiedene Arten von wchar_t
Text-String-Arrays. Die zweidimensionalen Arrays hatten eine unterschiedliche Anzahl von Mnemonics, je nach Art (Zweck) des Mnemonics, und die verschiedenen Arten von Mnemonics hatten eine unterschiedliche maximale Länge, die zwischen fünf und zwanzig Textzeichen lag. Vorlagen für die abgeleitete Proxy-Klasse waren eine natürliche Ergänzung, da die Vorlage die maximale Anzahl von Zeichen in jedem Mnemonischen erforderte. Nachdem das Proxy-Objekt erstellt wurde, verwenden wir die SetRange()
Methode, um das tatsächliche mnemonische 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 Iterator-Klasse selbst sieht wie folgt aus. Diese Klasse bietet nur grundlegende Vorwärts-Iterator-Funktionen, was im Moment alles ist, was benötigt wird. Ich erwarte jedoch, dass sich dies ändern oder erweitert werden 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-Objektfabrik bestimmt anhand der mnemonischen Kennung, welches Objekt erstellt werden soll. Das Proxy-Objekt wird erstellt und der zurückgegebene Zeiger ist der Standard-Basisklassentyp, um eine einheitliche Schnittstelle zu haben, unabhängig davon, auf welche der verschiedenen mnemonischen Abschnitte zugegriffen wird. Die Website SetRange()
Methode wird verwendet, um dem Proxy-Objekt die spezifischen Array-Elemente, die der Proxy repräsentiert, und den Bereich der Array-Elemente anzugeben.
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;
}
Verwendung der Proxy-Klasse und des Iterators
Die Proxy-Klasse und ihr Iterator werden, wie in der folgenden Schleife gezeigt, zum Ausfüllen einer CListCtrl
Objekt mit einer Liste von Mnemonics. Ich verwende std::unique_ptr
so dass, wenn die Proxyklasse nicht mehr benötigt wird und die std::unique_ptr
aus dem Anwendungsbereich verschwindet, wird der Speicher aufgeräumt.
In diesem Quellcode wird ein Proxy-Objekt für das Array innerhalb der struct
die dem angegebenen mnemonischen Bezeichner entspricht. Anschließend wird ein Iterator für dieses Objekt erstellt, der einen Bereich for
zum Ausfüllen der CListCtrl
Kontrolle und räumt dann auf. Diese sind alle roh wchar_t
Textstrings, die genau die Anzahl der Arrayelemente sein können, so dass wir den String in einen temporären Puffer kopieren, um sicherzustellen, dass der Text nullterminiert 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++;
}