Misunderstanding of how PROTECTED works?

General FreeBASIC programming questions.
wallyg
Posts: 270
Joined: May 08, 2009 7:08
Location: Tucson Arizona

Misunderstanding of how PROTECTED works?

Post by wallyg »

I have the following type definitions:

Code: Select all

Type		widget		Extends object
Protected:
as Integer		X
as Integer		Y
....
End Type

Type		container		Extends widget
Protected:
as Boolean	B
....
End Type
Then further on in the code, there is a definition of a routine

Code: Select all

Sub		Create(owner as container Ptr)
....
owner->X = 10
owner->Y = 10
owner->B = False
....
End Sub
I have an Up to date Windows 10 system, with FB 1.09.0 system
When I compile the above I get an error on the statement trying to set B to False, no error on setting X or Y
The error is "Error 202: Illegal member access: found 'B' in 'owner->B = False'

If I change the Protected: in the definition of the container, not in the widget definition to Public it all compiles ok.

So why can I access a protected member in the widget but not the container?

Thank you for any insight into what I have misunderstood about Protected or screwed up. I assumed all 3 assignments would be ok regardless of the EXTENDS level.

Wally
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Misunderstanding of how PROTECTED works?

Post by fxm »

With your code, one gets an error for the three lines:

Code: Select all

Type		widget		Extends object
Protected:
as Integer		X
as Integer		Y
'....
End Type

Type		container		Extends widget
Protected:
as Boolean	B
'....
End Type



Sub		Create(Byval owner as container Ptr)
'....
owner->X = 10
owner->Y = 10
owner->B = False
'....
End Sub
C:\...\FBIde0.4.6r4_fbc1.10.0\FBIDETEMP.bas(18) error 202: Illegal member access, found 'X' in 'owner->X = 10'
C:\...\FBIde0.4.6r4_fbc1.10.0\FBIDETEMP.bas(19) error 202: Illegal member access, found 'Y' in 'owner->Y = 10'
C:\...\FBIde0.4.6r4_fbc1.10.0\FBIDETEMP.bas(20) error 202: Illegal member access, found 'B' in 'owner->B = False'


Perhaps you have other 'X' and 'Y' members accessible from 'container'.
adeyblue
Posts: 300
Joined: Nov 07, 2019 20:08

Re: Misunderstanding of how PROTECTED works?

Post by adeyblue »

Protected is essentially Private, but it allows derived types to access them as if they were private in that type too. As opposed to true private, where you can't access them in derived types.

Code: Select all

Type BaseType
Private:
    priv As Long
    
Protected:
    prot As Long
End Type

Type Ext extends BaseType
    Declare Constructor()
End Type

Constructor Ext()
    priv = 7 '' not allowed
    prot = 4 '' this is ok
End Constructor

Sub ExternalFunc()
    dim obj as Ext
    obj.priv = 6 '' won't work - it's private
    obj.prot = 3 '' also won't work, it acts as private
End Sub
wallyg
Posts: 270
Joined: May 08, 2009 7:08
Location: Tucson Arizona

Re: Misunderstanding of how PROTECTED works?

Post by wallyg »

I shouldn't but this is a brand new program, first time trying to compile it. I will look into that, I suppose there is a possibility but in reality, I changed the names to something simple for the example. The names are normally related to the Type they come from. ie. Within the widget type, all names are prefixed by "widget" and all variables in the container as prefixed by "container".

So are you saying that the container pointer variable can only reference PUBLIC: members and Protected names are not available to a variable of type container ptr? That is obviously my misunderstanding of the meaning of Protected.

I wish there was some way to specify that a name defined within a type is available to other types so the related types that are not direct descendants can access them. I seem to remember an access type called Friend in an object-orientated language I looked into 20 years ago.

Thank you for your help.

Wally
wallyg
Posts: 270
Joined: May 08, 2009 7:08
Location: Tucson Arizona

Re: Misunderstanding of how PROTECTED works?

Post by wallyg »

Just finished checking and X and Y definitions do not exist anywhere else.

I took your test case and made a minor change that I had not thought was meaningful at the time

Code: Select all

Type		widget		Extends object
Protected:
as Integer		X
as Integer		Y
End Type

Type		container		Extends widget
Protected:
as Boolean	B
End Type

Type        button         	 Extends widget
as Integer  	C
Declare	 	sub Create(a as container ptr)
End Type

Sub		button.Create(Byval owner as container Ptr)
owner->X = 10
owner->Y = 10
owner->B = False
End Sub
Guess what, I now only get one error on the line owner->B = False

But the lines owner->X and owner-->Y compile without any compiler errors. Correctly who knows?

