* * *

Author Topic: Inherited records  (Read 1636 times)

SonnyBoyXXl

  • New member
  • *
  • Posts: 40
Inherited records
« on: January 13, 2018, 10:22:17 pm »
Hello together,
so my last silly question for today ;D

Is it possible to derive records in FPC (maybe trunk version) like in C++?

For example to translate this
Quote
struct CD3DX12_PACKED_MIP_INFO : public D3D12_PACKED_MIP_INFO{};
to something like this

Code: Pascal  [Select]
  1.  
  2. type
  3.     TD3DX12_PACKED_MIP_INFO = record(TD3D12_PACKED_MIP_INFO)
  4.     end;
  5.  

Thx

SonnyBoy

taazz

  • Hero Member
  • *****
  • Posts: 5036
Re: Inherited records
« Reply #1 on: January 13, 2018, 10:25:12 pm »
the old object construct can be used instead of records for the same purpose.
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

SonnyBoyXXl

  • New member
  • *
  • Posts: 40
Re: Inherited records
« Reply #2 on: January 13, 2018, 10:42:21 pm »
When you have your own record definition (with no external requirements) : yes.

But I'm using header definitions from Microsoft. And struct definied in the C++ .h headers are records in Pascal.
When I'm using external functions in a DLL which have an struct as input parameter this should be a "record", not a "class".
Is a class definied in FPC the "same"  as a struct in C++?



marcov

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 6101
Re: Inherited records
« Reply #3 on: January 13, 2018, 10:43:43 pm »
Afaik object is fairly struct like till you use virtual methods.

Zoran

  • Hero Member
  • *****
  • Posts: 1186
    • http://wiki.lazarus.freepascal.org/User:Zoran
Re: Inherited records
« Reply #4 on: January 13, 2018, 10:48:55 pm »
Yes, unlike classes (whose instances are always created on the heap, meaning they are implicit pointers), objects can be created on the stack, so they are very similar to C++ structs (classes). See: https://www.freepascal.org/docs-html/current/ref/refch5.html

Yes, you can simply use an object instead of record and derive from it -- see this example of inheritance, but add just fields, not methods, and you will get what you asked for.

Edit: important note -- just to clarify -- the word object has two unrelated meanings in Pascal -- The word object is often use with meaning "instance of a class", but this is not what we are talking about here. The objects and classes are two distinct concepts of OOP which Object Pascal has -- see here: http://wiki.freepascal.org/Programming_Using_Objects

« Last Edit: January 13, 2018, 10:55:22 pm by Zoran »

marcov

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 6101
Re: Inherited records
« Reply #5 on: January 13, 2018, 11:23:45 pm »
Only virtual methods, as demonstrated by :

Code: Pascal  [Select]
  1. Type
  2.    DrawingObject = Object
  3.       x, y : single;
  4.       height, width : single;
  5.       procedure Draw;
  6.    end;
  7.  
  8.    DrawingObject2 = Object
  9.       x, y : single;
  10.       height, width : single;
  11.       procedure Draw; virtual;
  12.    end;
  13.  
  14. procedure DrawingObject.Draw; begin end;
  15. procedure DrawingObject2.Draw; begin end;
  16.  
  17. begin  
  18.   writeln(sizeof(drawingobject));                // 16, 4 singles;
  19.   writeln(sizeof(drawingobject2));               // 20 in 32-bit. 4*single + 1 x vmt pointer due to virtual
  20. end.

Thaddy

  • Hero Member
  • *****
  • Posts: 5801
Re: Inherited records
« Reply #6 on: January 14, 2018, 07:43:40 am »
@SonnyBoy:
Your example shows just an empty record.
If it is just a case of naming the C++ record try this:
Code: Pascal  [Select]
  1. type
  2.     TD3DX12_PACKED_MIP_INFO =  TD3D12_PACKED_MIP_INFO;
  3. // or strongly typed:
  4. type
  5.     TD3DX12_PACKED_MIP_INFO = type TD3D12_PACKED_MIP_INFO;
  6.  
Usually such C(++) records are opaque to Pascal code. Therefor in recent trunk a special record and pointer type were added to the system unit: TOpaqueData and OpaquePointer that are for use in just such cases.
You can declare these in the same manner as I described above and the compiler will provide type checking.
 
Code: Pascal  [Select]
  1. type
  2.     PD3D12_PACKED_MIP_INFO = OpaquePointer;  // pointer to TOpaqueData
  3.     TD3D12_PACKED_MIP_INFO  = TOpaqueData;  // declared as an empty "opaque" record
  4.     PD3DX12_PACKED_MIP_INFO = ^TD3DX12_PACKED_MIP_INFO
  5.     TD3DX12_PACKED_MIP_INFO =  TD3D12_PACKED_MIP_INFO;
  6. // or strongly typed:
  7. type
  8.     TD3DX12_PACKED_MIP_INFO = type TD3D12_PACKED_MIP_INFO;
  9.  
