Lazarus

Free Pascal => General => Topic started by: simone on July 26, 2022, 10:17:49 am

Title: A doubt about the visibility specifiers in the classes
Post by: simone on July 26, 2022, 10:17:49 am
Consider the following program and the unit it uses:

Code: Pascal  [Select][+][-]
  1. program Project1;
  2. {$mode ObjFPC}{$H+}
  3. uses unit1;
  4. var
  5.   MyClass : TMyClass;
  6.  
  7. begin
  8.   MyClass:=TMyClass.Create;
  9.   MyClass.Test(Value1);
  10.   MyClass.Free;
  11. end.

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2. {$mode ObjFPC}{$H+}
  3.  
  4. interface
  5. uses
  6.   Classes, SysUtils;
  7. type
  8.  
  9.   TMyClass=class
  10.     private
  11.       type
  12.         TMyEnum=(Value1,Value2);
  13.     public
  14.       procedure Test(MyEnum : TMyEnum);
  15.   end;
  16.  
  17. implementation
  18.  
  19. procedure TMyClass.Test(MyEnum: TMyEnum);
  20. begin
  21.   writeln(MyEnum);
  22. end;
  23.  
  24. end.

In the Unit1, TMyEnum is an enum type declared in private section of TMyClass, so it should only be visible in the module that contains the class definition.
Nevertheless in the main program I can use the values of TMyEnum as parameters to invoke the Test method of the same class.

This is also possible if strict private is used.

Why are the values of TMyEnum visible outside the module, even though they have been declared as (strict) private in the class they belong to?

Thanks in advance for the kind explanations.

Title: Re: A doubt about the visibility specifiers in the classes
Post by: marcov on July 26, 2022, 10:32:12 am
It is a bit contrived having a public method with a private type.  How do you imagine that this would work?

Can you access the values (e.g. ord()ing) without declaring that method? Maybe the method causes the enum to be exported.
Title: Re: A doubt about the visibility specifiers in the classes
Post by: simone on July 26, 2022, 10:35:23 am
I'm not saying this is good programming practice, but since it's allowed, I'd just like to understand why the compiler behaves this way.
Title: Re: A doubt about the visibility specifiers in the classes
Post by: Thaddy on July 26, 2022, 10:47:13 am
Because the public test method is an access specifier?
It would be different if the enum itself is accessible directly from other units without an access specifier or even with strict private from the same unit: in that case it is a bug. But it isn't. The method 'exports' it because of its parameter.
Title: Re: A doubt about the visibility specifiers in the classes
Post by: simone on July 26, 2022, 11:04:35 am
Thanks Thaddy. So should I assume that a public method of a class makes public the types of its parameters that have been declared private in the same class?

Post Scriptum: It's obvious that the example does not show a good style of programming. I exemplified a situation that happened to me during a refactoring, in which certain edge situations like this can occur.
Title: Re: A doubt about the visibility specifiers in the classes
Post by: MarkMLl on July 26, 2022, 11:30:11 am
Post Scriptum: It's obvious that the example does not show a good style of programming. I exemplified a situation that happened to me during a refactoring, in which certain edge situations like this can occur.

I think this is something that the compiler could usefully warn about, but is quite simply such an absurdity (in the strictly logical sense) that it can be forgiven for overlooking it.

Do you have a more concrete example?

MarkMLl
Title: Re: A doubt about the visibility specifiers in the classes
Post by: simone on July 26, 2022, 11:53:57 am
I have a more concrete example, but it comes from too large a code base to show here. In brief, as part of a refactoring process, I reorganized some classes and had an unexpected outcome that made me discover this behavior.

I just wanted to know if it was known and expected. For me, modestly, it was not an expected behavior. That's all.
Title: Re: A doubt about the visibility specifiers in the classes
Post by: kupferstecher on July 26, 2022, 12:17:24 pm
Here is a simple test that might be related to the issue, see below. There are no classes involved, but a (record) type that is defined in an other unit (unit uTypes). The type is the result of a function implemented in Unit1. Obviously uTypes needs to be added to "uses" in that unit. Unit2 uses the function of unit1 and can access the result type even if uTypes is not added to "uses", see the code of unit2, especially the comments. I think its useful.