This is a 64 bit compile with options (-g -s gui -gen GAS64 -maxerr 1024 -e -ex -exx)

Wally
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Misunderstanding of how PROTECTED works?

Post by fxm »

Protected members are accessible only from inside a member function for the Type or Class, and classes which are derived from the Type or Class.
From the 'Create()' Sub member (in 'button'), the protected members accessible are those of the type itself ('button') and those of its parents (only 'widget').
'container' is not a parent of 'button'.
wallyg
Posts: 270
Joined: May 08, 2009 7:08
Location: Tucson Arizona

Re: Misunderstanding of how PROTECTED works?

Post by wallyg »

So I was lucky that the code was in a routine that was related to a type that had a common subroot of widget with the container pointer. This means that the code will work in some places but not others. This blows my mind.

A container ptr should have access to all or none of the Protected names anywhere it is used and not be related to the fact it was accidentally placed in a routine that coincidentally has a common root. I can understand and appreciate why the reference to a protected name like I used is not valid and will have to work out how to accomplish what I want another way.

However, then within the routine Create only buttons or button pointers should have the ability to access their protected names.

From my reading of the Protected keyword, I would never have expected this behavior. This needs some serious work in the documentation.

Given the explanation that I was given above than I think allowing the container ptr to access the protected variables of any common sub roots of button and container is an error. Something that works just because of a lucky placement has got to be questionable. When writing code an expectation on how it will work wherever the code is placed is paramount. I have code like this built into some macros that need to work wherever they are placed by my client. I have no idea of where it will be used and I am sure it will be used in places that are not lucky.

Thank you to everyone that has helped me understand this strange operation. Thank You, Thank You, Thank You, I would never have figured this out no matter how long I read the documentation.

Wally
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Misunderstanding of how PROTECTED works?

Post by fxm »

The rule between private and protected is simple:
  • Private data members cannot be accessed outside the type.
  • When a type inherits a base-type, all the data members except the private get inherited into it. So if one want data members to be accessible to only derived-types and not privately or publicly accessible, then one can use protected.
  • Thus, protected is similar to private. It makes type members inaccessible outside the type, but the members can be accessed by any sub-type of that type.
paul doe
Moderator
Posts: 1733
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: Misunderstanding of how PROTECTED works?

Post by paul doe »

fxm wrote: Apr 20, 2022 15:03 The rule between private and protected is simple:
  • Private data members cannot be accessed outside the type.
  • When a type inherits a base-type, all the data members except the private get inherited into it. So if one want data members to be accessible to only derived-types and not privately or publicly accessible, then one can use protected.
  • Thus, protected is similar to private. It makes type members inaccessible outside the type, but the members can be accessed by any sub-type of that type.
Do note that the snippet above contradicts these statements:

Code: Select all

Sub		button.Create(Byval owner as container Ptr)
owner->B = False '' This errs...
owner->X = 10    '' But also should these! Otherwise they're public!
owner->Y = 10
End Sub
Neither of the three members should be accessible. Protected can be understood as 'public for the class and derived classes', but this is clearly not the case and it is, thus, a bug as far as I can tell. I double checked the behavior with C++, Java, PHP, C# and Visual Basic. They all work as expected (ie protected NOT being accessible from client code)
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Misunderstanding of how PROTECTED works?

Post by fxm »

paul doe wrote: Apr 20, 2022 16:16 Protected can be understood as 'public for the class and derived classes', but this is clearly not the case and it is, thus, a bug as far as I can tell. I double checked the behavior with C++, Java, PHP, C# and Visual Basic. They all work as expected (ie protected NOT being accessible from client code)

Indeed, I think this behavior (when the access operator does not apply on the non-static 'This' instance, explicitly or implicitly) would deserve a discussion, perhaps here ?

Code: Select all

Type Parent
    Protected:
        Dim As Integer I
        Static As Integer J
End Type
Dim As Integer Parent.J

Type Child1 Extends Parent
End Type

Type Child2 Extends Parent
    Declare Sub test(Byref c2 As Child2, Byref p As Parent, Byref c1 As Child1)
    Declare Static Sub stest(Byref c2 As Child2, Byref p As Parent, Byref c1 As Child1)
End Type

Sub Child2.test(Byref c2 As Child2, Byref p As Parent, Byref c1 As Child1)
    ' non-static protected member access from non-static member procedure
    I = 1       ' OK
    This.I = 2  ' OK
    c2.I = 3    ' OK, but should be NOK ?
    p.I = 4     ' OK, but should be NOK ?
    c1.I = 5    ' OK, but should be NOK ?

    ' static protected member access from non-static member procedure
    J = 1       ' OK
    This.J = 2  ' OK
    c2.J = 3    ' OK
    p.J = 4     ' OK
    c1.J = 5    ' OK
