Recent

Author Topic: Helper for interface  (Read 9820 times)

SonnyBoyXXl

  • Jr. Member
  • **
  • Posts: 57
Helper for interface
« on: January 05, 2018, 09:56:57 am »
Hi folks,

Helpers for classes, etc are a good thing. But at the moment not possible for interfaces?

For example I want to declare something like this:
Code: Pascal  [Select][+][-]
  1. TestInterface = interface(IUnknown)
  2.     end;
  3.  
  4. TestInterfaceHelper =  interface helper for TestInterface
  5.       function Test(x: integer):Integer; stdcall;
  6.     end;
  7.  

The Interface Helper extend the interface with the new function.

The DirectX Headers uses much of this decleration type. For example.

Quote
/// <summary>
/// The root brush interface. All brushes can be used to fill or pen a geometry.
/// </summary>
interface DX_DECLARE_INTERFACE("2cd906a8-12e2-11dc-9fed-001143a055f9") ID2D1Brush  : public ID2D1Resource
{
   
    /// <summary>
    /// Sets the opacity for when the brush is drawn over the entire fill of the brush.
    /// </summary>
    STDMETHOD_(void, SetOpacity)(
        FLOAT opacity
        ) PURE;
   
    /// <summary>
    /// Sets the transform that applies to everything drawn by the brush.
    /// </summary>
    STDMETHOD_(void, SetTransform)(
        _In_ CONST D2D1_MATRIX_3X2_F *transform
        ) PURE;
   
    STDMETHOD_(FLOAT, GetOpacity)(
        ) CONST PURE;
   
    STDMETHOD_(void, GetTransform)(
        _Out_ D2D1_MATRIX_3X2_F *transform
        ) CONST PURE;
   
    COM_DECLSPEC_NOTHROW
    void
    SetTransform(
        CONST D2D1_MATRIX_3X2_F &transform
        ) 
    {
        SetTransform(&transform);
    }
}; // interface ID2D1Brush

The procedure SetTransform is overloaded. How can this be done in pascal to declare additional functions to a external interface? The interface helper would be a good thing therefor I think?

My translations of the DirectX headers is now only for the routines exposed by the interface itself. But it would be cool if the whole functionality could be translated.

Thaddy

  • Hero Member
  • *****
  • Posts: 14201
  • Probably until I exterminate Putin.
Re: Helper for interface
« Reply #1 on: January 05, 2018, 10:21:39 am »
here you go:
Code: Pascal  [Select][+][-]
  1. program intfhelpers;
  2. {$mode objfpc}{$modeswitch typehelpers}{$H+}{$I-}
  3. {$IF FPC_FULLVERSION < 30101}{$ERROR 'This needs at least FPC 3.1.1 or higher'}{$ENDIF}
  4. type
  5. ITestInterface = interface(IUnknown)
  6. // probably needs a guid here...
  7. end;
  8.  
  9. TestInterfaceHelper =  type helper for ITestInterface
  10.   function Test(x: integer):Integer; stdcall;
  11. end;
  12.  
  13. TMyClass = class(TInterfacedObject, ITestInterface)
  14. end;
  15.  
  16. function TestInterfaceHelper.Test(x:integer):integer;stdcall;
  17. begin
  18.  Result := x;
  19. end;
  20.  
  21. var c:iTestInterface;
  22. begin
  23.   c := TMyClass.Create;
  24.   writeln(c.Test(100));
  25. end.


But you can also simply overload the functions.
« Last Edit: January 05, 2018, 11:46:32 am by Thaddy »
Specialize a type, not a var.

SonnyBoyXXl

  • Jr. Member
  • **
  • Posts: 57
Re: Helper for interface
« Reply #2 on: January 05, 2018, 10:34:13 am »
On compilation I get the error:
project1.ppr(7,40) Error: Type "ITestInterface" cannot be extended by a type helper


Thaddy

  • Hero Member
  • *****
  • Posts: 14201
  • Probably until I exterminate Putin.
Re: Helper for interface
« Reply #3 on: January 05, 2018, 10:37:30 am »
Compile the newer code above (read it first!) It now throws a warning error. I editted it.(2x)
You need 3.0.4, but maybe the feature is not backported and only in trunk.- 3.1.1.
The feature is only in trunk.
« Last Edit: January 05, 2018, 11:48:55 am by Thaddy »
Specialize a type, not a var.

SonnyBoyXXl

  • Jr. Member
  • **
  • Posts: 57
