Estaba tratando de resolver el problema de poder iterar sobre varios arreglos de texto diferentes, todos los cuales están almacenados en una base de datos residente de memoria que es grande struct
.
Lo siguiente se resolvió utilizando Visual Studio 2017 Community Edition en una aplicación de prueba MFC. Incluyo esto como un ejemplo, ya que esta publicación fue una de varias que encontré y que me brindó ayuda, pero aún no era suficiente para mis necesidades.
El struct
contenido de los datos residentes de la memoria se parecía a lo siguiente. He eliminado la mayoría de los elementos en aras de la brevedad y tampoco he incluido el preprocesador que se utiliza (el SDK en uso es para C y C ++ y es antiguo).
Lo que me interesaba era tener iteradores para las diversas WCHAR
matrices bidimensionales que contenían cadenas de texto para mnemotécnicos.
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;
El enfoque actual es usar una plantilla para definir una clase de proxy para cada una de las matrices y luego tener una sola clase de iterador que se pueda usar para iterar sobre una matriz particular mediante el uso de un objeto proxy que represente la matriz.
Una copia de los datos residentes en la memoria se almacena en un objeto que maneja la lectura y escritura de los datos residentes en la memoria desde / hacia el disco. Esta clase, CFilePara
contiene la clase de proxy con plantilla ( MnemonicIteratorDimSize
y la clase sub partir de la cual es que se deriva, MnemonicIteratorDimSizeBase
) y la clase de iterador, MnemonicIterator
.
El objeto proxy creado se adjunta a un objeto iterador que accede a la información necesaria a través de una interfaz descrita por una clase base de la que se derivan todas las clases proxy. El resultado es tener un solo tipo de clase de iterador que se puede usar con varias clases de proxy diferentes porque las diferentes clases de proxy exponen la misma interfaz, la interfaz de la clase base de proxy.
Lo primero era crear un conjunto de identificadores que se proporcionarían a una fábrica de clases para generar el objeto proxy específico para ese tipo de mnemotécnico. Estos identificadores se utilizan como parte de la interfaz de usuario para identificar los datos de aprovisionamiento particulares que el usuario está interesado en ver y posiblemente modificar.
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 clase de proxy
La clase proxy con plantilla y su clase base son las siguientes. Necesitaba acomodar varios tipos diferentes de wchar_t
matrices de cadenas de texto. Las matrices bidimensionales tenían diferentes números de mnemónicos, dependiendo del tipo (propósito) del mnemónico y los diferentes tipos de mnemónicos tenían diferentes longitudes máximas, variando entre cinco caracteres de texto y veinte caracteres de texto. Las plantillas para la clase proxy derivada encajaban naturalmente con la plantilla que requería la cantidad máxima de caracteres en cada mnemónica. Después de crear el objeto proxy, usamos el SetRange()
método para especificar la matriz mnemónica real y su rango.
// 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 clase de iterador
La clase iteradora en sí es la siguiente. Esta clase proporciona la funcionalidad básica de iterador directo, que es todo lo que se necesita en este momento. Sin embargo, espero que esto cambie o se extienda cuando necesite algo adicional.
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 fábrica de objetos proxy determina qué objeto crear en función del identificador mnemónico. El objeto proxy se crea y el puntero devuelto es el tipo de clase base estándar para tener una interfaz uniforme independientemente de a cuál de las diferentes secciones mnemotécnicas se accede. El SetRange()
método se utiliza para especificar al objeto proxy los elementos de matriz específicos que representa el proxy y el rango de los elementos de la matriz.
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;
}
Uso de la clase de proxy y el iterador
La clase proxy y su iterador se usan como se muestra en el siguiente ciclo para completar un CListCtrl
objeto con una lista de mnemotécnicos. Estoy usando std::unique_ptr
para que cuando la clase proxy ya no la necesite y std::unique_ptr
quede fuera de alcance, la memoria se limpie.
Lo que hace este código fuente es crear un objeto proxy para la matriz dentro del struct
cual corresponde al identificador mnemotécnico especificado. Luego crea un iterador para ese objeto, usa un rango for
para completar el CListCtrl
control y luego limpia. Estas son todas wchar_t
cadenas de texto sin procesar que pueden ser exactamente el número de elementos de la matriz, por lo que copiamos la cadena en un búfer temporal para asegurarnos de que el texto esté terminado en cero.
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++;
}