End Sub

Static Sub Child2.stest(Byref c2 As Child2, Byref p As Parent, Byref c1 As Child1)
    ' non-static protected member access from static member procedure
    c2.I = 3    ' OK, but should be NOK ?
    p.I = 4     ' OK, but should be NOK ?
    c1.I = 5    ' OK, but should be NOK ?

    ' static protected member access from static member procedure
    J = 1       ' OK
    c2.J = 3    ' OK
    p.J = 4     ' OK
    c1.J = 5    ' OK
End Sub

The definition of 'Protected' in the documentation may appear to be consistent with current behavior, as this definition applies to type hierarchy in general, not to specific instance inheritance (like the other definition proposed in my post above for accessing protected non-static members).

In my opinion, the current behavior is correct for accessing protected static members, but maybe not for accessing protected non-static members.

@Jeff and others interested, what is your opinion on the 'Protected' access control definition ?
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Misunderstanding of how PROTECTED works?

Post by fxm »

@paul doe,

Can you test my code above but transposed to C++ for example ?
paul doe
Moderator
Posts: 1733
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: Misunderstanding of how PROTECTED works?

Post by paul doe »

fxm wrote: Apr 21, 2022 12:20 @paul doe,

Can you test my code above but transposed to C++ for example ?
Sure thing. I'll do it as soon as I get home.
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Misunderstanding of how PROTECTED works?

Post by dodicat »

wallyg wrote: Apr 19, 2022 21:23 Just finished checking and X and Y definitions do not exist anywhere else.

I took your test case and made a minor change that I had not thought was meaningful at the time

Code: Select all

Type		widget		Extends object
Protected:
as Integer		X
as Integer		Y
End Type

Type		container		Extends widget
Protected:
as Boolean	B
End Type

Type        button         	 Extends widget
as Integer  	C
Declare	 	sub Create(a as container ptr)
End Type

Sub		button.Create(Byval owner as container Ptr)
owner->X = 10
owner->Y = 10
owner->B = False
End Sub
Guess what, I now only get one error on the line owner->B = False

But the lines owner->X and owner-->Y compile without any compiler errors. Correctly who knows?

This is a 64 bit compile with options (-g -s gui -gen GAS64 -maxerr 1024 -e -ex -exx)

Wally
You wouldn't expect the protected member b in container to be available from the sub button.create(), as you wouldn't expect protected member b in container be available from anywhere because as it stands type container is an entity coupled with only type widget.
And coupled is too strong a word here, a loose association might describe it better.
You would have to get button to extend container to access protected container fields/methods from within button methods.
The parameter :button.Create (Byval owner as container Ptr) is of no help getting at B from within the sub, although at first glance it looks promising.
I don't think there is even an easy hack to get at boolean b from outside.
paul doe
Moderator
Posts: 1733
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: Misunderstanding of how PROTECTED works?

Post by paul doe »

Indeed, the OP simply made a mistake. However, it is interesting to note how differently C++ and FreeBasic interpret what constitutes a 'protected' member.

@fxm: here are my results for your code:

Code: Select all

class Parent
{
  protected:
    int i ;
    static int j ;
};

int Parent::j = -4 ;

class Child1 : Parent {};

class Child2 : Parent
{
  void test( Child2& c2, Parent& p, Child1& c1 )
  {
    // non-static protected member access from non-static member procedure
    i = 1 ;       // OK
    this->i = 2 ; // OK
    c2.i = 3 ;    // NOK
    p.i = 4 ;     // NOK
    c1.i = 5 ;    // NOK

    // static protected member access from non-static member procedure
    j = 1 ;       // OK
    this->j = 2 ; // OK
    c2.j = 3 ;    // NOK
    p.j = 4 ;     // NOK
    c1.j = 5 ;    // NOK
  }

  static void stest( Child2& c2, Parent& p, Child1& c1 )
  {
    // non-static protected member access from static member procedure
    c2.i = 3 ;   // OK
    p.i = 4 ;    // NOK
    c1.i = 5 ;   // NOK

    // static protected member access from static member procedure
    j = 1 ;      // OK
    c2.j = 3 ;   // OK
    p.j = 4 ;    // OK
    c1.j = 5 ;   // NOK
  }
};

int main()
{
  return 0 ;
}
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Misunderstanding of how PROTECTED works?

Post by fxm »

paul doe wrote: Apr 21, 2022 18:54 @fxm: here are my results for your code:
Thank you.
But I am struggling to find a logical reasoning behind the outcome for all those 17 access cases in C++.
Post Reply