Iterate Over Struct; Easily Display Struct Fields And Values In a RichEdit Box

后端 未结 9 1848
离开以前
离开以前 2020-12-13 22:10

Is there an easier way to display the struct fields and their corresponding values in RichEdit control?

This is what I am doing now:

<
相关标签:
9条回答
  • 2020-12-13 22:36

    There is no way to iterate over the members of a plain struct. You must supply this information outside your struct declaration.

    You can do it at compile time, as some of the previous answers have shown. However, you can do it at run-time, too. This is similar to the way some "Serialization" libraries work.

    You may have the following class:

    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;
    };
    

    This class contains a vector of "class members" (MemberStore::DataInfo), each of one has:

    • Offset from the class base.
    • A method to convert them to std::strings. This method is automatically generated if it is possible to use std::stringstream for the conversion. If it is not possible, it should be possible to specializate the template.

    You can add elements to this class using the & operator (you can concatenate several & operators). After that, you can iterate over to members and convert them them to std::string using its index:

    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;
    }
    

    It should be possible to modify the MemberStore class so that, instead of store a method to convert member to std::string, automatically inserts the data to the TextList.

    0 讨论(0)
  • 2020-12-13 22:38

    There is no way to iterate the members of a struct unless you build your own metadata to describe the struct. The C++ compiler simply doesn't emit the information you would need automatically.

    However, with a bit of macro magic, you can build the metadata you would need pretty easily. I wrote some code to do this (actually a full blown Windows custom control) many years ago and I still use it all the time.

    The basic trick is to use a bit macro magic of get the compiler to help you build the metadata.

    // 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);    
    }
    
    0 讨论(0)
  • 2020-12-13 22:39

    BOOST_FUSION_ADAPT_STRUCT seems to fit well here. For example:

    // 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));
    }
    
    0 讨论(0)
提交回复
热议问题