'treat private like protected' lazarus / 64-FPC development ---------------------------------------------------------------------- SymbolicFrank · 2022-10-26 11:12 Can we get an option for that? So we can fix bugs and extend the functionality of existing classes, without having to modify the FPC source code and make a merge request? As an example, I want to expand the TSQLConnector to pass the TSQLite3Connection.OpenFlags. But the encapsulated TSQLConnection object (FProxy) is private, so that's a no-go. ---------------------------------------------------------------------- marcov · 2022-10-26 11:31 http://talkdelphi.blogspot.com/2009/04/how-to-hack-into-delphi- class.html ---------------------------------------------------------------------- SymbolicFrank · 2022-10-26 12:01 Interesting. But I'm probably doing it wrong: 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 type  TSpecialConnector = class(TSQLConnector)  protected    procedure DoInternalConnect; override;  end;   TConnectionAccessor = class  public    FProxy: TSQLite3Connection;  end; implementation procedure TSpecialConnector.DoInternalConnect;begin  if ConnectorType = 'SQLite3' then    TConnectionAccessor(Self).FProxy.OpenFlags :=      [sofReadWrite, sofCreate, sofFullMutex, sofSharedCache];  inherited DoInternalConnect;end; This gives the compile error: Code: [Select]tablefunctions.pas(1473,5) Error: Class or Object types "TSpecialConnector" and "TConnectionAccessor" are not related ---------------------------------------------------------------------- Zvoni · 2022-10-26 13:19 ? 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 TConnectionAccessor = class(TSpecialConnector)  public    FProxy: TSQLite3Connection;  end; EDIT: Related to your other thread: Have you tried by checking the ConnectionDef of the FProxy? FProxy is a TSQLConnection, but you could try casting it AIRCODE 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 procedure TSpecialConnector.DoInternalConnect;begin  if ConnectorType = 'SQLite3' then    TSQLite3Connection(Self).FProxy.OpenFlags :=      [sofReadWrite, sofCreate, sofFullMutex, sofSharedCache];  inherited DoInternalConnect;//blablabla.... and i was under the impression one should call the inherited Method first in this case... 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 procedure TSQLConnector.DoInternalConnect; Var  D : TConnectionDef; begin  inherited DoInternalConnect;  CheckProxy;  FProxy.CharSet:=Self.CharSet;  FProxy.DatabaseName:=Self.DatabaseName; ---------------------------------------------------------------------- Martin_fr · 2022-10-26 13:24 Also keep in mind: https://wiki.freepascal.org/FPC_New_Features_3.0.0# Class_field_reordering QuoteSince the internal memory layout of a class is opaqueIn other words, whatever works now (with current FPC versions), it may stop work with any upcoming FPC version. (By either this, or by other optimizations / even by changes that aren't optimizations). Quotethis optimization may be moved to level -O2While FPC 3.0.0 only does this in -O4, it is already announced to come to -O2 (and we are past 3.0.0 ...) ---------------------------------------------------------------------- SymbolicFrank · 2022-10-26 14:07 Quote from: Zvoni on October 26, 2022, 03:19:05 pm? 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 TConnectionAccessor = class(TSpecialConnector)  public    FProxy: TSQLite3Connection;  end; That gives an 'invalid type cast' runtime error. I'm not sure, but I was under the impression that if you introduce a field with the same name of an inaccessible one, it creates a new field. But it's hard to find the documentation for that. QuoteEDIT: Related to your other thread: Have you tried by checking the ConnectionDef of the FProxy? FProxy is a TSQLConnection, but you could try casting it AIRCODE 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 procedure TSpecialConnector.DoInternalConnect;begin  if ConnectorType = 'SQLite3' then    TSQLite3Connection(Self).FProxy.OpenFlags :=      [sofReadWrite, sofCreate, sofFullMutex, sofSharedCache];  inherited DoInternalConnect;//blablabla....  Well, yes, but I cannot access FProxy, so far. The above code generates the 'not related' or 'invalid typecast' error, depending. Quoteand i was under the impression one should call the inherited Method first in this case... 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 procedure TSQLConnector.DoInternalConnect; Var  D : TConnectionDef; begin  inherited DoInternalConnect;  CheckProxy;  FProxy.CharSet:=Self.CharSet;  FProxy.DatabaseName:=Self.DatabaseName; Ok, I'll try that as soon as I can set those OpenFlags. But it's possible I don't understand what I'm doing. ---------------------------------------------------------------------- SymbolicFrank · 2022-10-26 14:08 Quote from: Martin_fr on October 26, 2022, 03:24:24 pmAlso keep in mind: https://wiki.freepascal.org/FPC_New_Features_3.0.0#Class_field_r eordering QuoteSince the internal memory layout of a class is opaqueIn other words, whatever works now (with current FPC versions), it may stop work with any upcoming FPC version. (By either this, or by other optimizations / even by changes that aren't optimizations). Quotethis optimization may be moved to level -O2While FPC 3.0.0 only does this in -O4, it is already announced to come to -O2 (and we are past 3.0.0 ...) Or, a hack like the above ones isn't going to keep on working, if I can get it functioning at all. Back to the drawing board. ---------------------------------------------------------------------- Zvoni · 2022-10-26 14:32 This compiles 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 Project1;Uses Sysutils, Classes, sqldb, sqlite3conn; Type   { TMyConnector }  TMyConnector = Class(TSQLConnector)    Protected      FProxy:TSQLConnection;  end;   { TMyHelper }   TMyHelper = Class helper for TMyConnector    Public      Procedure SetOpenFlags;  end; { TMyHelper } Procedure DoSomethingElse;BeginEnd; procedure TMyHelper.SetOpenFlags;begin  If ConnectorType='SQLite3' Then    TSQLite3Connection(FProxy).OpenFlags:=[sofReadWrite, sofCreate, sofFullMutex, sofSharedCache];  If ConnectorType='MySQL' Then DoSomethingElse;end;   beginend. ---------------------------------------------------------------------- SymbolicFrank · 2022-10-26 14:44 It does But it gives the error below: Tomorrow I'll look further. ---------------------------------------------------------------------- ASerge · 2022-10-26 15:57 Quote from: SymbolicFrank on October 26, 2022, 01:12:41 pmAs an example, I want to expand the TSQLConnector to pass the TSQLite3Connection.OpenFlags. But the encapsulated TSQLConnection object (FProxy) is private, so that's a no-go. May be 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 uses SQLDB, SQLite3Conn; type  TSpecialConnector = class(TSQLConnector)  protected    procedure CreateProxy; override;  end; procedure TSpecialConnector.CreateProxy;begin  inherited;  if Proxy is TSQLite3Connection then    TSQLite3Connection(Proxy).OpenFlags := [sofReadWrite, sofCreate, sofFullMutex, sofSharedCache];end; ---------------------------------------------------------------------- Zvoni · 2022-10-27 06:57 This compiles and works. No Idea about "SCHEMA HAS CHANGED" 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 project1;Uses Sysutils, Classes, sqldb, sqlite3conn; Type   { TMyConnector }  TMyConnector = Class(TSQLConnector)    Protected      Procedure SetOpenFlags;  end;Var  cn1,cn2:TMyConnector;  ta1,ta2:TSQLTransaction;  qr1,qr2:TSQLQuery;Procedure DoSomethingElse;BeginEnd; procedure TMyConnector.SetOpenFlags;begin  If ConnectorType='SQLite3' Then    TSQLite3Connection(Proxy).OpenFlags:=[sofReadWrite, sofCreate, sofFullMutex, sofSharedCache];  If ConnectorType='MySQL' Then DoSomethingElse;end; begin  cn1:=TMyConnector.Create(Nil);  cn1.ConnectorType:='SQLite3';  cn1.DatabaseName:='test.db';  ta1:=TSQLTransaction.Create(Nil);  cn1.Transaction:=ta1;  qr1:=TSQLQuery.Create(Nil);  qr1.DataBase:=cn1;  qr1.Transaction:=ta1;  cn1.SetOpenFlags;  cn1.Open;  cn1.ExecuteDirect('CREATE TABLE IF NOT EXISTS "tbl_test" ("ID" INT, "SomeText" TEXT);');  ta1.commit;     qr1.free;  ta1.free;  cn1.free;end.Thanks to Serge i noticed i missed, that there is a (Public?) Property "Proxy" (Not FProxy!) ---------------------------------------------------------------------- MarkMLl · 2022-10-27 11:15 Can I ask a supplementary question here please. I've got a vague recollection from the comparatively early Delphi days that one could "surface" something hidden in an ancestor class simply by redefining it with lower protection: is this accurate and if so what was its applicability... protected to public? MarkMLl ---------------------------------------------------------------------- marcov · 2022-10-27 11:48 Quote from: MarkMLl on October 27, 2022, 01:15:42 pmCan I ask a supplementary question here please. I've got a vague recollection from the comparatively early Delphi days that one could "surface" something hidden in an ancestor class simply by redefining it with lower protection: is this accurate and if so what was its applicability... protected to public? This is documented behaviour for properties (but afaik not for fields) ---------------------------------------------------------------------- MarkMLl · 2022-10-27 12:19 Do you have any references for that? I've been looking via Google with limited success. MarkMLl ---------------------------------------------------------------------- Zvoni · 2022-10-27 12:48 Quote from: MarkMLl on October 27, 2022, 02:19:04 pmDo you have any references for that? I've been looking via Google with limited success. MarkMLl Here? https://docwiki.embarcadero.com/RADStudio/Alexandria/en/Properties_(De lphi)#Property_Overrides_and_Redeclarations ---------------------------------------------------------------------- MarkMLl · 2022-10-27 13:04 Yes, I think that's it... thanks very much. I was having a lot of trouble with "surfacing" etc., and was hesitant about equating it with "override" since it already has a specific meaning. So basically, that boosts a protected property up to a public one, and (presumably) there isn't a trivial/safe way of directly boosting private to protected or public (OP's question). MarkMLl ---------------------------------------------------------------------- Thaddy · 2022-10-27 15:52 Quote from: MarkMLl on October 27, 2022, 03:04:09 pmSo basically, that boosts a protected property up to a public one, and (presumably) there isn't a trivial/safe way of directly boosting private to protected or public (OP's question). - A trivial solution is a derived class as suggested. Basic OOP and safe. - Using fields that has getters/setters for access through a property means you should never access the field directly: that depends on the complexity of the setter/getter. Not safe. - private has unit scope, not class scope (strict private has class scope) so you can also add a simple plain function to that unit. Safe/unsafe depending on programmers brains. - the old hacks are not a good idea as explained above. (But I still use it in some Delphi code  ) Unsafe, - Promotion of visibility is always possible, demotion is not (easy). Safe. Basically the language should take offence of your remark. ---------------------------------------------------------------------- SymbolicFrank · 2022-10-28 05:57 Oh, wow, there is a property   How did I miss that? I feel very stupid  But, it works, so I'm happy ---------------------------------------------------------------------- SymbolicFrank · 2022-10-28 06:02 Quote from: Thaddy on October 27, 2022, 05:52:51 pm- A trivial solution is a derived class as suggested. Basic OOP and safe. So, if you add a public field with the same name as an inaccessible private one, does it make that field public, or does it create a new field? Testing seems to indicate the latter. Or does it has to be in the same source file to work, as private fields are accessible there? ---------------------------------------------------------------------- PascalDragon · 2022-10-28 14:45 Quote from: SymbolicFrank on October 28, 2022, 08:02:26 amQuote from: Thaddy on October 27, 2022, 05:52:51 pm- A trivial solution is a derived class as suggested. Basic OOP and safe. So, if you add a public field with the same name as an inaccessible private one, does it make that field public, or does it create a new field? Testing seems to indicate the latter. Or does it has to be in the same source file to work, as private fields are accessible there? It will be a new field. Always. (Except in the same class, because there you can't have two fields with the same name ) ---------------------------------------------------------------------- Zvoni · 2022-10-28 17:07 Quote from: SymbolicFrank on October 28, 2022, 08:02:26 amQuote from: Thaddy on October 27, 2022, 05:52:51 pm- A trivial solution is a derived class as suggested. Basic OOP and safe. So, if you add a public field with the same name as an inaccessible private one, does it make that field public, or does it create a new field? Testing seems to indicate the latter. Or does it has to be in the same source file to work, as private fields are accessible there? Not Fields, but Properties (with Getter/Setter) Yes. At least i understand it that way. ---------------------------------------------------------------------- Arioch · 2022-11-18 13:31 Isn't there pre-processor in FPC like in C and unlike Delphi ? Could preprocessor, on condition of some $IFDEF or $IFOPT, be replacing "private" with "public" in the compilation pipeline, but not in editor or on disk ? I just remembereed a trend in newer languages like Nemerle or Scala to expose part of compiling pipeline to the sources themselves, providing for making "compiler plugins", "hygienic macros" and what not, including as part of the project itself. ---------------------------------------------------------------------- MarkMLl · 2022-11-18 13:53 Quote from: Arioch on November 18, 2022, 02:31:25 pmIsn't there pre- processor in FPC like in C and unlike Delphi ? No, and don't suggest that one should be added (it's been done to death, and the developers don't like it). There's a modest amount of conditional processing (similar to Delphi) at an early stage of compilation... QuoteCould preprocessor, on condition of some $IFDEF or $IFOPT, be replacing "private" with "public" in the compilation pipeline, but not in editor or on disk ? ...which does have quite good single-token ("macro") expansion, i.e. can expand to multiple lines etc. However that's not relevant in the current case, since what OP's asking about is converting the visibility of something which has been separately-compiled, i.e. a field etc. that's explicitly private nearer the root of the class tree. MarkMLl ---------------------------------------------------------------------- Arioch · 2022-11-18 15:07 Quote from: MarkMLl on November 18, 2022, 02:53:18 pmHowever that's not relevant in the current case, since what OP's asking about is converting the visibility of something which has been separately- compiled There is no BPLs in FPC yet, so from user PoV there is no "separate compilation" but all the .pas file of the project are recompiled as needed. So, the requested functionality could be added by a compiler-global settings as dumb string replace on the tokenizer level and even before it (preprocessor). The claimed goal is to reduce "git/svn commit log noise" and it could do. From the user-level surface view it seems relatively low-hanging fruit. Whether the goal is worth enough i have no opinion. ---------------------------------------------------------------------- MarkMLl · 2022-11-18 15:18 Quote from: Arioch on November 18, 2022, 04:07:08 pmSo, the requested functionality could be added by a compiler-global settings as dumb string replace on the tokenizer level and even before it (preprocessor). Could-could-could... the point is that it /doesn't/ and isn't going to. Definitions, i.e. the parameter of an $ifdef, are project-level so are conveniently global. I don't believe that macros are (I might check later, but not immediately; please feel free to preempt me). However that /still/ doesn't help OP, unless the original author of the unit he's inheriting from changes his code... and if he does there's plenty of ways better than relying on a preprocessor. I have my own opinion on this, which I have vented plenty of times already. The bottom line is that by now FPC- even discounting the RTL etc.- is a massively complex undertaking, and we're stuck with what the core team is (rather thanklessly) maintaining and enhancing. Later: unless my experiment misses something obvious, a macro has to be defined in the file where it's being used i.e. can't be defined at the project level. Assuming Lazarus, a project-level option like Code: [Select]-dmacro_test:='' is passed to the compiler on the command line without the quoted part but is not picked up by e.g. 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 initialization WriteLn( macro_test );...  However either of these forms works in a unit file: 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 { define macro_test:='' }{$define macro_test:= }  So it's the := in the definition that's significant, rather than the quoted parameter. And even if this /could/ be fixed the likelihood that it /will/ be fixed is vanishingly small. MarkMLl ---------------------------------------------------------------------- PascalDragon · 2022-11-19 22:36 Quote from: Arioch on November 18, 2022, 04:07:08 pmQuote from: MarkMLl on November 18, 2022, 02:53:18 pmHowever that's not relevant in the current case, since what OP's asking about is converting the visibility of something which has been separately-compiled There is no BPLs in FPC yet, so from user PoV there is no "separate compilation" but all the .pas file of the project are recompiled as needed. Yes, there is separate compilation, because the RTL and FCL are provided pre-compiled with the compiler installations. And also Lazarus essentially does this, because it separately compiles the Lazarus packages and only then compiles the program while only providing the units paths to the pre-compiled units. Quote from: Arioch on November 18, 2022, 04:07:08 pmSo, the requested functionality could be added by a compiler-global settings as dumb string replace on the tokenizer level and even before it (preprocessor). We won't add preprocessor support.