迭代结构; 在RichEdit框中轻松显示结构字段和值

12 c++ user-interface struct richedit c++builder-5

是否有更简单的方法来显示控件中的struct字段及其对应的值RichEdit

这就是我现在正在做的事情:

AnsiString s;

s = IntToStr(wfc.fontColor);
RichEdit1->Lines->Append(s);
Run Code Online (Sandbox Code Playgroud)

等等...

有没有比单独呼叫每个人更简单的方法?我想读取一个二进制文件,然后在RichEdit我正在构建的小实用程序的控件中显示相应的结构,并且没有找到其他方法.我知道如何读取二进制文件并将值读入struct已经存在的文件中.

Éri*_*ant 24

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));
}
Run Code Online (Sandbox Code Playgroud)


Ant*_*ton 11

如果我理解正确,原始问题的核心是如何迭代结构.简而言之,正如Jerry Coffin在评论中指出的那样,这是不可能做到的.我将尝试解释原因,然后我将尝试解释如何做下一个最好的事情.

结构作为单片数据存储在内存中,而没有任何描述其结构的元数据.例如,以下结构:

struct Foo {
    char a;
    char b;
    char c;
    int i;
}

Foo f = {'x', 'y', 'z', 122};
Run Code Online (Sandbox Code Playgroud)

可以使用十六进制表示法在存储器中表示如下

78 79 7A FF 7A 00 00 00
Run Code Online (Sandbox Code Playgroud)

前三个字节包含char字段,第四个是用于填充的随机值,接下来的四个字节是整数122的little-endian表示.这种布局因编译器和编译器以及系统而异.简而言之,二进制表示不会告诉您数据是什么或单个字段的存储位置.

那么编译器如何访问结构中的字段?代码

char c = f.c;
Run Code Online (Sandbox Code Playgroud)

被翻译成像

COPY BYTE FROM ([address of f] + 2) TO [address of c]
Run Code Online (Sandbox Code Playgroud)

换句话说,编译器将字段的字面偏移编码到代码中.同样,这对我们没有帮助.

因此,我们必须自己注释结构.这可以通过在结构内添加信息以将其转换为一种键值存储或通过添加第二结构来完成.您不想更改原始结构,因此第二种结构是可行的方法.

我假设您的结构只包含基本类型:int,char等.如果您是结构中复杂的其他类,那么我建议将一个ToString()方法添加到它们的基类并调用该方法 - 这就是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;    
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:所有指针算术应该在(char*)指针上完成,以确保偏移量被解释为字节.


Joh*_*ler 5

除非您构建自己的元数据来描述结构,否则无法迭代结构的成员.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);    
}
Run Code Online (Sandbox Code Playgroud)