Re: Helper for interface
« Reply #4 on: January 05, 2018, 10:55:47 am »
Thank You Thaddy for your reply.
I will Test in the evening.
I'm out now for skiing  :D
« Last Edit: January 05, 2018, 08:16:47 pm by SonnyBoyXXl »

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11383
  • FPC developer.
Re: Helper for interface
« Reply #5 on: January 05, 2018, 11:09:56 am »
Compile the newer code above (read it first!) It now throws a warning. I editted it.
You need 3.0.4, but maybe the feature is not backported and only in trunk.- 3.1.1.

Compiler features are (extremely) rarely backported to fixes versions.

Thaddy

  • Hero Member
  • *****
  • Posts: 14201
  • Probably until I exterminate Putin.
Re: Helper for interface
« Reply #6 on: January 05, 2018, 11:45:23 am »
Compiler features are (extremely) rarely backported to fixes versions.
Ok, I will make the example code throw an error instead of a warning.
Specialize a type, not a var.

ASerge

  • Hero Member
  • *****
  • Posts: 2222
Re: Helper for interface
« Reply #7 on: January 05, 2018, 06:54:12 pm »
Helpers for classes, etc are a good thing. But at the moment not possible for interfaces?
I do not really understand why you need a helper for the interface. If you know that the implementation exactly supports the interface function, well, describe it and that's it. This is simply an interface, and the helper is needed for implementation.

Thaddy

  • Hero Member
  • *****
  • Posts: 14201
  • Probably until I exterminate Putin.
Re: Helper for interface
« Reply #8 on: January 05, 2018, 07:11:15 pm »
@ASerge I explained that. And it is possible in trunk. Usability doubtful.
Specialize a type, not a var.

SonnyBoyXXl

  • Jr. Member
  • **
  • Posts: 57
Re: Helper for interface
« Reply #9 on: January 05, 2018, 11:28:56 pm »
Hello Thaddy,

as promised, I tested the option now with the latest trunk - it works !!!  :) :D

I've updated the Direct2D Text-Sample on GitHub with the additions. so everyone could try.
https://github.com/CMCHTPC/DelphiDX12/tree/master/Samples_FPC/Direct2D/TextAnimationSample

Short description what have been changed, and also as idea for ASerge why this is needed.

Defintion of the ID2D1RenderTarget

