PDA

View Full Version : Is there a way to enumerate *all* the fields of a class?



Chebmaster
21-05-2006, 04:58 AM
Goal: to create the system of automatic saving/loading the entire game world to/from a stream.

Why TPersistent + published properties won't do:
1. Must work for dynamic arrays (single- and multi-dimensional) and records. AFAIK, FPC doesn't allow to publish these types.
2. There is no way to control if I missed "publishing" some field - which, in turn can cause a nasty and hard to catch glitches in runtime - the last thing I need.

Solution: (is this an optimal one?)
Each class is registered at program startup. It then registers all its fields (via manually created method). To avoid mising something, the registering method looks up all the registered fields and analyzes if they cover the entire object instance (taking the aligning into account). If not, program terminates with a yell.

Does anybody know a better way?

marcov
11-07-2006, 08:21 PM
Goal: to create the system of automatic saving/loading the entire game world to/from a stream.


Make sure all relevant props are published then :-) There is nothing else but workarounds. Deep RTTI is horrible expensive.



Why TPersistent + published properties won't do:
1. Must work for dynamic arrays (single- and multi-dimensional) and records. AFAIK, FPC doesn't allow to publish these types.


For types you can't publish: wrap them in a publihed property that you only use for streaming (and mark them as such e.g. with a prefix)

arrays: define an array property with getters and setters.
records : define a class and stream the record in the class.

jdarling
11-07-2006, 08:43 PM
Also look into TReader and TWriter. These help out a bit. In the end, outside of published properties in $m there is no automated way of doing this. Simply put, the info isn't there at run time.

Chebmaster
16-08-2006, 12:49 PM
I just got notification about this thread on my e-mail as my ISP cut my access due to delayed payment.
Now I'm back and can reply at last.


...In the end I implemented the approach with "registering" the types and classes. Every "streameable" :?: class has a special method where all the class fields are registered one by one. The registrator watches for the "holes" in the class body and stops the program if I forget to register some field.

Example:

Type
TMipMapKind = (mmk_None, mmk_Normal, mmk_AlphaCorrected);
TMotherTexture = class (TTrulyPersistent)
//base class for all textures.
protected
f_SessionID: int64;
f_name: glUint;
f_target: glEnum;
f_quality: integer;
f_priority: glFloat;
f_mmk: TMipMapKind;
function _GetIsResident: GLboolean;
public
procedure RegisterFields; override;
procedure AfterLoading(); override;
procedure AfterConstruction(); override;

procedure Reload; virtual; abstract;
procedure Bind;
property IsResident: GLboolean read _GetIsResident;
end;


...

procedure TMotherTexture.RegisterFields;
begin
RegType(TypeInfo(TMIpMapKind));
RegField('f_SessionID', @f_SessionId, TypeInfo(int64));
RegField('f_name', @f_name, TypeInfo(glUint));
RegField('f_target', @f_target, TypeInfo(glEnum));
RegField('f_quality', @f_quality, TypeInfo(integer));
RegField('f_priority', @f_priority, TypeInfo(glFloat));
RegField('f_MipMapKind', @f_mmk, TypeInfo(TMipMapKind));
end;


In the end the system came out so powerful that the puny published properties can't even compare with it. I achieved the saving/loading speed of 1.000.000 cross-linked objects per second.

Test can be downloaded here (http://chebmaster.narod.ru/cge/cgedownload_e.html).