* * *

Author Topic: How to keep protected procedures accessible in derived classes  (Read 592 times)

Pascal

  • Sr. Member
  • ****
  • Posts: 318
How to keep protected procedures accessible in derived classes
« on: February 17, 2017, 11:29:54 am »
I have a BaseClass TBase with an protected procedure Foo.
Class TA is derived from TBase and
class TB is derived from TA.
In TB i can't access Foo! Why?
How can i circumvent this (except putting all classes in one unit)?
latest trunk - fpc 3.0.1 32bit - Windows 10 Pro x64

howardpc

  • Hero Member
  • *****
  • Posts: 2027
Re: How to keep protected procedures accessible in derived classes
« Reply #1 on: February 17, 2017, 11:58:44 am »
I can't reproduce your problem.
See attached project with TBase, TA and TB in separate units.
Have you declared Foo as strict protected?

Thaddy

  • Hero Member
  • *****
  • Posts: 2832
Re: How to keep protected procedures accessible in derived classes
« Reply #2 on: February 17, 2017, 12:11:03 pm »
I can't either. Are you confusing protected with private or strict private?
protected members are always accessible for derived classes, no matter where they are defined.
Code: Pascal  [Select]
  1. program testds;
  2. {$mode objfpc}
  3. uses dsunit;
  4. type
  5.   TDoSomething = class(TprotClass);
  6. var
  7.   a: TDoSomething;  
  8. begin
  9.   a := TDoSomething.Create;
  10.   try
  11.     a.DoSomething;
  12.   finally
  13.     a.free;
  14.   end;
  15. end.
Code: Pascal  [Select]
  1. unit dsunit;
  2. {$mode objfpc}
  3. interface
  4. type
  5.   TprotClass = class
  6.   protected
  7.     procedure DoSomething;virtual;
  8.   end;
  9.  
  10. implementation
  11.   procedure TprotClass.DoSomething;
  12.   begin
  13.     writeln('I am not lazy');
  14.   end;
  15. end.
« Last Edit: February 17, 2017, 12:23:25 pm by Thaddy »

Thaddy

  • Hero Member
  • *****
  • Posts: 2832
Re: How to keep protected procedures accessible in derived classes
« Reply #3 on: February 17, 2017, 12:25:23 pm »
I can't reproduce your problem.
Missed your attachment, sorry. Same answer.

Pascal

  • Sr. Member
  • ****
  • Posts: 318
Re: How to keep protected procedures accessible in derived classes
« Reply #4 on: February 17, 2017, 12:51:26 pm »
Sorry, i was a little confused. I didn't decribe my problem corectly.
It's correct that i can't reach the Foo procedure as i used a cast to a derived class of TB (TC) inside TB.

But:
See attached updated project. It has two build modes. Why does the one of them work?
latest trunk - fpc 3.0.1 32bit - Windows 10 Pro x64

howardpc

  • Hero Member
  • *****
  • Posts: 2027
Re: How to keep protected procedures accessible in derived classes
« Reply #5 on: February 17, 2017, 01:42:12 pm »
The real question is why does one of them not work.

In TB.Prepare you can call (plain) Foo, since it is a protected ancestor method of TB.
But you cannot access FC.Foo (whether or not you cast it to TC).
Here you are not using an inherited method of TB, but trying to access a protected method of a completely foreign class (FC which is a TBBase, or a TC if you cast it).
If you redeclare Foo in TBBase and make it public, then it will be visible inside TB.Prepare, and you can call FC.Foo with no cast (although you will get an access violation since in your implementation FC is nil).

BTW your example has a memory leak, since you create c but never free it.

Also, your example compiles because you have added a "uses uC;" to the implementation of unit uB (to avoid a circular dependency between uB and uC).
This is usually a sign of poor or over-complicated class hierarchy design. OK, you did it so you could make the artificial TC() cast. But needing to do that sort of thing, even in a contrived example, often indicates that a simpler re-design would be an improvement.

Blaazen

  • Hero Member
  • *****
  • Posts: 2370
  • POKE 54296,15
    • Eye-Candy Controls
