Lazarus

Free Pascal => FPC development => Topic started by: SonnyBoyXXl on January 13, 2018, 10:22:17 pm

Title: Inherited records
Post by: SonnyBoyXXl 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
Title: Re: Inherited records
Post by: taazz on January 13, 2018, 10:25:12 pm
the old object construct can be used instead of records for the same purpose.
Title: Re: Inherited records
Post by: SonnyBoyXXl 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++?


Title: Re: Inherited records
Post by: marcov on January 13, 2018, 10:43:43 pm
Afaik object is fairly struct like till you use virtual methods.
Title: Re: Inherited records
Post by: Zoran 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 (http://wiki.freepascal.org/Programming_Using_Objects#Objects_-_Static_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

Title: Re: Inherited records
Post by: marcov 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.
Title: Re: Inherited records
Post by: Thaddy 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
Title: Re: Inherited records
Post by: SonnyBoyXXl 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;
:)


Title: Re: Inherited records
Post by: dicepd 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.
Title: Re: Inherited records
Post by: dicepd 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.  
Title: Re: Inherited records
Post by: Thaddy 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.
Title: Re: Inherited records
Post by: dicepd 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?
Title: Re: Inherited records
Post by: Thaddy 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.
TinyPortal © 2005-2018