Я пытался решить проблему возможности перебирать несколько различных текстовых массивов, каждый из которых хранится в большой резидентной базе данных struct
.
Следующее было разработано с использованием Visual Studio 2017 Community Edition в тестовом приложении MFC. Я привожу это в качестве примера, так как эта публикация была одной из нескольких, с которыми я столкнулся, предоставляя некоторую помощь, но все еще не отвечающую моим потребностям.
struct
содержащих резидентных данных Памяти выглядели примерно следующий. Для краткости я удалил большинство элементов и также не включил используемые определения препроцессора (используемый SDK предназначен для C, а также C ++ и является старым).
Мне было интересно иметь итераторы для различных WCHAR
двумерных массивов, которые содержали текстовые строки для мнемоники.
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;
Текущий подход состоит в том, чтобы использовать шаблон для определения прокси-класса для каждого из массивов, а затем иметь один класс итераторов, который можно использовать для итерации по определенному массиву, используя прокси-объект, представляющий массив.
Копия резидентных данных памяти сохраняется в объекте, который обрабатывает чтение и запись резидентных данных памяти с / на диск. Этот класс CFilePara
содержит шаблонный прокси-класс ( MnemonicIteratorDimSize
и подкласс, из которого он получен MnemonicIteratorDimSizeBase
) и класс итератора MnemonicIterator
.
Созданный прокси-объект присоединяется к объекту итератора, который получает доступ к необходимой информации через интерфейс, описываемый базовым классом, из которого получены все прокси-классы. В результате получается один тип класса итератора, который можно использовать с несколькими разными прокси-классами, поскольку все разные прокси-классы предоставляют один и тот же интерфейс - интерфейс базового прокси-класса.
Первым делом нужно было создать набор идентификаторов, которые будут предоставлены фабрике классов для генерации конкретного прокси-объекта для этого типа мнемоники. Эти идентификаторы используются как часть пользовательского интерфейса для идентификации конкретных данных обеспечения, которые пользователь заинтересован в просмотре и, возможно, изменении.
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;
Прокси-класс
Шаблонный прокси-класс и его базовый класс следующие. Мне нужно было разместить несколько различных типов wchar_t
текстовых строковых массивов. Двухмерные массивы имели различное количество мнемоник, в зависимости от типа (цели) мнемоники, и разные типы мнемоник имели различную максимальную длину, варьируясь от пяти текстовых символов до двадцати текстовых символов. Шаблоны для производного прокси-класса естественно соответствовали шаблону, требующему максимального количества символов в каждой мнемонике. После создания прокси-объекта мы используем SetRange()
метод для указания фактического мнемонического массива и его диапазона.
// 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;
}
};
Класс Iterator
Сам класс итератора выглядит следующим образом. Этот класс предоставляет только основные функциональные возможности прямого итератора, и это все, что нужно в данный момент. Однако я ожидаю, что это изменится или будет расширено, когда мне понадобится что-то дополнительное.
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
};
Фабрика прокси-объектов определяет, какой объект должен быть создан на основе мнемонического идентификатора. Прокси-объект создается, и возвращаемый указатель является стандартным типом базового класса, чтобы иметь единый интерфейс независимо от того, к какому из различных мнемонических разделов обращаются. Этот SetRange()
метод используется для указания прокси-объекту конкретных элементов массива, которые представляет прокси-сервер, и диапазона элементов массива.
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;
}
Использование прокси-класса и итератора
Прокси-класс и его итератор используются, как показано в следующем цикле, для заполнения CListCtrl
объекта списком мнемоник. Я использую std::unique_ptr
так, что когда прокси-класс мне больше не нужен и std::unique_ptr
выходит из области видимости, память будет очищена.
Этот исходный код создает прокси-объект для массива, struct
который соответствует указанному мнемоническому идентификатору. Затем он создает итератор для этого объекта, использует ранжирование for
для заполнения элемента CListCtrl
управления, а затем очищает. Это все необработанные wchar_t
текстовые строки, которые могут точно соответствовать количеству элементов массива, поэтому мы копируем строку во временный буфер, чтобы гарантировать, что текст заканчивается нулем.
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++;
}