Community
cancel
Showing results for 
Search instead for 
Did you mean: 
Highlighted
Beginner
13 Views

17.01 vs 17.02 private component inheritance problems

Hi,

the code below compiles with ifort 17.01 and gfortran 6.3, but not with ifort 17.02. The error message is:

"A private component name is accessible only within the module containing the type definition.  
    this%a=1"

Module Mod_Parent
  Type, Abstract :: parent
    Integer, private :: a
  contains
    Procedure(SubSetA), Pass, deferred :: SetA
  End type parent
  Abstract Interface
    Subroutine SubSetA(this)
      Import parent
      Class(parent), Intent(InOut) :: this
    end subroutine
  end Interface
  Private :: SubSetA
End Module Mod_Parent
Module Mod_Child
  use Mod_Parent
  Type, extends(parent) :: Child
  contains
    Procedure, Pass :: SetA => SubSetA
  End type Child
  Interface
    Module Subroutine SubSetA(this)
      Class(child), Intent(InOut) :: this
    end subroutine
  end Interface
  Private :: SubSetA
End Module Mod_Child
Submodule(Mod_Child) Set
Contains
  Module Procedure SubSetA
    this%a=1
  End Procedure
End Submodule Set
Program Test
  use Mod_Child
  Type(Child) :: x
  call x%setA()
End Program Test

Is the code not standard compliant or is it a compiler bug. I thought the "inheritance vs private" clash was resolved with the advent of submodules.

Any idea?

0 Kudos
12 Replies
Highlighted
Employee
13 Views

The error occurs as a result of 17.0.2 containing a fix for the earlier reported error here. I believe 17.0.2 has it correct now. The submodule Set is tries accessing "a" via module Mod_Child and USE association with Mod_Parent which is not allowed. Variable "a" would be accessible within a submodule of Mod_Parent.

I don't know whether this discussion here helps with your "inheritance vs. private" clash question.

0 Kudos
Highlighted
Beginner
13 Views

Hi,

I reported a bug here https://software.intel.com/en-us/forums/intel-fortran-compiler-for-linux-and-mac-os-x/topic/704535. But there is a notable difference in the structure of the program. The example above is about inheritance, the one in the bug report not.

Your link is of no help because that fellow wanted to declare local variables private which has nothing to do with inheritance.

What I understand from reading this http://fortranwiki.org/fortran/show/Submodules, especially "....Entities in a submodule have access to the all entities and components of their ancestor module and any ancestor submodules by host association, just as if they were physically present inside the source code of their ancestors. .....", makes me concluding that the bug fix in 17.02 produced a bug. This may be also be obvious due to the error message saying implicitly that the "private" statement becomes absolutely useless as soon as the slightest inheritance is involved (that is the clash between inheritance and private components which was meant to be solved with submodules). However, someone else more into the language of the 2008 standard documentation may back or dismiss my view.

0 Kudos
Highlighted
Black Belt
13 Views

The compiler is correct.

The private component `a` is declared in the Mod_Parent module.  Only stuff inside the Mod_Parent module, or its submodule descendants, can access that component.

The submodule extends Mod_Child.  Mod_Child is not Mod_Parent.  Mod_Child and its submodules have no business accessing private stuff of Mod_Parent.

Inheritance - in the sense of type extension - doesn't change things.

0 Kudos
Highlighted
Beginner
13 Views

Thanks Ian for this clarification. Since you are a "black belt" it doesn't seem to make sense do start to dig around in the standard trying to proof you wrong. However this all is very sobering. Let me just reiterate what you have said in a different way to check whether I got you right: As soon as one moves slightly into the oop world with inheritance, the "private" statement will be the most redundant language feature. That is because one would constantly have to alter parent class code (having private components) as soon as new child classes are implemented. Am I right??

If so, any savety measures in terms of limited access to class components are impossible to implement as soon as inheritance is involved. Is that right??

That would also mean that component hiding in a structure as in my example, where an abstract class has all components already, but private, and child classes just implement the methods is impossible. Is that right??

Thanks for clarification.

0 Kudos
Highlighted
Beginner
13 Views

A possible work around might be this:

Module Mod_Types
  Type, Abstract :: parent
    Integer, private :: a
  contains
    Procedure(SubIF1), Pass, deferred :: SetA
  End type parent
  Type, extends(parent) :: Child
  contains
    Procedure, Pass :: SetA => SubSetA
  End type Child
  Abstract Interface
    Subroutine SubIF1(this)
      Import parent
      Class(parent), Intent(InOut) :: this
    end subroutine
  end Interface
  Interface
    Module Subroutine SubSetA(this)
      Class(child), Intent(InOut) :: this
    end subroutine
  end Interface
  Private :: SubSetA