Re: How to keep protected procedures accessible in derived classes
« Reply #6 on: February 17, 2017, 01:42:52 pm »
In mode without TEST:
Code: Pascal  [Select]
  1. procedure TB.Prepare;
  2. begin
  3.   TC(fC).Foo;
  4. end;
gives error, because Foo is protected. You can call public methods only. You can call self.Foo; because class TB is subclassed from TBase. But private field
Code: Pascal  [Select]
  1. private
  2.     fC: TBBase;  
is delegated, so you can call only public methods of fC in methods of TB.

In mode with TEST:
Method TB.Foo has nothing to do with TBase.Foo except its name. TBase.Foo is not declared as virtual and TB.Foo is not declared as override.
Method body contains inherited keyword:
Code: Pascal  [Select]
  1. {$IFDEF TEST}
  2. procedure TB.Foo;
  3. begin
  4.   inherited;
  5. end;
  6. {$ENDIF}  
Yes, FreePascal allows it of some reason, even if method is not properly inherited. If you want to use inheritance, use virtual, abstract and override keywords properly.
Lazarus 1.7 r52036M FPC 3.0.0 x86_64-linux-qt Chakra, Qt 4.8.7, Plasma 5.5
Lazarus 1.4RC3 r48629 FPC 2.6.4 i386-win32-win32/win64 Wine 1.7.43
Try Eye-Candy Controls: https://sourceforge.net/projects/eccontrols/files/

Pascal

  • Sr. Member
  • ****
  • Posts: 318
Re: How to keep protected procedures accessible in derived classes
« Reply #7 on: February 17, 2017, 05:23:45 pm »
In mode with TEST:
Method TB.Foo has nothing to do with TBase.Foo except its name. TBase.Foo is not declared as virtual and TB.Foo is not declared as override.
Method body contains inherited keyword:
Code: Pascal  [Select]
  1. {$IFDEF TEST}
  2. procedure TB.Foo;
  3. begin
  4.   inherited;
  5. end;
  6. {$ENDIF}  
Yes, FreePascal allows it of some reason, even if method is not properly inherited. If you want to use inheritance, use virtual, abstract and override keywords properly.

Yes, but Foo is still protected. So should also not be allowed.
And as it is allowed, why not allow it in the other case? (Argument: TB is a allowed to call Foo of TBase as it is protected)
latest trunk - fpc 3.0.1 32bit - Windows 10 Pro x64

Blaazen

  • Hero Member
  • *****
  • Posts: 2370
  • POKE 54296,15
    • Eye-Candy Controls
Re: How to keep protected procedures accessible in derived classes
« Reply #8 on: February 17, 2017, 05:47:22 pm »
In mode with Test:
This
Code: Pascal  [Select]
  1. procedure TB.Prepare;
  2. begin
  3.   TC(fC).Foo;
  4. end;
is possible because it is in the same unit (I mean Foo is declared in the same unit).
You can try to write new unit UD:
Code: Pascal  [Select]
  1. unit Ud;
  2. {$mode objfpc}{$H+}
  3.  
  4. interface
  5.  
  6. uses
  7.   Classes, SysUtils, ub, uc, uBBase;
  8.  
  9. type
  10.   { td }
  11.   td = class(TBBase)
  12.   private
  13.     cc: TBBase;
  14.   procedure TestProc;
  15.   end;
  16.  
  17. implementation
  18.  
  19.  { td }
  20.  
  21. procedure td.TestProc;
  22. begin
  23.   tc(cc).Foo;
  24. end;
  25.  
  26. end.
and you get the error message "ud.pas(23,10) Error: identifier idents no member "Foo"" again.
Lazarus 1.7 r52036M FPC 3.0.0 x86_64-linux-qt Chakra, Qt 4.8.7, Plasma 5.5
Lazarus 1.4RC3 r48629 FPC 2.6.4 i386-win32-win32/win64 Wine 1.7.43
Try Eye-Candy Controls: https://sourceforge.net/projects/eccontrols/files/

Thaddy

  • Hero Member
  • *****
  • Posts: 2832
