C++遍历结构体的字段,请大家指点我的方法!

gemo 2012-03-27 10:12:09
C++本身不支持遍历结构体的字段,我举个例子:
ShowPersons()用来打印所有人的各项信息。问题在于,如果其他人只能看到Person的定义,然后要对Person的字段进行遍历,岂不是又要写个遍历函数?如果Persons有上百个字段,如果打印格式经常变化,这样岂不是很容易出错?


struct Person
{
string name;
int age;
Person(string name_, int age_)
{
name = name_;
age = age_;
}
};
void MakePersons(vector<Person>& persons)
{
persons.clear();
persons.push_back(Person("Jack", 10));
persons.push_back(Person("Tom", 30));
persons.push_back(Person("Lucy", 15));
}
void ShowPersons(vector<Person>& persons)
{
for (size_t i = 0; i < persons.size(); i++)
{
cout<<"name: "<<persons[i].name.c_str()<<"\t";
cout<<"age: "<<persons[i].age<<endl;
}
}
...全文
7194 79 打赏 收藏 转发到动态 举报
写回复
用AI写文章
79 条回复
切换为时间正序
请发表友善的回复…
发表回复
olderma 2012-11-08
  • 打赏
  • 举报
回复
40的很不错哈
qq649610659 2012-06-29
  • 打赏
  • 举报
回复
你这样太浪费内存了。 建议你 在定义一个说明对结构Person 的说明结构 PersonInfo 。
ItemInfo
{
int type;//字段类型
char*name; ///名称
int *offset; //偏移量
int ex;//扩展用
}
PersonInfo
{
int num;//个数
ItemInfo* Info; //数组首地址
};

PersonInfo pinfo;

然后根据pinfo;遍历
来呀来打我啊 2012-04-06
  • 打赏
  • 举报
回复
好贴啊,我一直想转行做C或者C++
enic 2012-04-06
  • 打赏
  • 举报
回复 1
你自己整个k-v,然后放map不就搞定了?
ooseven1975 2012-04-05
  • 打赏
  • 举报
回复
标准c++是无法清爽的满足楼主的需求的,但是,vc倒是有一个途径可以轻松得到元数据,那就是自己去读取vc的pdb文件,里面啥数据都有,可以轻松的做运行时反射
没玩法了 2012-04-03
  • 打赏
  • 举报
回复
重载<<操作符
MagiSu 2012-04-03
  • 打赏
  • 举报
回复
LZ
需要的就是反射。
darkread 2012-04-02
  • 打赏
  • 举报
回复
可以用宏,像MFC那样,用用把你要输出的元素名字和值都放在一个列表里面。
事实上,即使是游戏输出熟悉也是XX->xx这样的模式。没办法的事情。
  • 打赏
  • 举报
回复
看不懂
jiandingzhe 2012-03-31
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 的回复:]

回楼上:
1 如果打印格式经常变化?
2 如果我又想生成一个关于Person所有字段的sql脚本文件?

那么你就每次都重新手写一个处理百个字段的函数?
[/Quote]
不要直接用C的struct,而是做你自己的容器,每个字段都是一个类,设计成这个类知道如何打印自己。
d_d_v 2012-03-31
  • 打赏
  • 举报
回复
看不大懂。
redleaves 2012-03-30
  • 打赏
  • 举报
回复
[Quote=引用 61 楼 的回复:]我这里有个小技巧,不需要使用到链表
BEGIN_UDT( class, test_class2 )/END_UDT()
实际定义了一个结构体test_class2_infomation
DEFINE_MEMBER( int, test_member )
定义了一个描述test_class2::test_member的成员变量,这个变量名叫test_member_descript;
注意……[/Quote]如果要加成员变量,或者专门定义一个METINFO类,就不用这么复杂了.之所以写这么复杂,就是为了保持数据原有的结构不变.并且在数据的定义过程中"自然"生成类型元信息.
做到完全不影响原来的程序.另外,这里用链表是因为这样写比较简单,不用链表也没什么问题...直接使用STL的容器也是可以的.或者心情好的话,可以把节点组织成红黑树~~
lanzhengpeng2 2012-03-30
  • 打赏
  • 举报
回复
我这里有个小技巧,不需要使用到链表
BEGIN_UDT( class, test_class2 )/END_UDT()
实际定义了一个结构体test_class2_infomation
DEFINE_MEMBER( int, test_member )
定义了一个描述test_class2::test_member的成员变量,这个变量名叫test_member_descript;
注意,每个DEFINE_MEMBER定义的descript的大小可以做到一样.
因此,(sizeof(test_class2_infomation)-sizeof(base_infomation))/sizeof(test_member_descript)就能知道有多少个descript成员变量.
这些成员变量的变量名其实无意义.所以,可以从第一个成员变量开始,当作有N个数据的数组看待,重新按照常用的查找类型(如成员变量名)进行排序.然后用二分查找就好
flxue 2012-03-30
  • 打赏
  • 举报