Code: Pascal  [Select][+][-]
  1. ID2D1RenderTarget = interface(ID2D1Resource)
  2.         ['{2cd90694-12e2-11dc-9fed-001143a055f9}']
  3.         function CreateBitmap(size: TD2D1_SIZE_U; srcData: Pointer; pitch: UINT32; const bitmapProperties: TD2D1_BITMAP_PROPERTIES;
  4.             out bitmap: ID2D1Bitmap): HResult; stdcall;
  5.         function CreateBitmapFromWicBitmap(wicBitmapSource: IWICBitmapSource; bitmapProperties: PD2D1_BITMAP_PROPERTIES;
  6.             out bitmap: ID2D1Bitmap): HResult;
  7.             stdcall;
  8.         function CreateSharedBitmap(const riid: TGUID; Data: Pointer; const bitmapProperties: TD2D1_BITMAP_PROPERTIES;
  9.             out bitmap: ID2D1Bitmap): HResult; stdcall;
  10.         function CreateBitmapBrush(bitmap: ID2D1Bitmap; bitmapBrushProperties: PD2D1_BITMAP_BRUSH_PROPERTIES;
  11.             brushProperties: PD2D1_BRUSH_PROPERTIES; out bitmapBrush: ID2D1BitmapBrush): HResult; stdcall;
  12.         function CreateSolidColorBrush(const color: TD2D1_COLOR_F; brushProperties: PD2D1_BRUSH_PROPERTIES;
  13.             out solidColorBrush: ID2D1SolidColorBrush): HResult;
  14.             stdcall;
  15.         function CreateGradientStopCollection(gradientStops: PD2D1_GRADIENT_STOP; gradientStopsCount: UINT32;
  16.             colorInterpolationGamma: TD2D1_GAMMA; extendMode: TD2D1_EXTEND_MODE;
  17.             out gradientStopCollection: ID2D1GradientStopCollection): HResult; stdcall;
  18.         function CreateLinearGradientBrush(linearGradientBrushProperties: PD2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES;
  19.             brushProperties: PD2D1_BRUSH_PROPERTIES; gradientStopCollection: ID2D1GradientStopCollection;
  20.             out linearGradientBrush: ID2D1LinearGradientBrush): HResult; stdcall;
  21.         function CreateRadialGradientBrush(radialGradientBrushProperties: PD2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES;
  22.             brushProperties: PD2D1_BRUSH_PROPERTIES; gradientStopCollection: ID2D1GradientStopCollection;
  23.             out radialGradientBrush: ID2D1RadialGradientBrush): HResult; stdcall;
  24.         function CreateCompatibleRenderTarget(const desiredSize: TD2D1_SIZE_F; desiredPixelSize: PD2D1_SIZE_U;
  25.             desiredFormat: PD2D1_PIXEL_FORMAT; options: TD2D1_COMPATIBLE_RENDER_TARGET_OPTIONS;
  26.             out bitmapRenderTarget: ID2D1BitmapRenderTarget): HResult; stdcall;
  27.         function CreateLayer(size: PD2D1_SIZE_F; out layer: ID2D1Layer): HResult; stdcall;
  28.         function CreateMesh(out mesh: ID2D1Mesh): HResult; stdcall;
  29.         procedure DrawLine(point0: TD2D1_POINT_2F; point1: TD2D1_POINT_2F; brush: ID2D1Brush; strokeWidth: single = 1.0;
  30.             strokeStyle: ID2D1StrokeStyle = nil);
  31.             stdcall;
  32.         procedure DrawRectangle(const rect: TD2D1_RECT_F; brush: ID2D1Brush; strokeWidth: single = 1.0;
  33.             strokeStyle: ID2D1StrokeStyle = nil); stdcall;
  34.         procedure FillRectangle(const rect: TD2D1_RECT_F; brush: ID2D1Brush); stdcall;
  35.         procedure DrawRoundedRectangle(const roundedRect: TD2D1_ROUNDED_RECT; brush: ID2D1Brush; strokeWidth: single = 1.0;
  36.             strokeStyle: ID2D1StrokeStyle = nil);
  37.             stdcall;
  38.         procedure FillRoundedRectangle(const roundedRect: TD2D1_ROUNDED_RECT; brush: ID2D1Brush); stdcall;
  39.         procedure DrawEllipse(const ellipse: TD2D1_ELLIPSE; brush: ID2D1Brush; strokeWidth: single = 1.0;
  40.             strokeStyle: ID2D1StrokeStyle = nil); stdcall;
  41.         procedure FillEllipse(const ellipse: TD2D1_ELLIPSE; brush: ID2D1Brush); stdcall;
  42.         procedure DrawGeometry(geometry: ID2D1Geometry; brush: ID2D1Brush; strokeWidth: single = 1.0; strokeStyle: ID2D1StrokeStyle = nil); stdcall;
  43.         procedure FillGeometry(geometry: ID2D1Geometry; brush: ID2D1Brush; opacityBrush: ID2D1Brush = nil); stdcall;
  44.         procedure FillMesh(mesh: ID2D1Mesh; brush: ID2D1Brush); stdcall;
  45.         procedure FillOpacityMask(opacityMask: ID2D1Bitmap; brush: ID2D1Brush; content: TD2D1_OPACITY_MASK_CONTENT;
  46.             destinationRectangle: PD2D1_RECT_F = nil; sourceRectangle: PD2D1_RECT_F = nil); stdcall;
  47.         procedure DrawBitmap(bitmap: ID2D1Bitmap; destinationRectangle: PD2D1_RECT_F = nil; opacity: single = 1.0;
  48.             interpolationMode: TD2D1_BITMAP_INTERPOLATION_MODE = D2D1_BITMAP_INTERPOLATION_MODE_LINEAR;
  49.             sourceRectangle: PD2D1_RECT_F = nil); stdcall;
  50.         procedure DrawText(Text: PWideChar; stringLength: UINT32; textFormat: IDWriteTextFormat; layoutRect: PD2D1_RECT_F;
  51.             defaultFillBrush: ID2D1Brush; options: TD2D1_DRAW_TEXT_OPTIONS = D2D1_DRAW_TEXT_OPTIONS_NONE;
  52.             measuringMode: TDWRITE_MEASURING_MODE = DWRITE_MEASURING_MODE_NATURAL); stdcall;
  53.         procedure DrawTextLayout(origin: TD2D1_POINT_2F; textLayout: IDWriteTextLayout; defaultFillBrush: ID2D1Brush;
  54.             options: TD2D1_DRAW_TEXT_OPTIONS = D2D1_DRAW_TEXT_OPTIONS_NONE); stdcall;
  55.         procedure DrawGlyphRun(baselineOrigin: TD2D1_POINT_2F; glyphRun: PDWRITE_GLYPH_RUN; foregroundBrush: ID2D1Brush;
  56.             measuringMode: TDWRITE_MEASURING_MODE = DWRITE_MEASURING_MODE_NATURAL); stdcall;
  57.         procedure SetTransform(const transform: TD2D1_MATRIX_3X2_F); stdcall;
  58.         procedure GetTransform(out transform: TD2D1_MATRIX_3X2_F); stdcall;
  59.         procedure SetAntialiasMode(antialiasMode: TD2D1_ANTIALIAS_MODE); stdcall;
  60.         function GetAntialiasMode(): TD2D1_ANTIALIAS_MODE; stdcall;
  61.         procedure SetTextAntialiasMode(textAntialiasMode: TD2D1_TEXT_ANTIALIAS_MODE); stdcall;
  62.         function GetTextAntialiasMode(): TD2D1_TEXT_ANTIALIAS_MODE; stdcall;
  63.         procedure SetTextRenderingParams(textRenderingParams: IDWriteRenderingParams = nil); stdcall;
  64.         procedure GetTextRenderingParams(out textRenderingParams: IDWriteRenderingParams); stdcall;
  65.         procedure SetTags(tag1: TD2D1_TAG; tag2: TD2D1_TAG); stdcall;
  66.         procedure GetTags(out tag1: TD2D1_TAG; out tag2: TD2D1_TAG); stdcall;
  67.         procedure PushLayer(const layerParameters: TD2D1_LAYER_PARAMETERS; layer: ID2D1Layer); stdcall;
  68.         procedure PopLayer(); stdcall;
  69.         function Flush(out tag1: TD2D1_TAG; out tag2: TD2D1_TAG): HResult; stdcall;
  70.         procedure SaveDrawingState(var drawingStateBlock: ID2D1DrawingStateBlock); stdcall;
  71.         procedure RestoreDrawingState(drawingStateBlock: ID2D1DrawingStateBlock); stdcall;
  72.         procedure PushAxisAlignedClip(const clipRect: TD2D1_RECT_F; antialiasMode: TD2D1_ANTIALIAS_MODE); stdcall;
  73.         procedure PopAxisAlignedClip(); stdcall;
  74.         procedure Clear(const ClearColor: TD2D1_COLOR_F); stdcall;
  75.         procedure BeginDraw(); stdcall;
  76.         function EndDraw(tag1: PD2D1_TAG = nil; Tag2: PD2D1_TAG = nil): HResult; stdcall;
  77.         function GetPixelFormat(): TD2D1_PIXEL_FORMAT; stdcall;
  78.         procedure SetDpi(dpiX: single; dpiY: single); stdcall;
  79.         procedure GetDpi(out dpiX: single; out dpiY: single); stdcall;
  80.         function GetSize(): TD2D1_SIZE_F; stdcall;
  81.         function GetPixelSize(): TD2D1_SIZE_U; stdcall;
  82.  
  83.         function GetMaximumBitmapSize(): UINT32; stdcall;
  84.         function IsSupported(renderTargetProperties: PD2D1_RENDER_TARGET_PROPERTIES): longbool; stdcall;
  85.     end;
  86.  
  87.      
