Recent

Author Topic: IInterface issues, compiler can't find members at compile time. This is GDIPLUS  (Read 1337 times)

jamie

  • Hero Member
  • *****
  • Posts: 6077
I finished an old pet project of making a very common unit floating around for GDIPLUS to work with older fpc.

The compiler in question is 3.0.4. 64bit, windows 10.

I've done IInterface objects before and they worked find but this GDIPLUS or the compiler has a problem with something.
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. var
  3.   I:TGDiPlusStartUpInPut;
  4.   Token:ULong;
  5.   G:TGPGraphics;
  6.   P:TGPPen;
  7.   C:TGPCOlor;
  8.   S:TGPStatus;
  9. begin
  10.   I.Intialize;
  11.   S:=GDiPlusStartUP(Token,@I,Nil);
  12.   G:=TGPGraphics.DCCreate(Canvas.Handle);
  13.   P:=TGPPen.Create(TGPCOlor.BurlyWood,2);
  14.   G.DrawLine(P,0,0,200,100); //<<<<<< Compiler can't find any members of the Interface.
  15.   P.Free;
  16.   G.Free;
  17.   GDIPlusShutdown(Token);
  18. end;                                    
  19.  

 The code above works basically because up to that line I pointed out, it's calling non interface members.
but when it gets to the DRAWLINE or any interface member of G, the graphics interface, the compiler has gone brain dead.

unit1.pas(46,5) Error: identifier idents no member "DrawLine"

 or any member that happens to be an interface method.
Code: Pascal  [Select][+][-]
  1.  
  2.     procedure DrawLine(const Pen: IGPPen; const Pt1, Pt2: TGPPointF); overload;
  3.     procedure DrawLine(const Pen: IGPPen; const X1, Y1, X2, Y2: Single); overload;
  4.     procedure DrawLine(const Pen: IGPPen; const Pt1, Pt2: TGPPoint); overload;
  5.     procedure DrawLine(const Pen: IGPPen; const X1, Y1, X2, Y2: Integer); overload;  
  6.  

As you can see, there are plenty of DrawLine methods there.

Does anyone know why the compiler can't find these methods at compile time?

EDIT:
Attached is a project and I hope the GDIPLUS came with it.
« Last Edit: June 05, 2023, 04:22:01 am by jamie »
The only true wisdom is knowing you know nothing

WooBean

  • Full Member
  • ***
  • Posts: 229
It may help?

This demo is not following environment described by jamie but may point at the real culprit.

Code: Pascal  [Select][+][-]
  1. program project;
  2. {$mode objFPC}
  3. function aa(const a:{int64}integer):boolean; overload;
  4. begin
  5.   Result:=a>0;
  6. end;
  7. function aa(const a:single):boolean; overload;
  8. begin
  9.   Result:=a>0;
  10. end;
  11.  
  12. begin
  13.   writeln(aa(9999999999)); //project.lpr(13,11) Error: Can't determine which overloaded function to call
  14.   ReadLn;
  15. end.
  16.  

Maybe const parameter values (X1, Y1, X2, Y2) passed to
  procedure DrawLine(const Pen: IGPPen; const X1, Y1, X2, Y2: Single)
or
  procedure DrawLine(const Pen: IGPPen; const X1, Y1, X2, Y2: Integer)
are neither integer nor single?

Try
G.DrawLine(P,integer(0),integer(0),integer(200),integer(100));



« Last Edit: June 05, 2023, 08:29:02 am by WooBean »
Platforms: Win7/64, Linux Mint Ulyssa/64

jamie

  • Hero Member
  • *****
  • Posts: 6077
If I access those members within the same unit, "GDIPLUS", then there is no complaint.

But it's not liking the idea of jumping unit boundaries.

The Code Tools have no issues locating any of those members while editing.

 As for the Overload problem, I did have that issue with the constructors, because it couldn't figure out which overload to use. I renamed the problem constructor from CREATE(DC:HDC) to DCCreate(DC:HDC); which solved the problem there.

 I will try casting but I don't think that is the issue because I am not getting that message that it can't decide which overload to use, it just can't find any of the INTERFACE members of the class but non-interface members the compiler can see from other units.