This works because we usually need the pointers and not the structure itself and these are now  typed pointers in the same fashion as C++ expects.
You need a very recent trunk for this. 3.1.1-r37942
« Last Edit: January 14, 2018, 08:02:16 am by Thaddy »
recommends {$macro on}{$define Silly:=ObjFpc}

SonnyBoyXXl

  • New member
  • *
  • Posts: 40
Re: Inherited records
« Reply #7 on: January 14, 2018, 10:10:03 am »
Hello Thaddy,

you are right, this was a very simple example.
@marcov and Zoran: objects can't handle unions and class operators (?)

So let me give a more complex example:

Quote
//------------------------------------------------------------------------------
// 2D Vector; 32 bit floating point components
struct XMFLOAT2
{
    float x;
    float y;

    XMFLOAT2() XM_CTOR_DEFAULT
    XM_CONSTEXPR XMFLOAT2(float _x, float _y) : x(_x), y(_y) {}
    explicit XMFLOAT2(_In_reads_(2) const float *pArray) : x(pArray[0]), y(pArray[1]) {}

    XMFLOAT2& operator= (const XMFLOAT2& Float2) { x = Float2.x; y = Float2.y; return *this; }
};

// 2D Vector; 32 bit floating point components aligned on a 16 byte boundary
__declspec(align(16)) struct XMFLOAT2A : public XMFLOAT2
{
    XMFLOAT2A() XM_CTOR_DEFAULT
    XM_CONSTEXPR XMFLOAT2A(float _x, float _y) : XMFLOAT2(_x, _y) {}
    explicit XMFLOAT2A(_In_reads_(2) const float *pArray) : XMFLOAT2(pArray) {}

    XMFLOAT2A& operator= (const XMFLOAT2A& Float2) { x = Float2.x; y = Float2.y; return *this; }
};

//------

So this is my example (maybe not perfect) to convert this to pascal when inherited records are possible:

Code: Pascal  [Select]
  1. type
  2.     TTwoSingleArray = array [0..1] of single;
  3.  
  4.     // 2D Vector; 32 bit floating point components
  5.  
  6.     { TXMFLOAT2 }
  7.  
  8.     TXMFLOAT2 = record
  9.         class operator Initialize(var AFloat2:TXMFLOAT2);
  10.         constructor Create(_x, _y: single);
  11.         class operator Explicit(a: TTwoSingleArray): TXMFLOAT2;
  12.         case integer of
  13.             0: (x: single;
  14.                 y: single);
  15.             1: (f: TTwoSingleArray);
  16.     end;
  17.  
  18.     // 2D Vector; 32 bit floating point components aligned on a 16 byte boundary
  19. {$PACKRECORD 16}
  20.  
  21.     { TXMFLOAT2A }
  22.  
  23.     TXMFLOAT2A = record (TXMFLOAT2)
  24.         class operator Initialize (var AFloat2A:TXMFLOAT2A);
  25.         constructor Create(_x, _y: single);
  26.         class operator Explicit(a: TTwoSingleArray): TXMFLOAT2A;
  27.     end;
  28.  
  29.  
  30. implementation
  31.  
  32. { TXMFLOAT2A }
  33.  
  34. class operator TXMFLOAT2A.Initialize(var AFloat2A: TXMFLOAT2A);
  35. begin
  36.     ZeroMemory(AFloat2A,SizeOf(TXMFLOAT2A));
  37. end;
  38.  
  39. constructor TXMFLOAT2A.Create(_x, _y: single);
  40. begin
  41.     inherited;
  42. end;
  43.  
  44. class operator TXMFLOAT2A.Explicit(a: TTwoSingleArray): TXMFLOAT2A;
  45. begin
  46.     inherited;
  47. end;
  48.  
  49.  
  50. { TXMFLOAT2 }
  51.  
  52. class operator TXMFLOAT2.Initialize(var AFloat2: TXMFLOAT2);
  53. begin
  54.     ZeroMemory(AFloat2,SizeOf(TXMFLOAT2));
  55. end;
  56.  
  57. constructor TXMFLOAT2.Create(_x, _y: single);
  58. begin
  59.     x:=_x;
  60.     y:=_y;
  61. end;
  62.  
  63. class operator TXMFLOAT2.Explicit(a: TTwoSingleArray): TXMFLOAT2;
  64. begin
  65.     Result.f:=a;
  66. end;



struct - public sequences are rare in the MS API as I checked yesterday. But if you have a definition like this