For my example the function definition for CreateSolidColorBrush in the interface is
Quote
STDMETHOD(CreateSolidColorBrush)(
        _In_ CONST D2D1_COLOR_F *color,
        _In_opt_ CONST D2D1_BRUSH_PROPERTIES *brushProperties,
        _COM_Outptr_ ID2D1SolidColorBrush **solidColorBrush
        ) PURE;

This is the function as you see in the above Interface decleration.

But in the original Header there is this additional function defined within the interface
Quote
COM_DECLSPEC_NOTHROW
    HRESULT
    CreateSolidColorBrush(
        CONST D2D1_COLOR_F &color,
        _COM_Outptr_ ID2D1SolidColorBrush **solidColorBrush
        ) 
    {
        return CreateSolidColorBrush(&color, NULL, solidColorBrush);
    }

So I have added this
Code: Pascal  [Select][+][-]
  1.   {$IF FPC_FULLVERSION >= 30101}
  2.      { ID2D1RenderTargetHelper }
  3.      ID2D1RenderTargetHelper =  type helper for ID2D1RenderTarget
  4.          function CreateSolidColorBrush(const color:TD2D1_COLOR_F; out solidColorBrush:ID2D1SolidColorBrush):HRESULT; stdcall; overload;
  5.      end;
  6.      {$ENDIF}  
and added the implementation

