Re: [fpc-pascal] Read Field names from VMT

2024-02-01 Thread Amir--- via fpc-pascal

I see... The trick was to define TMyClass!

Thanks!


On 2/1/24 02:19, Michael Van Canneyt via fpc-pascal wrote:



On Wed, 31 Jan 2024, Amir--- via fpc-pascal wrote:



Without more info (declaration of ChildTClass, declaration of the 
constructor

of that class etc), it is not possible to comment.

We need a complete compilable code sample to provide you with more 
insight.



Please have a look at the attachment.


The constructor of TObject is not virtual, in difference with the 
destructor.


Given the definitions

  ChildObj: TObject;
  ChildTClass: TClass;

The following statement

  ChildObj := ChildTClass.Create;

will always use the TObject.Create, since it is not virtual.

In general, you cannot use TObject for this kind of thing.

For this to work, you need to create a descendent of TObject with a 
virtual

constructor (call it TMyObject) of known signature, and do

  ChildObj: TMyObject;
  ChildTClass: TMyClass; // class of TMyobject

then

  ChildObj := ChildTClass.Create;

will take the correct overridden constructor.

Your code is also incomplete in the sense that it will only work for 
classes
with a constructor without arguments. If a overloaded constructor 
exists which takes arguments (like TComponent), it will not be called 
and the class will not be instantiated correctly.

But maybe that will not be your use-case.

I attached a version of your program that works and has no memleaks.

Michael.

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Read Field names from VMT

2024-02-01 Thread Michael Van Canneyt via fpc-pascal



On Wed, 31 Jan 2024, Amir--- via fpc-pascal wrote:



Without more info (declaration of ChildTClass, declaration of the 
constructor

of that class etc), it is not possible to comment.

We need a complete compilable code sample to provide you with more insight.


Please have a look at the attachment.


The constructor of TObject is not virtual, in difference with the destructor.

Given the definitions

  ChildObj: TObject;
  ChildTClass: TClass;

The following statement

  ChildObj := ChildTClass.Create;

will always use the TObject.Create, since it is not virtual.

In general, you cannot use TObject for this kind of thing.

For this to work, you need to create a descendent of TObject with a virtual
constructor (call it TMyObject) of known signature, and do

  ChildObj: TMyObject;
  ChildTClass: TMyClass; // class of TMyobject

then

  ChildObj := ChildTClass.Create;

will take the correct overridden constructor.

Your code is also incomplete in the sense that it will only work for classes
with a constructor without arguments. 
If a overloaded constructor exists which takes arguments (like TComponent), 
it will not be called and the class will not be instantiated correctly.

But maybe that will not be your use-case.

I attached a version of your program that works and has no memleaks.

Michael.program FieldAddress;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}
  cthreads,
  {$ENDIF}
  Classes, TypInfo
  { you can add units after this };

type
  {$M+}

  { TValue }
  TMyObject = class (TObject)
constructor create; virtual;
  end;
  TMyClass = class of TMyObject;
  
  TValue = class(TMyObject)
  private
FV: PInteger;
  public
constructor Create; override;
destructor destroy; override;
  end;

  { TParam }

  TParam = class(TObject)
  
  public
destructor destroy; override;
  
  published
V1: TValue;
V2: TValue;

  end;

procedure Process(vft: PVmtFieldTable; Obj: TObject);
var
  vfe: PVmtFieldEntry;
  i: SizeInt;
  Name: AnsiString;
  ChildObj: TMyObject;
  ChildTClass: TMyClass;
  FieldClass : TClass;