回复
好帖!!!
redleaves 2012-03-30
  • 打赏
  • 举报
回复
#include <stdio.h>
struct __meta_base_info_t {
const char *name;
enum meta_type_t{
META_OBJ,
META_MEMBER,
META_METHOD
} type;
__meta_base_info_t *_next;
__meta_base_info_t( const char *n, meta_type_t t ) : name(n), type(t), _next(0) {}
};

struct __meta_obj_info_t : public __meta_base_info_t {
__meta_base_info_t *_head;
__meta_obj_info_t( const char *n )
: __meta_base_info_t(n,__meta_base_info_t::META_OBJ)
, _head(0)
{}
void add_member( __meta_base_info_t*info ) {
if ( this->_head == 0 ) {
this->_head = info;
}
if ( this->_next != 0 ) {
this->_next->_next = info;
}
this->_next = info;
}
bool get_member( void *obj, const char *name, void *data ) const;
bool set_member( void *obj, const char *name, const void *data ) const;
void print() const {
printf( "name : %s\n", this->name );
__meta_base_info_t *hdr = this->_head;
while( hdr != 0 ) {
printf( "\t%s\n", hdr->name );
hdr = hdr->_next;
}
}
};

struct __meta_member_info_t : public __meta_base_info_t {
typedef void (*_getter_t)( void *obj, void *data );
typedef void (*_setter_t)( void *obj, const void *data );
__meta_member_info_t( __meta_obj_info_t &obj_info, const char *n, __meta_member_info_t::_getter_t g, __meta_member_info_t::_setter_t s)
: __meta_base_info_t(n,__meta_base_info_t::META_MEMBER)
, getter(g), setter(s)
{
obj_info.add_member( this );
}
_getter_t getter;
_setter_t setter;
};

bool __meta_obj_info_t::get_member( void *obj, const char *name, void *data ) const {
__meta_base_info_t *hdr = this->_head;
while( hdr != 0 ) {
if ( strcmp( name, hdr->name ) == 0 ) {
reinterpret_cast<__meta_member_info_t*>(hdr)->getter( obj, data );
return true;
}
hdr = hdr->_next;
}
return false;
}
bool __meta_obj_info_t::set_member( void *obj, const char *name, const void *data ) const {
__meta_base_info_t *hdr = this->_head;
while( hdr != 0 ) {
if ( strcmp( name, hdr->name ) == 0 ) {
reinterpret_cast<__meta_member_info_t*>(hdr)->setter( obj, data );
return true;
}
hdr = hdr->_next;
}
return false;
}

#define META_INFO_REGISTER(idx,_register_func)

#define BEGIN_UDT_IMPL( type, name, __ID__ ) type name { \
typedef name __meta__this_t;\
template <typename _Type, int __Idx>\
struct __meta_info_register {\
static void __init__() {\
__meta_info_register<_Type,__ID__+1>::__init__();\
}\
static __meta_obj_info_t &__meta_get_udt_info() { \
static __meta_obj_info_t __info_instance(#name);\
return __info_instance;\
}\
};\
typedef __meta_info_register<__meta__this_t, __ID__> __meta_info_instance__;\
friend __meta_info_register<__meta__this_t, __ID__>;

#define BEGIN_UDT( type, name ) BEGIN_UDT_IMPL(type,name, __COUNTER__)

#define END_UDT() \
template<typename _Type> struct __meta_info_register<_Type,__COUNTER__> {static void __init__() {}};\
public:\
typedef struct { \
static const __meta_obj_info_t &instance() { \
static struct _meta_info_init {_meta_info_init() { __meta__this_t::__meta_info_instance__::__init__(); }} _init;\
return __meta__this_t::__meta_info_instance__::__meta_get_udt_info();\
}\
} _META_INFO_;\
};