project1.lpr:
Code: Pascal  [Select][+][-]
  1. program project1;
  2. uses Unit1, uTypes, Unit2;
  3. begin
  4.   WriteSth;
  5.   ReadLn;
  6. end.  

unit1.pas:
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2. {$mode ObjFPC}{$H+}
  3. INTERFACE
  4.  
  5. uses uTypes;
  6.  
  7. Function GetValue: TSth;
  8.  
  9. IMPLEMENTATION
  10.  
  11. Function GetValue:TSth;
  12. begin
  13.   Result.one:= 1;
  14.   Result.two:= 2;
  15. end;
  16.  
  17. end.

unit2.pas:
Code: Pascal  [Select][+][-]
  1. unit Unit2;
  2. {$mode ObjFPC}{$H+}
  3. INTERFACE
  4.  
  5. uses Unit1;
  6.  
  7. Procedure writeSth;
  8.  
  9. IMPLEMENTATION
  10.  
  11. Procedure writeSth;
  12. //var res: TSth; //not possible, if uTypes is not in uses
  13. begin
  14.   writeln('Value: ',GetValue.two); //record member can be accessed without uTypes in "uses"
  15. end;
  16.  
  17. end.

uTypes.pas:
Code: Pascal  [Select][+][-]
  1. unit uTypes;
  2. {$mode ObjFPC}{$H+}
  3. INTERFACE
  4.  
  5. Type TSth = record
  6.   one: Integer;
  7.   two: Integer;
  8. end;
  9.  
  10. IMPLEMENTATION
  11.  
  12. end.
Title: Re: A doubt about the visibility specifiers in the classes
Post by: MarkMLl on July 26, 2022, 12:39:20 pm
Here is a simple test that might be related to the issue, see below. There are no classes involved, but a (record) type that is defined in an other unit (unit uTypes). The type is the result of a function implemented in Unit1. Obviously uTypes needs to be added to "uses" in that unit. Unit2 uses the function of unit1 and can access the result type even if uTypes is not added to "uses", see the code of unit2, especially the comments. I think its useful.

I'd suggest that there's a big difference between "needs to be added to 'uses'" and "is automatically made public" which is what OP was demonstrating.

MarkMLl
Title: Re: A doubt about the visibility specifiers in the classes
Post by: kupferstecher on July 26, 2022, 12:57:56 pm
I'd suggest that there's a big difference between "needs to be added to 'uses'" and "is automatically made public" which is what OP was demonstrating.

Agreed, but I don't see that the OP demonstarted that the type itself was automatically made public[1], but only that the enum identifiers were made public. And I see the enum identifiers similar to the record member identifiers.

Then it seems these identifiers are automatically made available by the compiler if the type is used as parameter. And "making it available" in the one case is like making it public and in the other case like beeing added to uses.

[1]
If you try to add
Code: Pascal  [Select][+][-]
  1. var enum: TMyEnum;
in project1.lpr then it will fail:
project1.lpr(6,9) Error: Identifier not found "TMyEnum"
Title: Re: A doubt about the visibility specifiers in the classes
Post by: simone on July 26, 2022, 01:18:07 pm

.... I don't see that the OP demonstarted that the type itself was automatically made public[1], but only that the enum identifiers were made public. And I see the enum identifiers similar to the record member identifiers.

Then it seems these identifiers are automatically made available by the compiler if the type is used as parameter. And "making it available" in the one case is like making it public and in the other case like beeing added to uses.


Yes you are right. The enum type is not made public, but the identifiers used in its definition.

Honestly, I'm not sure this behavior is consistent. However, I leave the judgment to more authoritative people.
Title: Re: A doubt about the visibility specifiers in the classes
Post by: PascalDragon on July 26, 2022, 02:01:17 pm
Why are the values of TMyEnum visible outside the module, even though they have been declared as (strict) private in the class they belong to?

It might be that enums and nested types might bite each other a bit here, due how they are to behave in TP and Delphi without nested types. I'll need to crosscheck with those two to decide on what is right and what is wrong.
TinyPortal © 2005-2018