EDIT:
 Casting the constants does not have any effect on the compiler locating the members of the interface.
« Last Edit: June 05, 2023, 01:12:13 pm by jamie »
The only true wisdom is knowing you know nothing

jamie

  • Hero Member
  • *****
  • Posts: 6077
It seems for whatever reason there is a PRIVATE field marker at the start of these Interfaces and the constructors and some other basics are in the PUBLIC section.

Code tools can see all of this but the compiler can not from a remote unit?

I can't say if making these PUBLIC is the correct fix or even why it was like that.

In any case the test project now compiles and executes.

 I'll have to ding into this a little more, something strange indeed.
The only true wisdom is knowing you know nothing

PascalDragon

  • Hero Member
  • *****
  • Posts: 5444
  • Compiler Developer
The compiler sees the TGPGraphics which indeed has DrawLine declared as private, thus it must not provide access to it (the interface isn't taken into account here).

You have to cast TGPGraphics to IGPGraphics so that you can access DrawLine. It's a bug in Lazarus that its CodeTools provide access to DrawLine when used on the TGPGraphics instance.

jamie

  • Hero Member
  • *****
  • Posts: 6077
Ok, well I did some reading on the FPC support sights on interfaces etc, and basically it clearly states not to use Private sections in the definitions of the interface, because, like it states, it makes no sense.

 As for the compiler, yes, I think that is a mistake but as a reminder I am using a slightly dated Lazarus and that issue may not exists in above 2.0.4 I am using.

 The only other issue that couldn't work out was a problem of it not being able to determine which constructor to use so I renamed one of the constructors for now to work around it.
 
 I have made some minor changes to the GDIPLUS file and I will upload a project here with some samples of use.
The only true wisdom is knowing you know nothing

PascalDragon

  • Hero Member
  • *****
  • Posts: 5444
  • Compiler Developer
Ok, well I did some reading on the FPC support sights on interfaces etc, and basically it clearly states not to use Private sections in the definitions of the interface, because, like it states, it makes no sense.

Where does it state so?

And depending on the use case it does indeed make sense: if you want the user to only access these methods using the interface, but not using the class instance itself.

As for the compiler, yes, I think that is a mistake but as a reminder I am using a slightly dated Lazarus and that issue may not exists in above 2.0.4 I am using.

It is not a bug. This is by design (and also the case in Delphi). This becomes especially apparent when you do the following:

Code: Pascal  [Select][+][-]
  1. program tintf;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. type
  6.   IIntf = interface
  7.   ['{FF53A18B-9D3B-4E6D-B597-AB78DF89F878}']
  8.     procedure Test;
  9.   end;
  10.  
  11.   { TImpl }
  12.  
  13.   TImpl = class(TInterfacedObject, IIntf)
  14.     procedure Test;
  15.   end;
  16.  
  17.   { TProvider }
  18.  
  19.   TProvider = class(TObject, IIntf)
  20.   private
  21.     fIntf: IIntf;
  22.   public
  23.     constructor Create;
  24.     property Intf: IIntf read fIntf implements IIntf;
  25.   end;
  26.  
  27. { TImpl }
  28.  
  29. procedure TImpl.Test;
  30. begin
  31.   Writeln('Test');
  32. end;
  33.  
  34. { TProvider }
  35.  
  36. constructor TProvider.Create;
  37. begin
  38.   fIntf := TImpl.Create;
  39. end;
  40.  
  41. var
  42.   p: TProvider;
  43.   i: IIntf;
  44. begin
  45.   p := TProvider.Create;
  46.   // will not compile, because TProvider does not have a Test method
  47.   //p.Test;
  48.   i := p as IIntf;
  49.   i.Test;
  50. end.

It would make no sense for p.Test to compile here, because TProvider does simply not have a Test method. The analogous is true for classes that directly implement the interface as the visibility of a method is dictated by the class, and not the presence of a interface.

 

TinyPortal © 2005-2018