Intereting Posts
Возвращает auto_ptr из функций неправильно / подвержен ошибкам? Вызов библиотеки с поддержкой CUDA в новом streamе перемотка вызывает ошибку сегментации Использование classа gr :: fec :: code :: cc_encoder в иерархическом блоке тип посетителя над списком типов в c ++ Генератор синтаксиса C ++ Когда компилятор C ++ 11 сделает RVO и NRVO опережающими семантику перемещения и привязку ссылки ссылки? ISO C ++ запрещает объявление «Stack» без типа Как открыть проект Qt Creator (itk-snap) с помощью Cmake как использовать Microsoft Crypto API с USB Dongle после PKCS # 11 используя boost: asio с выбором? блокировка ввода TCP или обновление файла Неинициализированный конструктор std :: complex при использовании ‘new’ как массивы работают внутри c / c ++ Будет ли исключение в блоке catch исключить два исключения? Является ли экземпляр шаблона classа неполным типом плохо сформированным, если тип определяется впоследствии?

Итерация над строкой; Легко отображать значения полей и значений в блоке RichEdit

Есть ли более простой способ отображения полей struct и их соответствующих значений в элементе управления RichEdit ?

Это то, что я делаю сейчас:

 AnsiString s; s = IntToStr(wfc.fontColor); RichEdit1->Lines->Append(s); 

так далее…

Есть ли более простой способ, чем индивидуальный вызов каждого из них? Я хочу прочитать двоичный файл, а затем отобразить соответствующую структуру в элементе управления RichEdit для небольшой утилиты, которую я создаю, и не нашел другого пути. Я знаю, как читать двоичные файлы и читать значения в struct уже.

Кажется, BOOST_FUSION_ADAPT_STRUCT хорошо подходит. Например:

 // Your existing struct struct Foo { int i; bool j; char k[100]; }; // Generate an adapter allowing to view "Foo" as a Boost.Fusion sequence BOOST_FUSION_ADAPT_STRUCT( Foo, (int, i) (bool, j) (char, k[100]) ) // The action we will call on each member of Foo struct AppendToTextBox { AppendToTextBox(RichEditControl& Ctrl) : m_Ctrl(Ctrl){} template void operator()(T& t)const { m_Ctrl.Lines.Append(boost::lexical_cast(t)); } RichEditControl& m_Ctrl; }; // Usage: void FillTextBox(Foo& F, RichEditControl& Ctrl) { boost::fusion::for_each(F, AppendToTextBox(Ctrl)); } 

Если я правильно понимаю, суть первоначального вопроса заключается в том, как перебирать структуру. Короче говоря, как отметил Джерри Коффин в комментарии, это невозможно. Я попытаюсь объяснить, почему, а затем я попытаюсь объяснить, как сделать следующее лучшее.

Структура хранится в памяти как monoлитная часть данных без каких-либо метаданных, описывающих ее структуру. Например, следующая структура:

 struct Foo { char a; char b; char c; int i; } Foo f = {'x', 'y', 'z', 122}; 

могут быть представлены в памяти с использованием шестнадцатеричной записи следующим образом

 78 79 7A FF 7A 00 00 00 

где первые 3 байта содержат поля char, четвертый – случайное значение, используемое для заполнения, а следующие четыре байта представляют собой малозначное представление целого числа 122. Этот макет будет отличаться от компилятора к компилятору и системе к системе. Короче говоря, двоичное представление не сообщает вам, что такое данные или где хранятся отдельные поля.

Итак, как поля доступа к компилятору в структурах? Код

 char c = fc; 

переводится в инструкцию типа

 COPY BYTE FROM ([address of f] + 2) TO [address of c] 

другими словами, компилятор кодирует литературные смещения полей в код. Опять же, это нам не помогает.

Поэтому мы должны сами аннотировать структуру. Это можно сделать, добавив информацию внутри структуры, чтобы превратить ее в своего рода хранилище значений ключа или путем добавления второй структуры. Вы не хотите изменять исходную структуру, поэтому вторая структура – это путь.

Я предполагаю, что ваша структура содержит только базовые типы: int, char и т. Д. Если вы являетесь сложными другими classами в структуре, я бы предложил добавить метод ToString () в свой базовый class и вызвать этот метод – вот как C # и Java это делает.

 Foo tmp; #define FIELD_OFFSET(f) ((char*)&(tmp.f) - (char*)&tmp) enum FieldType { INT_FIELD, CHAR_FIELD, OBJECT_FIELD }; struct StructMeta { FieldType type; size_t offset; }; StructMeta[] metadata = { {CHAR_FIELD, FIELD_OFFSET(a)}, {CHAR_FIELD, FIELD_OFFSET(b)}, {CHAR_FIELD, FIELD_OFFSET(c)}, {INT_FIELD, FIELD_OFFSET(i)}, {OBJECT_FIELD, FIELD_OFFSET(o)}, } void RenderStruct(Foo* f) { for (int i = 0; i < sizeof(metadata)/sizeof(StructMeta); i++) { switch (metadata[i].type) { case CHAR_FIELD: char c = *((char*)f + metadata[i].offset); // render c break; case INT_FIELD: int i = *(int*)((char*)f + metadata[i].offset); // render i break; case OBJECT_FIELD: Object* o = (object*)((char*)f + metadata[i].offset); const char* s = o->ToString(); // render s break; } } } 

