Is there an easier way to display the struct
fields and their corresponding values in RichEdit
control?
This is what I am doing now:
<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:
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.
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);
}
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));
}