Re: How to keep protected procedures accessible in derived classes
« Reply #9 on: February 17, 2017, 06:36:49 pm »
I think you mess up protected and private after all, like I suspected this morning:
Private means it is not accessible to derived classes. Strict private means not even in the same unit.
protected means it is accessible to derived classes. Strict protected means as above.

See protected as: you can only access me if we have the same ancestor, DNA if you like.
See private as: even if you are my parents, this information is for me only.

Blaazen

  • Hero Member
  • *****
  • Posts: 2370
  • POKE 54296,15
    • Eye-Candy Controls
Re: How to keep protected procedures accessible in derived classes
« Reply #10 on: February 17, 2017, 06:39:58 pm »
Here: http://www.freepascal.org/docs-html/3.0.0/ref/refse32.html
In FPC it also depends if it is in the same module (unit) or not.
Lazarus 1.7 r52036M FPC 3.0.0 x86_64-linux-qt Chakra, Qt 4.8.7, Plasma 5.5
Lazarus 1.4RC3 r48629 FPC 2.6.4 i386-win32-win32/win64 Wine 1.7.43
Try Eye-Candy Controls: https://sourceforge.net/projects/eccontrols/files/

Thaddy

  • Hero Member
  • *****
  • Posts: 2832
Re: How to keep protected procedures accessible in derived classes
« Reply #11 on: February 17, 2017, 06:46:05 pm »
Here: http://www.freepascal.org/docs-html/3.0.0/ref/refse32.html
In FPC it also depends if it is in the same module (unit) or not.

T
Yes and No, that's why we have strict in the language. As my answer implied.

Pascal

  • Sr. Member
  • ****
  • Posts: 318
Re: How to keep protected procedures accessible in derived classes
« Reply #12 on: February 18, 2017, 05:57:10 am »
I know about private, protected and public. But the FPC-Way is a little bit messed up by using also the module/unit.

Therefore i would call one of the two cases a bug:
1. "without TEST": Shouldn't throw an erros as Foo is visible in other units to decendants (TB in uB)!
2. "with TEST": Should throw an error as Foo is accessed from outside and protected members aren't accessible from outside
latest trunk - fpc 3.0.1 32bit - Windows 10 Pro x64

Thaddy

  • Hero Member
  • *****
  • Posts: 2832
Re: How to keep protected procedures accessible in derived classes
« Reply #13 on: February 18, 2017, 07:24:05 am »
Look at the unit concept as namespace, like in C++ or Java.
I would expect the code to fail in both cases because there's no foo in Tbbase (no member error).
The TC cast is wrong. (and bad coding, but that is less relevant)
Didn't test it, this is pure on analyses. Does it even compile, because the compiler should catch that?

Pascal

  • Sr. Member
  • ****
  • Posts: 318
Re: How to keep protected procedures accessible in derived classes
« Reply #14 on: February 20, 2017, 05:56:19 am »
The TC cast is wrong.
But how do you circumvent circular unit references? I think this is the bigest issue in Pascal.

In my case i am dealing with COBOL-Sources. Simplyfied my class tree looks like this

Code: Text  [Select]
  1.             TprimeFile
  2.                  |
  3.             TprimeTextFile
  4.                  |
  5.             TprimeSVNFile
  6.                  |
  7.             TprimeSourceFile
  8.               |        |
  9. TprimeProgramFile   TprimeCopyFile

I want to put each class in one unit and i need a reference in TprimeSourceFile to the corresponding TprimeProgamFile!
Therefore i put a TprimeSourceFileBase between TprimeSVNFile and TprimeSourceFile and use this a reference type. In the implementation of TprimeSourceFile
i cast this to TprimeProgramFile.

What is the prefered way to prevent circular unit references?

Why cant't the compile stop when it detects a circular unit reference and handle things like forward declarations?
latest trunk - fpc 3.0.1 32bit - Windows 10 Pro x64

 

Recent

Get Lazarus at SourceForge.net. Fast, secure and Free Open Source software downloads Open Hub project report for Lazarus