Примечание: все указательные арифметики должны выполняться на указателях (char *), чтобы убедиться, что смещения интерпретируются как байты.

Невозможно выполнить итерацию членов структуры, если вы не создадите свои собственные метаданные для описания структуры. Компилятор C ++ просто не выделяет необходимую вам информацию.

Тем не менее, с небольшим количеством магии, вы можете собрать метаданные, которые вам потребуются довольно легко. Несколько лет назад я написал код для этого (на самом деле полнофункциональный пользовательский элемент управления Windows), и я все еще использую его все время.

Основной трюк заключается в том, чтобы использовать малую магию магии, чтобы заставить компилятор помочь вам в создании метаданных.

 // this is the structure I want to iterate typedef struct { int foo; char bar[16]; } StructIWantToIterate; // this is the metadata I need for each field of the structure typedef struct { char * pszFieldName; size_t oFieldOffset; size_t cbFieldSize; int eType; } MyStructMeta; // these are the field types I need to handle. enum { type_is_int, type_is_char, }; // these macros help to emit the metadata #define NUMELMS(ary) (sizeof(ary)/(sizeof(ary)[0])) #define FIELDOFF(tag,fld) ((size_t)&(((tag *)0)->fld)) #define FIELDSIZ(tag,fld) sizeof(((tag *)0)->fld) #define STDFLD(tag,fld,as) #fld, FIELDOFF(tag,fld), FIELDSIZ(tag,fld), as // now we declare the metadata for the StructIWantToIterate structure #undef MYFLD #define MYFLD(fld,as) STDFLD(StructIWantToIterate,fld,as) static const MyStructMeta aMeta[] = { MYFLD(foo, type_is_int), // expands to "foo", 0, sizeof(int), type_is_int MYFLD(bar, type_is_char),// expands to "bar", sizeof(int), 16, type_is_char }; // and when we want to do the iteration, assume ptr is a pointer to an instance // of StructIWantToIterate for (int ii = 0; ii < NUMELMS(aMeta); ++ii) { char szLine[100]; // pick your own worst case line size. // get a pointer to the current field within the struct void * pfld = ((byte*)ptr) + aMeta[ii].oFieldOffset; // print out the field data based on the type_is_xxx information switch (aMeta[ii].eType) { case type_is_int: sprintf(szLine, "%s : %d", aMeta[ii].pszFieldName, *(int*)pfld); break; case type_is_char: sprintf(szLine, "%s : %*s", aMeta[ii].pszFieldName, aMeta[ii].cbFieldSize, pfld); break; } // send it to the richedit control RichEdit1->Lines->Append(asLine); } 

Невозможно перебрать элементы простой структуры. Вы должны предоставить эту информацию вне объявления структуры.

Вы можете сделать это во время компиляции, как показали некоторые из предыдущих ответов. Однако вы можете это сделать и во время выполнения. Это похоже на то, как работают некоторые библиотеки «Сериализация».

У вас может быть следующий class:

 class MemberStore { public: template MemberStore(const Base &base) : m_basePtr(reinterpret_cast(&base)) {} template MemberStore& operator&(const Member &classMember){ DataInfo curMember; curMember.m_offset = reinterpret_cast(&classMember) - m_basePtr; curMember.m_func = &CvtChar; m_members.push_back(curMember); return *this; } std::string convert(size_t index) { return m_members[index].m_func(m_basePtr + m_members[index].m_offset); } size_t size() const { return m_members.size(); } protected: template static std::string CvtChar(const void *data) { std::stringstream str; str << *reinterpret_cast(data); return str.str(); } private: struct DataInfo { size_t m_offset; std::string (*m_func)(const void *data); }; std::vector m_members; const char *m_basePtr; }; 

Этот class содержит вектор «членов classа» (MemberStore :: DataInfo), каждый из которых имеет:

  • Смещение от базы classов.
  • Метод преобразования их в std :: strings. Этот метод генерируется автоматически, если для преобразования можно использовать std :: stringstream. Если это невозможно, должно быть возможно специализировать шаблон.

