Выполните итерации по структуре; легко поля структуры дисплея и значения в поле RichEdit

Один угловой случай для учета, который удивил меня, когда я сначала узнал об этом: Windows позволяет вести пробелы в именах файлов! Например, следующее все легальные, и отличные, имена файлов в Windows (минус кавычки):

"file.txt"
" file.txt"
"  file.txt"

Одна еда на дом от этого: Соблюдите осторожность при записи кода, который обрезает пробел продвижения/запаздывания от строки имени файла.

12
задан Uli Gerhardt 2 September 2010 в 08:32
поделиться

9 ответов

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<typename T>
    void operator()(T& t)const
    {

        m_Ctrl.Lines.Append(boost::lexical_cast<std::string>(t));
    }

    RichEditControl& m_Ctrl;

};

// Usage:
void FillTextBox(Foo& F, RichEditControl& Ctrl)
{
    boost::fusion::for_each(F, AppendToTextBox(Ctrl));
}
24
ответ дан 2 December 2019 в 03:11
поделиться

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

class richedit_stream { 
    TRichEditControl &ctrl;
public:
    richedit_stream(TRichEditControl &trc) : ctrl(trc) {}

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

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

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

2
ответ дан 2 December 2019 в 03:11
поделиться

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

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

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

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

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

1
ответ дан 2 December 2019 в 03:11
поделиться

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

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

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 байта содержат поля символов, четвертый - случайное значение, используемое для заполнения, а следующие четыре байта - это представление целого числа 122 с прямым порядком байтов. Этот макет будет отличаться от компилятора к компилятору и от системы к системе. Короче говоря, двоичное представление не Я не могу сказать вам, что это за данные или где хранятся отдельные поля.

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

char c = f.c;

транслируется в команду типа

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

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

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

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

11
ответ дан 2 December 2019 в 03:11
поделиться

Невозможно выполнить итерацию элементов структуры, если вы не создадите свои собственные метаданные для описания структуры. Компилятор 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);    
}
5
ответ дан 2 December 2019 в 03:11
поделиться

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

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

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

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

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

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

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

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

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

это прекрасно ведет к Generic Programming , где вы можете работать с полями и записями, не зная их типов; или позаботившись об этом на уровне листа.

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

это прекрасно ведет к Generic Programming , где вы можете работать с полями и записями, не зная их типов; или позаботившись об этом на уровне листа.

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

1
ответ дан 2 December 2019 в 03:11
поделиться

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

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

Вы можете иметь следующий класс:

class MemberStore
{
public:
  template<typename Base>
  MemberStore(const Base &base) : 
    m_basePtr(reinterpret_cast<const char*>(&base))
  {}

  template<typename Member>
  MemberStore& operator&(const Member &classMember){
    DataInfo curMember;
    curMember.m_offset = reinterpret_cast<const char*>(&classMember) - m_basePtr;
    curMember.m_func = &CvtChar<Member>;
    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<typename Type> 
  static std::string CvtChar(const void *data) {
    std::stringstream str;
    str << *reinterpret_cast<const Type*>(data);
    return str.str();
  }

private:
  struct DataInfo {
    size_t m_offset;
    std::string (*m_func)(const void *data);
  };
  std::vector<DataInfo> m_members;
  const char *m_basePtr;
};

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

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

Элементы в этот класс можно добавлять с помощью оператора & (можно объединять несколько операторов &). После этого можно выполнить итерацию к членам и преобразовать их в 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<myDataMembers.size();++i) {
    std::cout << myDataMembers.convert(i) << std::endl;
  }

    return 0;
}

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

.
3
ответ дан 2 December 2019 в 03:11
поделиться

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

  1. Explicit Metadata, как это продемонстрировали Антонмарков и Джон Кноллер. Вы должны синхронизировать ее со структурой, но она обладает тем преимуществом, что не трогает первоначальное определение структуры.

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

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

1
ответ дан 2 December 2019 в 03:11
поделиться

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

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

.
1
ответ дан 2 December 2019 в 03:11
поделиться
Другие вопросы по тегам:

Похожие вопросы: