Helper for interface
lazarus / 64-FPC development
----------------------------------------------------------------------
SonnyBoyXXl · 2018-01-05 08:56
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][+][-]window.onload = function(){var x1 =
document.getElementById("main_content_section"); if (x1) { var x =
document.getElementsByClassName("geshi");for (var i = 0; i
TestInterface = interface(IUnknown) end; TestInterfaceHelper =
interface helper for TestInterface function Test(x:
integer):Integer; stdcall; end;
The Interface Helper extend the interface with the new function.
The DirectX Headers uses much of this decleration type. For example.
Quote///
/// The root brush interface. All brushes can be used to fill or pen a
geometry.
///
interface DX_DECLARE_INTERFACE("2cd906a8-12e2-11dc-9fed-001143a055f9")
ID2D1Brush : public ID2D1Resource
{
///
/// Sets the opacity for when the brush is drawn over the entire
fill of the brush.
///
STDMETHOD_(void, SetOpacity)(
FLOAT opacity
) PURE;
///
/// Sets the transform that applies to everything drawn by the
brush.
///
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 · 2018-01-05 09:21
here you go:
Code: Pascal [Select][+][-]window.onload = function(){var x1 =
document.getElementById("main_content_section"); if (x1) { var x =
document.getElementsByClassName("geshi");for (var i = 0; i program
intfhelpers;{$mode objfpc}{$modeswitch typehelpers}{$H+}{$I-}{$IF
FPC_FULLVERSION < 30101}{$ERROR 'This needs at least FPC 3.1.1 or
higher'}{$ENDIF}type ITestInterface = interface(IUnknown)// probably
needs a guid here...end; TestInterfaceHelper = type helper for
ITestInterface function Test(x: integer):Integer;
stdcall;end; TMyClass = class(TInterfacedObject,
ITestInterface)end; function
TestInterfaceHelper.Test(x:integer):integer;stdcall;begin Result :=
x;end; var c:iTestInterface; begin c := TMyClass.Create;
writeln(c.Test(100));end.
But you can also simply overload the functions.
----------------------------------------------------------------------
SonnyBoyXXl · 2018-01-05 09:34
On compilation I get the error:
project1.ppr(7,40) Error: Type "ITestInterface" cannot be extended by
a type helper
----------------------------------------------------------------------
Thaddy · 2018-01-05 09:37
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.
----------------------------------------------------------------------
SonnyBoyXXl · 2018-01-05 09:55
Thank You Thaddy for your reply.
I will Test in the evening.
I'm out now for skiing
----------------------------------------------------------------------
marcov · 2018-01-05 10:09
Quote from: Thaddy on January 05, 2018, 10:37:30 amCompile 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 · 2018-01-05 10:45
Quote from: marcov on January 05, 2018, 11:09:56 amCompiler features
are (extremely) rarely backported to fixes versions.
Ok, I will make the example code throw an error instead of a warning.
----------------------------------------------------------------------
ASerge · 2018-01-05 17:54
Quote from: SonnyBoyXXl on January 05, 2018, 09:56:57 amHelpers 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 · 2018-01-05 18:11
@ASerge I explained that. And it is possible in trunk. Usability
doubtful.
----------------------------------------------------------------------
SonnyBoyXXl · 2018-01-05 22:28
Hello Thaddy,
as promised, I tested the option now with the latest trunk - it works
!!!
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][+][-]window.onload = function(){var x1 =
document.getElementById("main_content_section"); if (x1) { var x =
document.getElementsByClassName("geshi");for (var i = 0; i
ID2D1RenderTarget = interface(ID2D1Resource)
['{2cd90694-12e2-11dc-9fed-001143a055f9}'] function
CreateBitmap(size: TD2D1_SIZE_U; srcData: Pointer; pitch: UINT32;
const bitmapProperties: TD2D1_BITMAP_PROPERTIES; out
bitmap: ID2D1Bitmap): HResult; stdcall; function
CreateBitmapFromWicBitmap(wicBitmapSource: IWICBitmapSource;
bitmapProperties: PD2D1_BITMAP_PROPERTIES; out bitmap:
ID2D1Bitmap): HResult; stdcall; function
CreateSharedBitmap(const riid: TGUID; Data: Pointer; const
bitmapProperties: TD2D1_BITMAP_PROPERTIES; out bitmap:
ID2D1Bitmap): HResult; stdcall; function
CreateBitmapBrush(bitmap: ID2D1Bitmap; bitmapBrushProperties:
PD2D1_BITMAP_BRUSH_PROPERTIES; brushProperties:
PD2D1_BRUSH_PROPERTIES; out bitmapBrush: ID2D1BitmapBrush): HResult;
stdcall; function CreateSolidColorBrush(const color:
TD2D1_COLOR_F; brushProperties: PD2D1_BRUSH_PROPERTIES; out
solidColorBrush: ID2D1SolidColorBrush): HResult; stdcall;
function CreateGradientStopCollection(gradientStops:
PD2D1_GRADIENT_STOP; gradientStopsCount: UINT32;
colorInterpolationGamma: TD2D1_GAMMA; extendMode: TD2D1_EXTEND_MODE;
out gradientStopCollection: ID2D1GradientStopCollection):
HResult; stdcall; function
CreateLinearGradientBrush(linearGradientBrushProperties:
PD2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES; brushProperties:
PD2D1_BRUSH_PROPERTIES; gradientStopCollection:
ID2D1GradientStopCollection; out linearGradientBrush:
ID2D1LinearGradientBrush): HResult; stdcall; function
CreateRadialGradientBrush(radialGradientBrushProperties:
PD2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES; brushProperties:
PD2D1_BRUSH_PROPERTIES; gradientStopCollection:
ID2D1GradientStopCollection; out radialGradientBrush:
ID2D1RadialGradientBrush): HResult; stdcall; function
CreateCompatibleRenderTarget(const desiredSize: TD2D1_SIZE_F;
desiredPixelSize: PD2D1_SIZE_U; desiredFormat:
PD2D1_PIXEL_FORMAT; options: TD2D1_COMPATIBLE_RENDER_TARGET_OPTIONS;
out bitmapRenderTarget: ID2D1BitmapRenderTarget): HResult;
stdcall; function CreateLayer(size: PD2D1_SIZE_F; out layer:
ID2D1Layer): HResult; stdcall; function CreateMesh(out mesh:
ID2D1Mesh): HResult; stdcall; procedure DrawLine(point0:
TD2D1_POINT_2F; point1: TD2D1_POINT_2F; brush: ID2D1Brush;
strokeWidth: single = 1.0; strokeStyle: ID2D1StrokeStyle =
nil); stdcall; procedure DrawRectangle(const rect:
TD2D1_RECT_F; brush: ID2D1Brush; strokeWidth: single = 1.0;
strokeStyle: ID2D1StrokeStyle = nil); stdcall; procedure
FillRectangle(const rect: TD2D1_RECT_F; brush: ID2D1Brush); stdcall;
procedure DrawRoundedRectangle(const roundedRect:
TD2D1_ROUNDED_RECT; brush: ID2D1Brush; strokeWidth: single = 1.0;
strokeStyle: ID2D1StrokeStyle = nil); stdcall;
procedure FillRoundedRectangle(const roundedRect: TD2D1_ROUNDED_RECT;
brush: ID2D1Brush); stdcall; procedure DrawEllipse(const
ellipse: TD2D1_ELLIPSE; brush: ID2D1Brush; strokeWidth: single = 1.0;
strokeStyle: ID2D1StrokeStyle = nil); stdcall;
procedure FillEllipse(const ellipse: TD2D1_ELLIPSE; brush:
ID2D1Brush); stdcall; procedure DrawGeometry(geometry:
ID2D1Geometry; brush: ID2D1Brush; strokeWidth: single = 1.0;
strokeStyle: ID2D1StrokeStyle = nil); stdcall; procedure
FillGeometry(geometry: ID2D1Geometry; brush: ID2D1Brush; opacityBrush:
ID2D1Brush = nil); stdcall; procedure FillMesh(mesh: ID2D1Mesh;
brush: ID2D1Brush); stdcall; procedure
FillOpacityMask(opacityMask: ID2D1Bitmap; brush: ID2D1Brush; content:
TD2D1_OPACITY_MASK_CONTENT; destinationRectangle:
PD2D1_RECT_F = nil; sourceRectangle: PD2D1_RECT_F = nil); stdcall;
procedure DrawBitmap(bitmap: ID2D1Bitmap; destinationRectangle:
PD2D1_RECT_F = nil; opacity: single = 1.0;
interpolationMode: TD2D1_BITMAP_INTERPOLATION_MODE =
D2D1_BITMAP_INTERPOLATION_MODE_LINEAR; sourceRectangle:
PD2D1_RECT_F = nil); stdcall; procedure DrawText(Text:
PWideChar; stringLength: UINT32; textFormat: IDWriteTextFormat;
layoutRect: PD2D1_RECT_F; defaultFillBrush: ID2D1Brush;
options: TD2D1_DRAW_TEXT_OPTIONS = D2D1_DRAW_TEXT_OPTIONS_NONE;
measuringMode: TDWRITE_MEASURING_MODE =
DWRITE_MEASURING_MODE_NATURAL); stdcall; procedure
DrawTextLayout(origin: TD2D1_POINT_2F; textLayout: IDWriteTextLayout;
defaultFillBrush: ID2D1Brush; options:
TD2D1_DRAW_TEXT_OPTIONS = D2D1_DRAW_TEXT_OPTIONS_NONE); stdcall;
procedure DrawGlyphRun(baselineOrigin: TD2D1_POINT_2F; glyphRun:
PDWRITE_GLYPH_RUN; foregroundBrush: ID2D1Brush;
measuringMode: TDWRITE_MEASURING_MODE =
DWRITE_MEASURING_MODE_NATURAL); stdcall; procedure
SetTransform(const transform: TD2D1_MATRIX_3X2_F); stdcall;
procedure GetTransform(out transform: TD2D1_MATRIX_3X2_F); stdcall;
procedure SetAntialiasMode(antialiasMode: TD2D1_ANTIALIAS_MODE);
stdcall; function GetAntialiasMode(): TD2D1_ANTIALIAS_MODE;
stdcall; procedure SetTextAntialiasMode(textAntialiasMode:
TD2D1_TEXT_ANTIALIAS_MODE); stdcall; function
GetTextAntialiasMode(): TD2D1_TEXT_ANTIALIAS_MODE; stdcall;
procedure SetTextRenderingParams(textRenderingParams:
IDWriteRenderingParams = nil); stdcall; procedure
GetTextRenderingParams(out textRenderingParams:
IDWriteRenderingParams); stdcall; procedure SetTags(tag1:
TD2D1_TAG; tag2: TD2D1_TAG); stdcall; procedure GetTags(out
tag1: TD2D1_TAG; out tag2: TD2D1_TAG); stdcall; procedure
PushLayer(const layerParameters: TD2D1_LAYER_PARAMETERS; layer:
ID2D1Layer); stdcall; procedure PopLayer(); stdcall;
function Flush(out tag1: TD2D1_TAG; out tag2: TD2D1_TAG): HResult;
stdcall; procedure SaveDrawingState(var drawingStateBlock:
ID2D1DrawingStateBlock); stdcall; procedure
RestoreDrawingState(drawingStateBlock: ID2D1DrawingStateBlock);
stdcall; procedure PushAxisAlignedClip(const clipRect:
TD2D1_RECT_F; antialiasMode: TD2D1_ANTIALIAS_MODE); stdcall;
procedure PopAxisAlignedClip(); stdcall; procedure Clear(const
ClearColor: TD2D1_COLOR_F); stdcall; procedure BeginDraw();
stdcall; function EndDraw(tag1: PD2D1_TAG = nil; Tag2:
PD2D1_TAG = nil): HResult; stdcall; function GetPixelFormat():
TD2D1_PIXEL_FORMAT; stdcall; procedure SetDpi(dpiX: single;
dpiY: single); stdcall; procedure GetDpi(out dpiX: single; out
dpiY: single); stdcall; function GetSize(): TD2D1_SIZE_F;
stdcall; function GetPixelSize(): TD2D1_SIZE_U; stdcall;
function GetMaximumBitmapSize(): UINT32; stdcall; function
IsSupported(renderTargetProperties: PD2D1_RENDER_TARGET_PROPERTIES):
longbool; stdcall; end; For my example the function
definition for CreateSolidColorBrush in the interface is
QuoteSTDMETHOD(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
QuoteCOM_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][+][-]window.onload = function(){var x1 =
document.getElementById("main_content_section"); if (x1) { var x =
document.getElementsByClassName("geshi");for (var i = 0; i {$IF
FPC_FULLVERSION >= 30101} { ID2D1RenderTargetHelper }
ID2D1RenderTargetHelper = type helper for ID2D1RenderTarget
function CreateSolidColorBrush(const color:TD2D1_COLOR_F; out
solidColorBrush:ID2D1SolidColorBrush):HRESULT; stdcall; overload;
end; {$ENDIF} and added the implementation
Code: Pascal [Select][+][-]window.onload = function(){var x1 =
document.getElementById("main_content_section"); if (x1) { var x =
document.getElementsByClassName("geshi");for (var i = 0; i {
ID2D1RenderTargetHelper }{$IF FPC_FULLVERSION >= 30101}function
ID2D1RenderTargetHelper.CreateSolidColorBrush( const color:
TD2D1_COLOR_F; out solidColorBrush: ID2D1SolidColorBrush ): HRESULT;
stdcall;begin result:= CreateSolidColorBrush(color, nil,
solidColorBrush); end;{$ENDIF}
So you can use the function this way
Code: Pascal [Select][+][-]window.onload = function(){var x1 =
document.getElementById("main_content_section"); if (x1) { var x =
document.getElementsByClassName("geshi");for (var i = 0; i if
(SUCCEEDED(hr)) then begin // Nothing in this sample
requires antialiasing so we set the antialias // mode to
aliased up front.
m_pRT.SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
//create a black brush {$IF FPC_FULLVERSION >= 30101}
hr :=
m_pRT.CreateSolidColorBrush(DX12.D2D1.ColorF(DX12.D2D1.Black),{ nil,}
m_pBlackBrush); // calling the helper function {$ELSE}
hr :=
m_pRT.CreateSolidColorBrush(DX12.D2D1.ColorF(DX12.D2D1.Black), nil,
m_pBlackBrush); {$ENDIF} 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 · 2018-01-06 07:21
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..
----------------------------------------------------------------------
Thaddy · 2018-01-06 11:23
This is also interesting: https://wiert.me/2018/01/04/record-helpers-
can-do-wonders-for-code-clarity/
Jeroen obviously misses that feature!
----------------------------------------------------------------------
guest58172 · 2018-01-06 12:40
Quote from: ASerge on January 05, 2018, 06:54:12 pmQuote from:
SonnyBoyXXl on January 05, 2018, 09:56:57 amHelpers 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 · 2018-01-06 12:50
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.
----------------------------------------------------------------------
Thaddy · 2018-01-06 13:21
Quote from: soerensen3 on January 06, 2018, 01:50:29 pmMaybe 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.
----------------------------------------------------------------------
soerensen3 · 2018-01-06 21:17
Direct X uses interfaces to pass for example a device. As far as I
know one interface is never mixed with another type of interface but I
might be wrong about that. It is merely used as a way of communication
between DX and the application. You cannot change the methods of it
with inheritance because you only have the interface though. Therefore
the helper or the wrapper.
----------------------------------------------------------------------
Thaddy · 2018-01-08 09:16
Quote from: soerensen3 on January 06, 2018, 10:17:47 pmDirect X uses
interfaces to pass for example a device. As far as I know one
interface is never mixed with another type of interface but I might be
wrong about that. It is merely used as a way of communication between
DX and the application. You cannot change the methods of it with
inheritance because you only have the interface though. Therefore the
helper or the wrapper.
No, the general case for an interface helper is a single type helper
for an interface as opposed to multiple wrappers for the classes that
support it.... So it is not
"the helper or the wrapper" but "the helper or the wrappers".
The ratio is that interfaces can be supported by classes that are
otherwise completely unrelated.
I am kind of lazy, I prefer the interface helper. Get the point?
Otherwise I will provide a simple example why this is the case.
A second ratio is, by application of the above:
If you examine the example that is now solved you will see that there
is a parameter with a default value *before* a parameter without a
default. Object Pascal does not support this, but with an interface
helper we do....And actually in a far more readable way.
Technically it has no impact on the actual interface. It has only
impact on the way the interface can be called. The default value
somewhere in the middle of the parameter list is solved by the type
helper.
Even for any unrelated class that supports the original interface...
that contains a default in its definition... That's why it is so
powerful.
Side note: I originally disapproved of this feature, but now I am
convinced.
----------------------------------------------------------------------
PascalDragon · 2018-01-10 10:43
Quote from: SonnyBoyXXl on January 05, 2018, 11:28:56 pmSo I have
added this
Code: Pascal [Select][+][-]window.onload = function(){var x1 =
document.getElementById("main_content_section"); if (x1) { var x =
document.getElementsByClassName("geshi");for (var i = 0; i {$IF
FPC_FULLVERSION >= 30101} { ID2D1RenderTargetHelper }
ID2D1RenderTargetHelper = type helper for ID2D1RenderTarget
function CreateSolidColorBrush(const color:TD2D1_COLOR_F; out
solidColorBrush:ID2D1SolidColorBrush):HRESULT; stdcall; overload;
end; {$ENDIF} and added the implementation
Code: Pascal [Select][+][-]window.onload = function(){var x1 =
document.getElementById("main_content_section"); if (x1) { var x =
document.getElementsByClassName("geshi");for (var i = 0; i {
ID2D1RenderTargetHelper }{$IF FPC_FULLVERSION >= 30101}function
ID2D1RenderTargetHelper.CreateSolidColorBrush( const color:
TD2D1_COLOR_F; out solidColorBrush: ID2D1SolidColorBrush ): HRESULT;
stdcall;begin result:= CreateSolidColorBrush(color, nil,
solidColorBrush); end;{$ENDIF}
Note 1: you don't need to declare the method in the helper as
"stdcall"
Note 2: you can declare the method in the helper as "inline" so that
the compiler will completely omit the call to the helper's method and
directly call the interface's method instead.
----------------------------------------------------------------------
Thaddy · 2018-01-10 11:14
I didn't realize that (Your two remarks) Sven, but anyway he has a
good example why it is useful, don't you think? Thanks for
implementing it.
----------------------------------------------------------------------
PascalDragon · 2018-01-13 12:56
Quote from: Thaddy on January 10, 2018, 12:14:08 pmI didn't realize
that (Your two remarks) Sven, but anyway he has a good example why it
is useful, don't you think? Thanks for implementing it.
Definitely. And examples like this are one of the reasons I
implemented it.