End Module Mod_Types
Submodule(Mod_Types) Set
Contains
  Module Procedure SubSetA
  this%a=1
  write(*,*) this%a
  End Procedure
End Submodule Set
Program Test
  use Mod_Types
  Type(Child) :: x
  call x%setA()
End Program Test

However, from my understanding that has the following implications:

  • the part of the code in the module will be cluttered by interface definitions of all parents and childs
  • the namespace of the procedures encompasses all parents and childs making it necessary to "invent" unique names for every single child (e.g. "SubSetAin_A" for child A and "SubSetAin_B" for child B etc.)

Cheers

0 Kudos
Highlighted
Black Belt
13 Views

In reference to the three questions - no. 

Accessibility is based on modules, not on types.  If you want the procedure `SubSetA` original code to be able to access the private `a` component then SubSetA, or some appropriate portion of it, needs to be within Mod_Parent or its descendants. 

That might be inconsistent with (I'm guessing...) an implied programming style of having a type definition per module, but in that case it is the programming style that needs to be revised.

Consider that types do not "own" procedures in Fortran.  Instead, types may have bindings that reference procedures.  The procedures stand on their own, with their own identity.  It is quite possible (and useful, in some contexts) for one procedure to be bound to multiple types, procedures that are bound to a type do not have to be defined in the same module as the type (or any module, for that matter).  When compiling the body of `SubSetA` in the original code, there is nothing to tell the compiler that there is some sort of special relation between `SubSetA` and the `parent` type, or the Mod_Parent module that holds the `parent` type definition. 

Perhaps you are looking for something akin to protected accessibility in C++, where member functions of derived types (note that language has the possibility of a different ownership model for procedures and types) can access protected members of their base types.  Fortran does not have an equivalent to protected accessibility of components.

0 Kudos
Highlighted
Beginner
13 Views

Thanks.

With regard to the "no" and the second and third paragraph of your answer you basically trying to explain what I have implemented in the example(work around) above??

0 Kudos
Highlighted
Black Belt
13 Views

You could also keep the type definition in a separate module, and simply expose a procedure from the `Mod_Parent` module that permitted the operation on the a component of a polymorphic object of type `parent`.  It is just the operation on the private `a` component that needs to be within the effective scope of the `Mod_Parent` module.

The name of the extension type (and the interfaces of all its bindings) only needs to be defined in the specification part of the module if that name needs to be accessible outside of the module.  I don't know your requirements, but if the name of the extension type does not need to be accessible outside of the module, then you can pretty much just shove everything associated with the extension type into a submodule, bar a factory procedure of some sort.

Module Mod_Types
  Implicit WhateverIveMissed
  
  Private
  
  ! Clients of Mod_Types work with this type.
  Type, Abstract, Public :: parent
    Integer, private :: a
  contains
    Procedure(parent_SetA), Pass, Deferred :: SetA
  End type parent
  
  Abstract Interface
    Subroutine parent_SetA(this)
      Import parent
      Implicit None
      Class(parent), Intent(InOut) :: this
    end subroutine parent_SetA
  end Interface
  
  Interface
    Module Function MakeChild()
      Import parent
      Implicit None
      Class(parent), Allocatable :: MakeChild
    End Function MakeChild
  end Interface
  ! Clients of Mod_Types can also create specific varieties 
  ! of Parent with factory procedures like...
  Public :: MakeChild
End Module Mod_Types

Submodule (Mod_Types) Submod_for_child
  Implicit None
  
  Type, Extends(parent) :: child
  contains
    Procedure :: SetA
  End type child
Contains
  Module Function MakeChild()
    Class(parent), Allocatable :: MakeChild
    
    Type(child), Allocatable :: tmp
    Allocate(tmp)
    ! Construct tmp appropriately...
    
    Call Move_Alloc(tmp, MakeChild)
  End Function MakeChild
  
  Subroutine SetA(this)
    Class(child), Intent(InOut) :: this
    this%a=1
    write(*,*) this%a
  End Subroutine SetA
End Submodule Submod_for_child

Program Test
  Use Mod_Types
  Implicit None
  
  Class(parent), Allocatable :: x
  
  ! Requires F2008 support, not currently in ifort :(
  ! x = MakeChild()
  Allocate(x, SOURCE=MakeChild())
  
  Call x%setA()
End Program Test

(Head compiled, I don't have access to a Fortran compiler at the moment.)

There has been discussion here and on c.l.f in the last few years about limitations in the current language that interfere with some typical OOP patterns, whether your use case is also affected by these limitations depends on specifics.

0 Kudos
Highlighted
Beginner
13 Views

Thanks a lot.

Although the code may work it looks a bit like case interpretation in a legal battle (1+1-1+1-4+5=1+2). However, in my application childs need to be visible to the outside, and childs must be able to have more components than the parent, the inherited and their own. Then move_alloc wouldn't work. Thus I assume that my second example from above is the only way to implement oop.

0 Kudos
Highlighted
Black Belt
13 Views

(The use of MOVE_ALLOC is just a relatively efficient (source succinctness, perhaps performance) method of construction.  You can construct the object in whatever way you consider appropriate.  MOVE_ALLOC doesn't care about the number of components in each object - it just requires appropriate type compatibility between the FROM and TO arguments.)

What "OOP" is, or is not, is spectacularly subjective, but I don't consider #6 the only way.  You need to make the appropriate design choices given the tools that the language gives you and your requirements.  At the end of the day, the language's rules around accessibility are just aids provided by the language to authors of source code to help them write code that is consistent with their design.

To be clear about the first sentence of #9...

Module Mod_Parent
  Implicit None
  Private
  
  Type, Abstract, Public :: parent
    Integer, Private :: a
  Contains
    Procedure(parent_SetA), Deferred :: SetA
  End Type parent
  
  Abstract Interface
    Subroutine parent_SetA(this)
      Import parent
      Class(parent), Intent(InOut) :: this
    End Subroutine parent_SetA
  End Interface
  
  ! By making this procedure public, a fundamental 
  ! characteristic of objects that are of any type that 
  ! extends `parent` is that you can call `DoSetA` on 
  ! that object.  It is as fundamental to the 
  ! characteristics of extensions of `parent` 
  ! as any public component or binding.  If extensions 
  ! should not have such a capability, then perhaps they 
  ! shouldn't be extensions - the typical OOP consideration 
  ! of inheritance (i.e Liskov's substitution principle - 
  ! "an extension object is a parent object") versus 
  ! composition.
  Public :: DoSetA
Contains
  Subroutine DoSetA(arg)
    Class(parent), Intent(InOut) :: arg
    
    ! Presumed complexity elided.
    arg%a = ...
  End Subroutine DoSetA
End Module Mod_Parent


Module Mod_Child
  Use Mod_Parent
  Implicit None
  Private
  
  Type, Extends(parent), Public :: child
  Contains
    Procedure :: SetA => child_SetA
  End Type child
Contains
  Subroutine child_SetA(this)
    Class(child), Intent(InOut) :: this
    Call DoSetA(this)
  End Subroutine child_SetA
End Module Mod_Child


Program Test
  Use Mod_Child
  Type(Child) :: x
  Call x%SetA()
  
  ! As alluded to above, a possible issue is that there is nothing 
  ! to stop client code then doing "direct" operations via DoSetA.  
  ! Is this a problem?  That depends...  but Fortran's accessibility 
  ! is solely based on modules, so whatever design is chosen needs 
  ! to accommodate that.
  Call DoSetA(x)    ! Maybe bad, maybe we don't care.
End Program Test

 

0 Kudos
Highlighted
Honored Contributor I
13 Views

may.ka wrote:

.. That would also mean that component hiding in a structure as in my example, where an abstract class has all components already, but private, and child classes just implement the methods is impossible. Is that right?? ..

If your use case is indeed that "an abstract class has .. components already, but private, and child classes .. implement the methods", then you have to resort to something along the design suggested by IanH in message #10 where you implement "setter" procedures which are preferably not type-bound in order to maintain some "information hiding" from the callers who will most likely be 'use'ing the child modules and the extended types therein.

0 Kudos
Highlighted
Honored Contributor I
13 Views

ianh wrote:

..

Program Test
  Use Mod_Child
  ..
  
  ! As alluded to above, a possible issue is that there is nothing 
  ! to stop client code then doing "direct" operations via DoSetA.  
  ! ..
End Program Test

As shown, DoSetA from parent class is not accessible to the client.  So there exists a packaging option with modules for clients to prevent access to setters in the module containing the abstract types.

 

0 Kudos