Code: Pascal  [Select]
  1.  TDWRITE_FONT_METRICS = record
  2.         designUnitsPerEm: UINT16;
  3.         ascent: UINT16;
  4.         descent: UINT16;
  5.         lineGap: INT16;
  6.         capHeight: UINT16;
  7.         xHeight: UINT16;
  8.         underlinePosition: INT16;
  9.         underlineThickness: UINT16;
  10.         strikethroughPosition: INT16;
  11.         strikethroughThickness: UINT16;
  12.     end;
  13.  
  14. TDWRITE_FONT_METRICS1 = record
  15.         {TDWRITE_FONT_METRICS}
  16.         designUnitsPerEm: UINT16;
  17.         ascent: UINT16;
  18.         descent: UINT16;
  19.         lineGap: INT16;
  20.         capHeight: UINT16;
  21.         xHeight: UINT16;
  22.         underlinePosition: INT16;
  23.         underlineThickness: UINT16;
  24.         strikethroughPosition: INT16;
  25.         strikethroughThickness: UINT16;
  26.  
  27.      { New }
  28.         glyphBoxLeft: INT16;
  29.  
  30.         glyphBoxTop: INT16;
  31.  
  32.         glyphBoxRight: INT16;
  33.         glyphBoxBottom: INT16;
  34.         subscriptPositionX: INT16;
  35.         subscriptPositionY: INT16;
  36.         subscriptSizeX: INT16;
  37.         subscriptSizeY: INT16;
  38.         superscriptPositionX: INT16;
  39.         superscriptPositionY: INT16;
  40.         superscriptSizeX: INT16;
  41.         superscriptSizeY: INT16;
  42.         hasTypographicMetrics: longbool;
  43.     end;
  44.  

inherited records can save code

Code: Pascal  [Select]
  1. TDWRITE_FONT_METRICS1 = record (TDWRITE_FONT_METRICS)
  2.       { New }
  3.         glyphBoxLeft: INT16;
  4.  
  5.         glyphBoxTop: INT16;
  6.  
  7.         glyphBoxRight: INT16;
  8.         glyphBoxBottom: INT16;
  9.         subscriptPositionX: INT16;
  10.         subscriptPositionY: INT16;
  11.         subscriptSizeX: INT16;
  12.         subscriptSizeY: INT16;
  13.         superscriptPositionX: INT16;
  14.         superscriptPositionY: INT16;
  15.         superscriptSizeX: INT16;
  16.         superscriptSizeY: INT16;
  17.         hasTypographicMetrics: longbool;
  18.     end;
:)



dicepd

  • Full Member
  • ***
  • Posts: 163
Re: Inherited records
« Reply #8 on: January 15, 2018, 08:12:55 pm »
I can think of another case where inheritance in adv records would be really useful.

Code: Pascal  [Select]
  1. TGLZVector4f =  record  
  2. {lots of methods here}
  3.    class operator -(constref A: TGLZVector4f): TGLZVector4f; overload;
  4.     case Byte of
  5.       0: (V: TGLZVector4fType);
  6.       1: (X, Y, Z, W: Single);
  7.       2: (Red, Green, Blue, Alpha: Single);
  8.       3: (AsVector3f : TGLZVector3f);  
  9.       4: (ST,UV : TGLZVector2f);
  10.       5: (Left, Top, Right, Bottom: Single);
  11.       6: (TopLeft,BottomRight : TGLZVector2f);        
  12. end;
  13.  

For most of the 'types' in the accessors negate works fine, the negative of each of the members. However when it comes to using as a homogenous vector it all falls down and needs some way of overriding negate to only negate X,Y,Z.
Currently means whole new record for homogenous or ugly HmgNegate method.
Lazarus 1.8rc5 Win64 / Linux gtk2 64 / FreeBSD qt4

dicepd

  • Full Member
  • ***
  • Posts: 163
Re: Inherited records
« Reply #9 on: January 15, 2018, 09:38:04 pm »
The other way around the above would be to allow operators in record helpers.

Code: Pascal  [Select]
  1. TGLZHmgVector4f = TGLZVector4f;
  2. TGLZVectorHelper = record helper for TGLZVector4f  
  3. public
  4.    class operator -(constref A: TGLZHmgVector4f): TGLZVector4f; overload;
  5. end;
  6.  
  7.  
  8.  
Lazarus 1.8rc5 Win64 / Linux gtk2 64 / FreeBSD qt4

Thaddy

  • Hero Member
  • *****
  • Posts: 5801
Re: Inherited records
« Reply #10 on: January 16, 2018, 07:45:39 am »
Note you should always add the methods *after* the fields, so fields first, then the methods, albeit only to reflect it is still a record.
recommends {$macro on}{$define Silly:=ObjFpc}

dicepd

  • Full Member
  • ***
  • Posts: 163
Re: Inherited records
« Reply #11 on: January 16, 2018, 09:23:16 am »
Note you should always add the methods *after* the fields, so fields first, then the methods, albeit only to reflect it is still a record.

The compiler does not give a hoot about the order, just your convention?
Lazarus 1.8rc5 Win64 / Linux gtk2 64 / FreeBSD qt4

Thaddy

  • Hero Member
  • *****
  • Posts: 5801
Re: Inherited records
« Reply #12 on: January 16, 2018, 09:27:34 am »
Depends. e.g.  field re-ordering. You can not rely on that. Now it is OK, but that is implementation detail. Better to keep it in record order as intended. See all official examples.
« Last Edit: January 16, 2018, 09:30:09 am by Thaddy »
recommends {$macro on}{$define Silly:=ObjFpc}

 

Recent

Get Lazarus at SourceForge.net. Fast, secure and Free Open Source software downloads Open Hub project report for Lazarus