Вы можете добавлять элементы в этот class с помощью оператора & (вы можете объединить несколько операторов и операторов). После этого вы можете перебирать элементы и преобразовывать их в std :: string, используя свой индекс:

 struct StructureIWantToPrint { char a; int b; double c; }; int main(int argc, wchar_t* argv[]) { StructureIWantToPrint myData; myData.a = 'b'; myData.b = 18; myData.c = 3.9; MemberStore myDataMembers(myData); myDataMembers & myData.a & myData.b & myData.c; for(size_t i=0;i 

Должно быть возможно изменить class MemberStore, чтобы вместо хранения метода преобразования элемента в std :: string автоматически вставлял данные в TextList.

Я не использую C ++ Builder, поэтому некоторые из деталей этого, вероятно, будут немного отключены, но общая идея должна быть, по крайней мере, достаточно близкой:

 class richedit_stream { TRichEditControl &ctrl; public: richedit_stream(TRichEditControl &trc) : ctrl(trc) {} template  richedit_stream &operator<<(T const &value) { std::stringstream buffer; buffer << value; ctrl.Lines->Append(value.str().c_str()); return *this; } }; 

Основная идея довольно проста: интерфейс для управления richedit, который предоставляет шаблонный оператор <<. Оператор помещает элемент в строковый поток, чтобы преобразовать его в строку. Затем он получает полученную строку и добавляет ее к строкам в элементе управления. Поскольку это шаблон, он может работать со всеми обычными типами, поддерживаемыми строковым потоком.

У этого есть недостатки – без дополнительной работы вы не сможете использовать манипуляторы для управления форматированием данных по мере их преобразования в строку. Поскольку он использует строковый stream для преобразования вещей в строки, он, вероятно, также немного медленнее, чем ваш код, явно кодирующий тип каждого преобразования. В то же время вы можете использовать довольно чистый, простой, идиоматический код в обмен на довольно минимальные инвестиции.

Я предлагаю создать шаблонные методы для записи в текстовое поле:

 template  void Write_To_Textbox(const T& variable, const std::string& variable_name, TRichTextEdit & textbox) { //... } 

Затем используйте некоторые функции редактирования вырезания, копирования, вставки и регулярного выражения, способные заменить функции редактора и создать функцию «аннотировать»:

 void annotate(TRichTextEdit& textbox) { Write_To_Textbox(member1, "member1", textbox); //... } 

Примечание. Проверьте синтаксис функций шаблона, поскольку я не думаю, что я правильно понял этот пример.

Поскольку у вас довольно большое количество полей в структуре, используйте парсер или напишите свой собственный, чтобы генерировать исходный код для печати членов, их имен и их значений.

Как интересное упражнение, позаботьтесь о том, как вы пишете утилиту. Вы можете обнаружить, что использование редактора с возможностью поиска и замены регулярного выражения может быть быстрее.

В противном случае выкиньте свой текущий дизайн и примите новый. Я использую дизайн записей и полей. Каждая запись (структура) имеет вектор одного или нескольких указателей на Field_Interface . У Field_Interface есть такие методы, как get_field_name() и get_sql_data_type_text() . Также не забывайте, что Java любимый toString() который возвращает значение поля в виде строки. Этот метод позволяет вам перебирать контейнер полей и распечатывать их значения (используя toString ) и их имя (используя get_field_name() ).

Добавьте шаблон посетителя для чтения и записи (я называю «Читатели и писатели»), и у вас есть поля и записи, которые можно легко адаптировать, не изменяя их внутреннего содержимого. Кроме того, это отлично ведет к Generic Programming, где вы можете работать с полями и записями, не зная их типов; или заботясь о них на уровне листьев.

Кстати, в то время, когда вы ждали идеального ответа, вы могли бы написать функцию «итерации» или посещения членов структуры.

Таким образом, вам нужно сделать информацию о типе доступной во время выполнения. Эти метаданные доступны во время компиляции, но затем отбрасываются. Нам просто нужен способ спасти его от компилятора.

  1. Явные метаданные , о чем свидетельствуют антонмарков и Джон Кноллер. Вы должны держать его в синхронизации со структурой, но это имеет силу не касаться вашего первоначального определения структуры.

    1.1 Генерация кода Если ваше определение структуры достаточно регулярно, вы можете автоматизировать создание этой таблицы метаданных с помощью awk.

  2. Метапрограммирование : если вы не возражаете переписывать структуру (но оставляя макет таким же, чтобы сохранить двоичную совместимость), вы можете заставить компилятор сделать тяжелую работу для вас. Вы можете использовать Boost.tuple, чтобы объявить вашу структуру и перебрать ее элементы с помощью Boost.Fusion .

Не то чтобы я думаю, что это отличный ответ, но похоже, что он должен быть включен по соображениям полноты. Несколько иной подход состоял бы в том, чтобы написать расширение отладчика, используя API-интерфейсы расширения отладчика Windows . Задача, которую вы описываете, почти идеально подходит для расширения отладчика. Я говорю почти потому, что не уверен, что включение в сборку релизов – очень хороший план. Но в зависимости от того, где вы нуждаетесь в этой функции, это может быть возможно. Если это необходимо «в доме» для ваших собственных целей, это может сработать. Если это необходимо для запуска на сайте клиента, я бы менее склонен использовать его из-за лишнего багажа (символы отладки), которые необходимо будет отправить.

В вашей среде есть одна большая потенциальная проблема. Похоже, вы используете C ++ Builder версии 5. Я не знаю, как создать отладочные символы из этой среды, которая будет работать с инструментами отладки Windows. Существует утилита map2dbg, которая выполняет преобразование, но, по-видимому, ей требуется, по крайней мере, C ++ Builder v6.