begin
  if vft=nil then exit;
  Writeln(vft^.Count, ' field(s) with ', vft^.ClassTab^.Count, ' type(s)');
  for i := 0 to vft^.Count - 1 do
  begin
 vfe := vft^.Field[i];
 Name := vfe^.Name;

 Writeln(i, ' -> ', vfe^.Name, ' @ ', vfe^.FieldOffset, ' of type ', 
vft^.ClassTab^.ClassRef[vfe^.TypeIndex - 1]^.ClassName);

 FieldClass := vft^.ClassTab^.ClassRef[vfe^.TypeIndex - 1]^;
 if FieldClass.InheritsFrom(TMyObject) then
 begin
   Writeln('Inherits');
   ChildTClass := TMyClass(FieldClass);
   WriteLn('ChidTClass: ', ChildTClass.ClassName);
   ChildObj := ChildTClass.Create;
   if ((ChildObj as TValue).FV = nil) or ((ChildObj as TValue).FV^ <> 1) 
then
   begin
 WriteLn('ERROR');
 Halt(1);
   end;
   writeln('Calling sub');
   TObject(Obj.FieldAddress(Name)^):=ChildObj;
   Process(
 PVmtFieldTable(PVMT(ChildTClass)^.vFieldTable),
 ChildObj
   );
 end;
   end;

end;


{ TValue }

constructor TMyObject.create;
begin
end;

constructor TValue.Create;
begin
  inherited Create;
   Writeln('Called');
  FV := New(PInteger);
  FV^ := 1;

end;

destructor TValue.Destroy; 
begin
  dispose(FV);
  inherited;
end;

{ TParam}

destructor TParam.Destroy;

begin
  V1.Free;
  V2.Free;
  Inherited;
end;

var
  Param: TParam;

begin
  Param := TParam.Create;

  Process(PVmtFieldTable(PVMT(TParam)^.vFieldTable), Param);

  Param.Free;
end.

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Read Field names from VMT

2024-01-31 Thread Amir--- via fpc-pascal


Without more info (declaration of ChildTClass, declaration of the 
constructor

of that class etc), it is not possible to comment.

We need a complete compilable code sample to provide you with more 
insight.



Please have a look at the attachment.

Best,
Amir

FieldAddress.lpr.gz
Description: application/gzip
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Read Field names from VMT

2024-01-30 Thread Michael Van Canneyt via fpc-pascal



On Mon, 29 Jan 2024, Amir--- via fpc-pascal wrote:


I am still struggling with this!

for i := 0 to vft^.Count - 1 do
begin
   vfe := vft^.Field[i];
   Name := vfe^.Name;

   ChildTClass := vft^.ClassTab^.ClassRef[vfe^.TypeIndex - 1]^;
   ChildObj := ChildTClass.Create;
   TObject(Obj.FieldAddress(Name)^) := ChildObj;
end;

Looks like "ChildTClass.ClassName" is correct but the constructor of the 
appropriate class is not being called.


Without more info (declaration of ChildTClass, declaration of the constructor
of that class etc), it is not possible to comment.

We need a complete compilable code sample to provide you with more insight.

Michael.___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Read Field names from VMT

2024-01-29 Thread Amir--- via fpc-pascal

I am still struggling with this!

for i := 0 to vft^.Count - 1 do
begin
   vfe := vft^.Field[i];
   Name := vfe^.Name;

   ChildTClass := vft^.ClassTab^.ClassRef[vfe^.TypeIndex - 1]^;
   ChildObj := ChildTClass.Create;
   TObject(Obj.FieldAddress(Name)^) := ChildObj;
end;

Looks like "ChildTClass.ClassName" is correct but the constructor of the 
appropriate class is not being called.



On 1/22/24 23:05, Michael Van Canneyt via fpc-pascal wrote:



On Mon, 22 Jan 2024, Amir--- via fpc-pascal wrote:


  Params := TParams.Create;
  WriteLn(Params.Int2.IntVal);
  WriteLn(TInteger(Params.FieldAddress('Int2')).IntVal);

The third line does not work (the zipped code is attached).


It should be

WriteLn(TInteger(Params.FieldAddress('Int2')^).IntVal);

Terribly convoluted code, though...

Michael.

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Read Field names from VMT

2024-01-22 Thread Amir--- via fpc-pascal



On 1/22/24 23:05, Michael Van Canneyt via fpc-pascal wrote:



On Mon, 22 Jan 2024, Amir--- via fpc-pascal wrote:


  Params := TParams.Create;
  WriteLn(Params.Int2.IntVal);
  WriteLn(TInteger(Params.FieldAddress('Int2')).IntVal);

The third line does not work (the zipped code is attached).


It should be

WriteLn(TInteger(Params.FieldAddress('Int2')^).IntVal);

+1

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Read Field names from VMT

2024-01-22 Thread Michael Van Canneyt via fpc-pascal



On Mon, 22 Jan 2024, Amir--- via fpc-pascal wrote:


  Params := TParams.Create;
  WriteLn(Params.Int2.IntVal);
  WriteLn(TInteger(Params.FieldAddress('Int2')).IntVal);

The third line does not work (the zipped code is attached).


It should be

WriteLn(TInteger(Params.FieldAddress('Int2')^).IntVal);

Terribly convoluted code, though...

Michael.___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Read Field names from VMT

2024-01-22 Thread Amir--- via fpc-pascal

  Params := TParams.Create;
  WriteLn(Params.Int2.IntVal);
  WriteLn(TInteger(Params.FieldAddress('Int2')).IntVal);

The third line does not work (the zipped code is attached).

On 1/21/24 23:43, Michael Van Canneyt via fpc-pascal wrote:



On Sun, 21 Jan 2024, Amir--- via fpc-pascal wrote:


How can I set the value?
I tried something like

Test := TTest.Create

Ptr := Pointer(Test);
TSub(Ptr+8) := TSub.Create;

But it is not working?


Depending on your platform, it can be an alignment issue.

Use

 function TObject.FieldAddress(const name : shortstring) : pointer;

This function is used internally by the streaming system (see
TComponent.SetReference), so it should always work.

Michael.

You can use the PVmtFieldTable and PVmtFieldEntry types from the 
TypInfo unit:


=== code begin ===

program tfield;

{$mode objfpc}{$H+}

uses
  TypInfo;

type
  {$M+}
  TSub = class

  end;

  TTest = class
  published
    fTest: TSub;
  end;

var
  vft: PVmtFieldTable;
  vfe: PVmtFieldEntry;
  i: SizeInt;