#define DEFINE_MEMBER_IMPL( type, name, __ID__ ) type name;\
template<typename _Type> struct __meta_info_register<_Type,__ID__> {\
static void __meta_setter_##name( __meta__this_t*obj, const type &data ) {obj->name = data;}\
static void __meta_getter_##name( __meta__this_t*obj, type &data ) {data = obj->name;}\
static void __meta_register_##name() {static __meta_member_info_t __info_instance(__meta_info_instance__::__meta_get_udt_info(),#name,(__meta_member_info_t::_getter_t)__meta__this_t::__meta_info_register<_Type,__ID__>::__meta_getter_##name,(__meta_member_info_t::_setter_t)__meta__this_t::__meta_info_register<_Type,__ID__>::__meta_setter_##name);}\
static void __init__() { __meta_register_##name(); __meta_info_register<_Type,__ID__+1>::__init__(); }\
};\
friend __meta_info_register<__meta__this_t, __ID__>;

#define DEFINE_MEMBER( type, name) DEFINE_MEMBER_IMPL( type, name, __COUNTER__ )

BEGIN_UDT( struct, test_class )
DEFINE_MEMBER( int, test_member )
END_UDT()

BEGIN_UDT( class, test_class2 )
DEFINE_MEMBER( int, test_member )
DEFINE_MEMBER( int, foo )
DEFINE_MEMBER( int, bar )
END_UDT()

int main(int argc, char* argv[])
{
printf( "sizeof:%d\n", sizeof(test_class) );
test_class::_META_INFO_::instance().print();
test_class2::_META_INFO_::instance().print();
test_class a;
a.test_member = 10;

const char * member_name[] = {
"test_member",
"test_member_xxx",
};
for( int i=0; i<sizeof(member_name)/sizeof(member_name[0]); i++ ) {
const char * name = member_name[i];
int val;
if ( test_class2::_META_INFO_::instance().get_member( &a, name, &val ) ) {
printf( "get member ->%s\t%d\n", name, val );
} else {
printf( "invalid member %s\n", name );
}
}
int new_val = 12;
test_class::_META_INFO_::instance().set_member( &a, "test_member", &new_val );
printf( "set new val %d\n", a.test_member );
return 0;
}
利用了一个非标准扩展实现的自动元数据记录.只有vc和icl以及gcc可以编译.
宏比较丑,而且会使程序膨胀得比较厉害...
只会给对象加几个类型定义,不改变对象的数据结构.不会影响数据的兼容性.
可以实现动态的对象加载,修改,调用.
楼主可以在这个基础加上更复杂的机制实现成员类型信息的记录和使用.
lanzhengpeng2 2012-03-30
  • 打赏
  • 举报
回复
另外,你的gettor,settor我觉得一者没有太多必要,二者也不能满足需要.C++的元数据拿来仅仅读写意义不大,关键要拿来表达.而要表达,涉及到如何表达的问题.如DWORD,可以是一个普通的数字,也可以表示ARGB颜色,或者位的组合,或者枚举值;对应到表达UI就是 普通输入框/颜色编辑控件/多选下拉列表/单选下拉列表.
所以,gettor,settor再附加一个convert以及一个对应的约束条件,就可以做出很多有意思的东西来了。
我这面,利用这个玩意儿,外加一个扩展的CListCtrl,实现了绝大部分数据的编辑录入工作。
lanzhengpeng2 2012-03-30
  • 打赏
  • 举报
回复
保持数据原有的结构不变:
当然不能写在原结构体中,而是另起一个结构体来描述原结构体.这两者之间可以没有任何关系,需要的时候通过RTTI可以找到描述结构体就行.
并且在数据的定义过程中"自然"生成类型元信息:
我这个做法需要单独申明结构体,但也可以在原数据上加标示,利用工具自动生成.
至于各种复杂的结构体来组织类型元信息,我认为就没有必要了.有序数组就足够了.
luciferisnotsatan 2012-03-29
  • 打赏
  • 举报
回复
[Quote=引用 47 楼 的回复:]

只是个示例,具体用的时候根据需要调整,用Prop.h主要是方便,改成回调方式也只是几行代码的事,如果代码给到这份上还写不出自己的东西,那也就无话可说了

思想最重要,对吧?呵呵
[/Quote]
是的。如果lz要用你的方法,建议lz把这帖子当说明文档放着。
这样接手的人看完帖子,多少能知道为啥要用这种方法了。

又看了下,这个方法有个好处是,连作者自己都不用去关心遍历了。不会加了个变量,然后在遍历的show(我的方法)里漏了增加对应的调用。
坏处是,能不能看懂。。。
做游戏的小明 2012-03-29
  • 打赏
  • 举报
回复
只是个示例,具体用的时候根据需要调整,用Prop.h主要是方便,改成回调方式也只是几行代码的事,如果代码给到这份上还写不出自己的东西,那也就无话可说了

思想最重要,对吧?呵呵
luciferisnotsatan 2012-03-29
  • 打赏
  • 举报
回复
[Quote=引用 44 楼 的回复:]

漏了点东西,Prop.h里面声明结构的属性,写法就类似下面

C/C++ code

DeclProp(int, m_prop1)
DeclProp(std::string, m_prop2)
[/Quote]
如果Prop.h是用户来写,那还是没解决lz的问题。
如果是作者来写,个人觉得还是直接由作者把类型,名当字符串传给回调函数看起来简单点。
加载更多回复(56)

64,654

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