Eu estava tentando resolver o problema de conseguir iterar em várias matrizes de texto diferentes, todas armazenadas em um banco de dados residente na memória que é grande struct
.
O seguinte foi elaborado usando o Visual Studio 2017 Community Edition em um aplicativo de teste do MFC. Estou incluindo isso como um exemplo, pois essa postagem foi uma das várias que encontrei, fornecendo alguma ajuda, mas ainda assim insuficiente para minhas necessidades.
O struct
contendo os dados residentes da memória era semelhante ao seguinte. Eu removi a maioria dos elementos por questões de brevidade e também não incluí as definições de pré-processador usadas (o SDK em uso é para C e C ++ e é antigo).
O que eu estava interessado em fazer é ter iteradores para as várias WCHAR
matrizes bidimensionais que continham cadeias de texto para mnemônicos.
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;
A abordagem atual é usar um modelo para definir uma classe de proxy para cada uma das matrizes e, em seguida, ter uma única classe de iterador que pode ser usada para iterar sobre uma matriz específica usando um objeto proxy que representa a matriz.
Uma cópia dos dados residentes da memória é armazenada em um objeto que lida com a leitura e gravação dos dados residentes da memória de / para o disco. Esta classe CFilePara
contém a classe de proxy com modelo ( MnemonicIteratorDimSize
e a subclasse da qual é derivada MnemonicIteratorDimSizeBase
) e a classe do iterador MnemonicIterator
,.
O objeto proxy criado é anexado a um objeto iterador que acessa as informações necessárias por meio de uma interface descrita por uma classe base da qual todas as classes proxy são derivadas. O resultado é ter um único tipo de classe de iterador que pode ser usado com várias classes de proxy diferentes, porque as diferentes classes de proxy expõem a mesma interface, a interface da classe base do proxy.
A primeira coisa foi criar um conjunto de identificadores que seriam fornecidos a uma fábrica de classes para gerar o objeto proxy específico para esse tipo de mnemônico. Esses identificadores são usados como parte da interface do usuário para identificar os dados de provisionamento específicos que o usuário está interessado em ver e possivelmente 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;
A Classe Proxy
A classe de proxy modelada e sua classe base são as seguintes. Eu precisava acomodar vários tipos diferentes de wchar_t
matrizes de string de texto. As matrizes bidimensionais tinham números diferentes de mnemônicos, dependendo do tipo (objetivo) do mnemônico e os diferentes tipos de mnemônicos tinham diferentes comprimentos máximos, variando entre cinco caracteres e vinte caracteres. Os modelos para a classe de proxy derivada eram um ajuste natural ao modelo que requer o número máximo de caracteres em cada mnemônico. Após a criação do objeto proxy, usamos o SetRange()
método para especificar a matriz mnemônica real e seu intervalo.
// 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;
}
};
A classe Iterator
A própria classe de iterador é a seguinte. Essa classe fornece apenas a funcionalidade básica do iterador direto, que é tudo o que é necessário no momento. No entanto, espero que isso mude ou seja estendido quando precisar de 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
};
A fábrica de objetos proxy determina qual objeto será criado com base no identificador mnemônico. O objeto proxy é criado e o ponteiro retornado é o tipo de classe base padrão para ter uma interface uniforme, independentemente de quais das diferentes seções mnemônicas estão sendo acessadas. O SetRange()
método é usado para especificar para o objeto proxy os elementos específicos da matriz que o proxy representa e o intervalo dos elementos da 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;
}
Usando a classe de proxy e o iterador
A classe proxy e seu iterador são usados como mostrado no loop a seguir para preencher um CListCtrl
objeto com uma lista de mnemônicos. Estou usando std::unique_ptr
para que, quando a classe proxy não seja mais necessária e a std::unique_ptr
saída do escopo, a memória seja limpa.
O que esse código-fonte faz é criar um objeto proxy para a matriz dentro do struct
que corresponde ao identificador mnemônico especificado. Em seguida, ele cria um iterador para esse objeto, usa um intervalo for
para preencher o CListCtrl
controle e depois limpa. Essas são todas as wchar_t
strings de texto bruto que podem ser exatamente o número de elementos da matriz, por isso copiamos a string em um buffer temporário para garantir que o texto seja zero terminado.
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++;
}