I'm developing a library and am trying to use inheritance where appropriate. An example of the sort of thing I'm trying to do is shown below.
FUNCTIONBLOCKINTERNALBaseClass VAR_IN_OUT  i0:T1; END_VAR VAR_IN  i1:BOOL;  i2:BOOL;  i3:T2;  i4:T3; END_VAR VAR_OUT  o0:BOOL;  o1:BOOL;  o2:BOOL;  o3:BOOL;  o4:BOOL;  o5:WORD; END_VAR //DefinecommonlogichereusingI/Ovariablesandmethods/propertiesoverriddenbyderivedclasses.END_FUNCTION_BLOCKFUNCTIONBLOCKDerivedClassEXTENDSBaseClass VAR_IN  i5:LREAL;  i6:LREAL;  i7:LREAL;  i8:LREAL; END_VAR SUPER^(i0:=i0); //Callthebaseclass'slogic(usingoverriddenmethods/properties)END_FUNCTION_BLOCK```Thisalmostworks,butwhenIuseitthus:
VAR
 fb: DerivedClass;
 x: T1;
 y: BOOL;
END_VAR
fb(i0 := x, i1 := TRUE, i5 := 100.0, i6 := 100.0, i7 := 10.0, o0 => y);
```
I get 'Cannot access internal variable BaseClass.i0' (and the same for 'o0'). The problem is that I have made the base class internal, as I want a user of the library to be neither aware of the base class's existence nor able to use it. The problem seems to be that the INTERNAL qualifier on the base class FB seems to make all its I/O variables inaccessible when the derived class is used. If I remove the INTERNAL qualifier, all is well, but the user can see the base class in the auto-complete pop-up.
If I do the above, but include it directly in a PRG, then all is fine, it's just when it is in a library that the problem with INTERNAL arises.
I have tried placing {attribute 'hide'} ahead of the base class's definition, which serves well from the point of view of stopping the base class from appearing in auto-complete pop-ups, but it is still possible to use the base class if you know its name.
Can anyone advise on the best way to hide base classes in a library from the user, but not in such a way that stops the base class's I/O variables from being accessed?
Finally, is the way that the INTERNAL qualifier hides its I/O variables correct, i.e. should it just make the FB for internal use only?
Thanks in advance for your help.
Andrew.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Anonymous
-
2016-02-10
Originally created by: scott_cunningham
The keyword INTERNAL means that your derived class can access the base class within it's code, but you cannot access the base class from outside (which is what your are doing when you call your derived class). You will need to add properties to your derived class to pass on the values to the base class.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
This behaviour depends on how you look at it. One way is that the derived class inherits the I/O variables, so they should be usable because the derived class is not internal. The other way is that the derived class does not inherit the parent class's I/O variables, such that when you use them with an instance of the derived class, you are actually interfacing directly with the parent class, which is not possible because it is internal. The latter way appears to match how it's implemented.
The behaviour as-is is akin to the base class's I/O variables having a notional PRIVATE or PROTECTED attribute, making them accessible in only the base class or base and parent class respectively, but not accessible outside the derived class. Personally, I don't find this semantic interpretation too useful.
This is especially true in my situation since the base class has a VAR_IN_OUT variable (a structure), which the compiler insists I initialise when I call an instance of the derived class, so I cannot even rename all the base class's I/O variables (and duplicate them in the derived class) and assign them when I call SUPER^() from the derived class. For example, issuing SUPER^(i0base := i0, i1base := i1, i2base := i2, i3base := i3, i4base := i4, o0base => o0, o1base => o1, o2base => o2, o3base => o3, o4base => o4, o5base => o5) in the derived class's code body seems okay, but when the derived class's instance is called, the compiler insists that 'i0base' needs assigning in that call, but this cannot be done because it is INTERNAL. This makes using an FB derived from an INTERNAL FB that has a VAR_IN_OUT I/O variable impossible! Consequently, doing such should either result in a compilation error or the semantics of how the base class's I/O variables are interpreted needs changing. I don't even think using properties as you suggested will work, as the compiler will still insist that the VAR_IN_OUT I/O variable of the base class be initialised, even though it won't allow me to do so.
Thanks again for your help,
Andrew.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Anonymous
-
2016-02-10
Originally created by: scott_cunningham
For reasons you mention (and others), I try to avoid VAR_IN_OUTs. I will use either a pointer or REFERENCE TO instead to achieve the link. Maybe you could use the following little trick:
FB_BASE
- Prop1
- Prop2
FB_BIGGER
FbBase : FB_BASE
- PropA
- PropB
PROPERTY ProbB REFERENCE TO FB_BASE
Only define a GET (PropB := FbBase).
(This is from memory - it was something like this).
Then, when you can do: FbBig.PropB.Prop1 and FbBig.PropB.Prop2
This is different to what you were originally hoping for (FbBig.Prop1 and FbBig.Prop2), however.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
This is finally the concept of OOP. If you want to access any data of the base FB, use properties or methods inside the derived FB and "forward" or whatever you want to do with the data to the base FB. It's not perfect inside CoDeSys, but I guess it's the best on the market, at least inside automation business
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Hello,
I'm developing a library and am trying to use inheritance where appropriate. An example of the sort of thing I'm trying to do is shown below.
VAR
 fb: DerivedClass;
 x: T1;
 y: BOOL;
END_VAR
fb(i0 := x, i1 := TRUE, i5 := 100.0, i6 := 100.0, i7 := 10.0, o0 => y);
```
I get 'Cannot access internal variable BaseClass.i0' (and the same for 'o0'). The problem is that I have made the base class internal, as I want a user of the library to be neither aware of the base class's existence nor able to use it. The problem seems to be that the INTERNAL qualifier on the base class FB seems to make all its I/O variables inaccessible when the derived class is used. If I remove the INTERNAL qualifier, all is well, but the user can see the base class in the auto-complete pop-up.
If I do the above, but include it directly in a PRG, then all is fine, it's just when it is in a library that the problem with INTERNAL arises.
I have tried placing {attribute 'hide'} ahead of the base class's definition, which serves well from the point of view of stopping the base class from appearing in auto-complete pop-ups, but it is still possible to use the base class if you know its name.
Can anyone advise on the best way to hide base classes in a library from the user, but not in such a way that stops the base class's I/O variables from being accessed?
Finally, is the way that the INTERNAL qualifier hides its I/O variables correct, i.e. should it just make the FB for internal use only?
Thanks in advance for your help.
Andrew.
Originally created by: scott_cunningham
The keyword INTERNAL means that your derived class can access the base class within it's code, but you cannot access the base class from outside (which is what your are doing when you call your derived class). You will need to add properties to your derived class to pass on the values to the base class.
Thanks for the help Scott.
This behaviour depends on how you look at it. One way is that the derived class inherits the I/O variables, so they should be usable because the derived class is not internal. The other way is that the derived class does not inherit the parent class's I/O variables, such that when you use them with an instance of the derived class, you are actually interfacing directly with the parent class, which is not possible because it is internal. The latter way appears to match how it's implemented.
The behaviour as-is is akin to the base class's I/O variables having a notional PRIVATE or PROTECTED attribute, making them accessible in only the base class or base and parent class respectively, but not accessible outside the derived class. Personally, I don't find this semantic interpretation too useful.
This is especially true in my situation since the base class has a VAR_IN_OUT variable (a structure), which the compiler insists I initialise when I call an instance of the derived class, so I cannot even rename all the base class's I/O variables (and duplicate them in the derived class) and assign them when I call SUPER^() from the derived class. For example, issuing SUPER^(i0base := i0, i1base := i1, i2base := i2, i3base := i3, i4base := i4, o0base => o0, o1base => o1, o2base => o2, o3base => o3, o4base => o4, o5base => o5) in the derived class's code body seems okay, but when the derived class's instance is called, the compiler insists that 'i0base' needs assigning in that call, but this cannot be done because it is INTERNAL. This makes using an FB derived from an INTERNAL FB that has a VAR_IN_OUT I/O variable impossible! Consequently, doing such should either result in a compilation error or the semantics of how the base class's I/O variables are interpreted needs changing. I don't even think using properties as you suggested will work, as the compiler will still insist that the VAR_IN_OUT I/O variable of the base class be initialised, even though it won't allow me to do so.
Thanks again for your help,
Andrew.
Originally created by: scott_cunningham
For reasons you mention (and others), I try to avoid VAR_IN_OUTs. I will use either a pointer or REFERENCE TO instead to achieve the link. Maybe you could use the following little trick:
FB_BASE
- Prop1
- Prop2
FB_BIGGER
FbBase : FB_BASE
- PropA
- PropB
PROPERTY ProbB REFERENCE TO FB_BASE
Only define a GET (PropB := FbBase).
(This is from memory - it was something like this).
Then, when you can do: FbBig.PropB.Prop1 and FbBig.PropB.Prop2
This is different to what you were originally hoping for (FbBig.Prop1 and FbBig.Prop2), however.
This is finally the concept of OOP. If you want to access any data of the base FB, use properties or methods inside the derived FB and "forward" or whatever you want to do with the data to the base FB. It's not perfect inside CoDeSys, but I guess it's the best on the market, at least inside automation business