Я намагався вирішити проблему з можливістю повторення декількох різних текстових масивів, всі вони зберігаються у великій базі даних резидентів пам'яті 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;
}
};
Клас ітераторів
Сам клас ітератора такий. Цей клас забезпечує просто основний функціонал ітератора вперед, який є всім, що потрібно в цей час. Однак я сподіваюся, що це зміниться або продовжиться, коли мені буде потрібно щось додаткове.
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++;
}