begin
  vft := PVmtFieldTable(PVMT(TTest)^.vFieldTable);
  Writeln(vft^.Count, ' field(s) with ', vft^.ClassTab^.Count, ' 
type(s)');

  for i := 0 to vft^.Count - 1 do begin
    vfe := vft^.Field[i];
    Writeln(i, ' -> ', vfe^.Name, ' @ ', vfe^.FieldOffset, ' of type 
', vft^.ClassTab^.ClassRef[vfe^.TypeIndex - 1]^.ClassName);

  end;
end.

=== code end ===

=== output begin ===

PS C:\fpc\git> .\testoutput\tfield.exe
1 field(s) with 1 type(s)
0 -> fTest @ 8 of type TSub

=== output end ===

Side note: contrary to what I had originally written only classes, 
but not interfaces are allowed for published fields and they need to 
have $M enabled.


Regards,
Sven

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal



___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


FieldAddress.lpr.gz
Description: application/gzip
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Read Field names from VMT

2024-01-21 Thread Michael Van Canneyt via fpc-pascal



On Sun, 21 Jan 2024, Amir--- via fpc-pascal wrote:


How can I set the value?
I tried something like

Test := TTest.Create

Ptr := Pointer(Test);
TSub(Ptr+8) := TSub.Create;

But it is not working?


Depending on your platform, it can be an alignment issue.

Use

 function TObject.FieldAddress(const name : shortstring) : pointer;

This function is used internally by the streaming system (see
TComponent.SetReference), so it should always work.

Michael.

You can use the PVmtFieldTable and PVmtFieldEntry types from the 
TypInfo unit:


=== code begin ===

program tfield;

{$mode objfpc}{$H+}

uses
  TypInfo;

type
  {$M+}
  TSub = class

  end;

  TTest = class
  published
    fTest: TSub;
  end;

var
  vft: PVmtFieldTable;
  vfe: PVmtFieldEntry;
  i: SizeInt;
begin
  vft := PVmtFieldTable(PVMT(TTest)^.vFieldTable);
  Writeln(vft^.Count, ' field(s) with ', vft^.ClassTab^.Count, ' 
type(s)');

  for i := 0 to vft^.Count - 1 do begin
    vfe := vft^.Field[i];
    Writeln(i, ' -> ', vfe^.Name, ' @ ', vfe^.FieldOffset, ' of type 
', vft^.ClassTab^.ClassRef[vfe^.TypeIndex - 1]^.ClassName);

  end;
end.

=== code end ===

=== output begin ===

PS C:\fpc\git> .\testoutput\tfield.exe
1 field(s) with 1 type(s)
0 -> fTest @ 8 of type TSub

=== output end ===

Side note: contrary to what I had originally written only classes, but 
not interfaces are allowed for published fields and they need to have 
$M enabled.


Regards,
Sven

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Read Field names from VMT

2024-01-21 Thread Amir--- via fpc-pascal

How can I set the value?
I tried something like

Test := TTest.Create

Ptr := Pointer(Test);
TSub(Ptr+8) := TSub.Create;

But it is not working?
You can use the PVmtFieldTable and PVmtFieldEntry types from the 
TypInfo unit:


=== code begin ===

program tfield;

{$mode objfpc}{$H+}

uses
  TypInfo;

type
  {$M+}
  TSub = class

  end;

  TTest = class
  published
    fTest: TSub;
  end;

var
  vft: PVmtFieldTable;
  vfe: PVmtFieldEntry;
  i: SizeInt;
begin
  vft := PVmtFieldTable(PVMT(TTest)^.vFieldTable);
  Writeln(vft^.Count, ' field(s) with ', vft^.ClassTab^.Count, ' 
type(s)');

  for i := 0 to vft^.Count - 1 do begin
    vfe := vft^.Field[i];
    Writeln(i, ' -> ', vfe^.Name, ' @ ', vfe^.FieldOffset, ' of type 
', vft^.ClassTab^.ClassRef[vfe^.TypeIndex - 1]^.ClassName);

  end;
end.

=== code end ===

=== output begin ===

PS C:\fpc\git> .\testoutput\tfield.exe
1 field(s) with 1 type(s)
0 -> fTest @ 8 of type TSub

=== output end ===

Side note: contrary to what I had originally written only classes, but 
not interfaces are allowed for published fields and they need to have 
$M enabled.


Regards,
Sven

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Read Field names from VMT

2023-12-27 Thread Amir via fpc-pascal
Thank you!On Dec 27, 2023 7:46 AM, Sven Barth via fpc-pascal  wrote:

Am 26.12.2023 um 21:29 schrieb Amir---
  via fpc-pascal:


  
  
  On 12/26/23 01:14, Sven Barth via
fpc-pascal wrote:
  
  


  

  Amir--- via fpc-pascal

schrieb am Di., 26. Dez. 2023, 07:03:
  
  Hi,

   I want to retrieve the name of the fields in a
record/class, at run 
time. It looks like "TVmt.vFieldTable" is what I need.
But I cannot find 
any documentation about how to explore the content of
this table. I 
appreciate any pointer.
  

  
  
  
  This only works for published fields and only
fields of type class or interface can be published. 
  

  
  That would work for me. How can I enumerate over those fields?

You can use the PVmtFieldTable and PVmtFieldEntry types from the
TypInfo unit:

=== code begin ===

program tfield;

{$mode objfpc}{$H+}

uses
  TypInfo;

type
  {$M+}
  TSub = class

  end;

  TTest = class
  published
    fTest: TSub;
  end;

var
  vft: PVmtFieldTable;
  vfe: PVmtFieldEntry;
  i: SizeInt;
begin
  vft := PVmtFieldTable(PVMT(TTest)^.vFieldTable);
  Writeln(vft^.Count, ' field(s) with ', vft^.ClassTab^.Count, '
type(s)');
  for i := 0 to vft^.Count - 1 do begin
    vfe := vft^.Field[i];
    Writeln(i, ' -> ', vfe^.Name, ' @ ', vfe^.FieldOffset, ' of
type ', vft^.ClassTab^.ClassRef[vfe^.TypeIndex - 1]^.ClassName);
  end;
end.

=== code end ===

=== output begin ===

PS C:\fpc\git> .\testoutput\tfield.exe
1 field(s) with 1 type(s)
0 -> fTest @ 8 of type TSub

=== output end ===

Side note: contrary to what I had originally written only classes,
but not interfaces are allowed for published fields and they need to
have $M enabled.

Regards,
Sven
  
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Read Field names from VMT

2023-12-27 Thread Sven Barth via fpc-pascal

Am 26.12.2023 um 21:29 schrieb Amir--- via fpc-pascal:


On 12/26/23 01:14, Sven Barth via fpc-pascal wrote:
Amir--- via fpc-pascal  schrieb am 
Di., 26. Dez. 2023, 07:03:


Hi,

   I want to retrieve the name of the fields in a record/class,
at run
time. It looks like "TVmt.vFieldTable" is what I need. But I
cannot find
any documentation about how to explore the content of this table. I
appreciate any pointer.


This only works for published fields and only fields of type class or 
interface can be published.

That would work for me. How can I enumerate over those fields?


You can use the PVmtFieldTable and PVmtFieldEntry types from the TypInfo 
unit:


=== code begin ===

program tfield;

{$mode objfpc}{$H+}

uses
  TypInfo;

type
  {$M+}
  TSub = class

  end;

  TTest = class
  published
    fTest: TSub;
  end;

var
  vft: PVmtFieldTable;
  vfe: PVmtFieldEntry;
  i: SizeInt;
begin
  vft := PVmtFieldTable(PVMT(TTest)^.vFieldTable);
  Writeln(vft^.Count, ' field(s) with ', vft^.ClassTab^.Count, ' type(s)');
  for i := 0 to vft^.Count - 1 do begin
    vfe := vft^.Field[i];
    Writeln(i, ' -> ', vfe^.Name, ' @ ', vfe^.FieldOffset, ' of type ', 
vft^.ClassTab^.ClassRef[vfe^.TypeIndex - 1]^.ClassName);

  end;
end.

=== code end ===

=== output begin ===

PS C:\fpc\git> .\testoutput\tfield.exe
1 field(s) with 1 type(s)
0 -> fTest @ 8 of type TSub

=== output end ===

Side note: contrary to what I had originally written only classes, but 
not interfaces are allowed for published fields and they need to have $M 
enabled.


Regards,
Sven___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Read Field names from VMT

2023-12-26 Thread Amir--- via fpc-pascal


On 12/26/23 01:14, Sven Barth via fpc-pascal wrote:
Amir--- via fpc-pascal  schrieb am 
Di., 26. Dez. 2023, 07:03:


Hi,

   I want to retrieve the name of the fields in a record/class, at
run
time. It looks like "TVmt.vFieldTable" is what I need. But I
cannot find
any documentation about how to explore the content of this table. I
appreciate any pointer.


This only works for published fields and only fields of type class or 
interface can be published.

That would work for me. How can I enumerate over those fields?

Best,
Amir

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Read Field names from VMT

2023-12-26 Thread Sven Barth via fpc-pascal
Amir--- via fpc-pascal  schrieb am Di.,
26. Dez. 2023, 07:03:

> Hi,
>
>I want to retrieve the name of the fields in a record/class, at run
> time. It looks like "TVmt.vFieldTable" is what I need. But I cannot find
> any documentation about how to explore the content of this table. I
> appreciate any pointer.
>

This only works for published fields and only fields of type class or
interface can be published.

Anything more requires extended RTTI, which is not yet implemented in FPC
(it's already more or less ready in a merge request, but there's still the
one or other issue that needs to be addressed).

Regards,
Sven

>
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal