Stavo cercando di risolvere il problema di poter iterare su diversi array di testo, che sono tutti archiviati in un database residente in memoria di grandi dimensioni struct
.
Di seguito è stato elaborato utilizzando Visual Studio 2017 Community Edition su un'applicazione di test MFC. Lo sto includendo come esempio in quanto questo post è stato uno dei tanti che ho incontrato che mi ha fornito un aiuto, ma che era ancora insufficiente per le mie esigenze.
Il struct
contenuto dei dati residenti in memoria era simile al seguente. Ho rimosso la maggior parte degli elementi per brevità e non ho incluso nemmeno il preprocessore definito usato (l'SDK in uso è per C e C ++ ed è vecchio).
Quello che mi interessava fare era avere iteratori per i vari WCHAR
array bidimensionali che contenevano stringhe di testo per la mnemonica.
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;
L'approccio attuale consiste nell'utilizzare un modello per definire una classe proxy per ciascuno degli array e quindi disporre di una singola classe iteratore che può essere utilizzata per iterare su un determinato array utilizzando un oggetto proxy che rappresenta l'array.
Una copia dei dati residenti in memoria è archiviata in un oggetto che gestisce la lettura e la scrittura dei dati residenti in memoria da / su disco. Questa classe, CFilePara
contiene la classe proxy templato ( MnemonicIteratorDimSize
e la classe secondaria da cui si deriva, MnemonicIteratorDimSizeBase
) e la classe iteratore, MnemonicIterator
.
L'oggetto proxy creato è collegato a un oggetto iteratore che accede alle informazioni necessarie tramite un'interfaccia descritta da una classe base da cui derivano tutte le classi proxy. Il risultato è avere un solo tipo di classe iteratore che può essere utilizzato con diverse classi proxy diverse poiché le diverse classi proxy espongono tutte la stessa interfaccia, l'interfaccia della classe base proxy.
La prima cosa era creare un insieme di identificatori che sarebbero stati forniti a una factory di classe per generare l'oggetto proxy specifico per quel tipo di mnemonico. Questi identificatori vengono utilizzati come parte dell'interfaccia utente per identificare i dati di provisioning specifici che l'utente è interessato a vedere e possibilmente modificare.
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;
La classe proxy
La classe proxy basata su modelli e la relativa classe base sono le seguenti. Avevo bisogno di ospitare diversi tipi di wchar_t
array di stringhe di testo. Le matrici bidimensionali avevano numeri diversi di mnemonici, a seconda del tipo (scopo) del mnemonico e i diversi tipi di mnemonici avevano lunghezze massime diverse, che variavano tra cinque caratteri di testo e venti caratteri di testo. I modelli per la classe proxy derivata si adattavano perfettamente al modello che richiedeva il numero massimo di caratteri in ciascun mnemonico. Dopo aver creato l'oggetto proxy, utilizziamo quindi il SetRange()
metodo per specificare l'array mnemonico effettivo e il suo intervallo.
// 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;
}
};
La classe Iterator
La stessa classe iteratore è la seguente. Questa classe fornisce solo le funzionalità di base dell'iteratore avanzato che è tutto ciò che è necessario in questo momento. Tuttavia mi aspetto che questo cambi o venga esteso quando ho bisogno di qualcosa in più.
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
};
La factory di oggetti proxy determina quale oggetto creare in base all'identificatore mnemonico. L'oggetto proxy viene creato e il puntatore restituito è il tipo di classe base standard in modo da avere un'interfaccia uniforme indipendentemente da quale delle diverse sezioni mnemoniche si accede. Il SetRange()
metodo è utilizzato per specificare l'oggetto proxy elementi dell'array specifici proxy rappresenta e la gamma degli elementi dell'array.
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;
}
Utilizzo della classe proxy e dell'iteratore
La classe proxy e il suo iteratore vengono utilizzati come mostrato nel ciclo seguente per compilare un CListCtrl
oggetto con un elenco di mnemonici. Sto usando in std::unique_ptr
modo che quando la classe proxy non è più necessaria e non std::unique_ptr
rientra nell'ambito di applicazione, la memoria verrà ripulita.
Ciò che fa questo codice sorgente è creare un oggetto proxy per l'array all'interno del struct
quale corrisponde all'identificatore mnemonico specificato. Quindi crea un iteratore per quell'oggetto, usa un intervallo for
per riempire il CListCtrl
controllo e quindi pulisce. Queste sono tutte wchar_t
stringhe di testo non elaborate che possono corrispondere esattamente al numero di elementi dell'array, pertanto copiamo la stringa in un buffer temporaneo al fine di garantire che il testo sia terminato con zero.
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++;
}