Code: Pascal  [Select][+][-]
  1. { ID2D1RenderTargetHelper }
  2. {$IF FPC_FULLVERSION >= 30101}
  3. function ID2D1RenderTargetHelper.CreateSolidColorBrush(
  4.   const color: TD2D1_COLOR_F; out solidColorBrush: ID2D1SolidColorBrush
  5.   ): HRESULT; stdcall;
  6. begin
  7.     result:= CreateSolidColorBrush(color, nil, solidColorBrush);
  8.  end;
  9. {$ENDIF}  

So you can use the function this way

Code: Pascal  [Select][+][-]
  1. if (SUCCEEDED(hr)) then
  2.         begin
  3.             // Nothing in this sample requires antialiasing so we set the antialias
  4.             // mode to aliased up front.
  5.             m_pRT.SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
  6.             //create a black brush
  7.             {$IF FPC_FULLVERSION >= 30101}
  8.             hr := m_pRT.CreateSolidColorBrush(DX12.D2D1.ColorF(DX12.D2D1.Black),{ nil,} m_pBlackBrush); // calling the helper function
  9.             {$ELSE}
  10.              hr := m_pRT.CreateSolidColorBrush(DX12.D2D1.ColorF(DX12.D2D1.Black), nil, m_pBlackBrush);
  11.             {$ENDIF}
  12.         end;  

I know, this is just a simple example and it is just one simple parameter. But there are many functions inside that have
defintion with input values where the default parameter value is the result of another function with standard values. So the usage of the DirectX - interfaces can be simplified.


Great thanks for this nice new feature!

Thaddy

  • Hero Member
  • *****
  • Posts: 14201
  • Probably until I exterminate Putin.
Re: Helper for interface
« Reply #10 on: January 06, 2018, 08:21:48 am »
The feature was implemented by PascalDragon I believe.

You've shown us an actually very good example of its use: If you can't use a default value for a parameter because it is followed by parameters without a default, you can add a typehelper that omits that parameter from its signature and sets it in the body. Since this is a common scenario with MS provided interfaces, using a type helper for an interface is a very clean way to solve such an issue.
Well, well: proper use of a typehelper.. :D :o ::) 8-)
« Last Edit: January 06, 2018, 09:01:48 am by Thaddy »
Specialize a type, not a var.

Thaddy

  • Hero Member
  • *****
  • Posts: 14201
  • Probably until I exterminate Putin.
Re: Helper for interface
« Reply #11 on: January 06, 2018, 12:23:17 pm »
This is also interesting: https://wiert.me/2018/01/04/record-helpers-can-do-wonders-for-code-clarity/
Jeroen obviously misses that feature!
Specialize a type, not a var.

guest58172

  • Guest
Re: Helper for interface
« Reply #12 on: January 06, 2018, 01:40:45 pm »
Helpers for classes, etc are a good thing. But at the moment not possible for interfaces?
I do not really understand why you need a helper for the interface. If you know that the implementation exactly supports the interface function, well, describe it and that's it. This is simply an interface, and the helper is needed for implementation.

Simple things like `if assigned(someInterface)` can be rewritten `if someInterface.isNotNil`. To some extent you can also use them to implement the "Non Virtual Interface" idiom (see https://en.wikipedia.org/wiki/Non-virtual_interface_pattern). Actually i wished more the ability to implement directly an interface method, like in D(see example at the bottom), but interface helpers work too or that purpose: in the interface helper you call several interface method to do things.

Sincerely, with the hope it helps you to understand.

soerensen3

  • Full Member
  • ***
  • Posts: 213
Re: Helper for interface
« Reply #13 on: January 06, 2018, 01:50:29 pm »
Maybe in this case it makes sense because you do have the control of the object. But in my opinion it would be better to define a wrapper class with the interface as a property for direct acess.
Lazarus 1.9 with FPC 3.0.4
Target: Manjaro Linux 64 Bit (4.9.68-1-MANJARO)

Thaddy

  • Hero Member
  • *****
  • Posts: 14201
  • Probably until I exterminate Putin.
Re: Helper for interface
« Reply #14 on: January 06, 2018, 02:21:12 pm »
Maybe in this case it makes sense because you do have the control of the object. But in my opinion it would be better to define a wrapper class with the interface as a property for direct acess.
It is quite the opposite: in this case you do not have control over the interface and a wrapper class would be inappropriate.
In this particular case, given all options, this is close to optimal. Otherwise you would need to write a class wrapper for every class that implements the interface.
« Last Edit: January 06, 2018, 03:03:49 pm by Thaddy »
Specialize a type, not a var.

 

TinyPortal © 2005-2018