Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

Encapsulation

OP1
New Contributor III
707 Views
Is it possible to encapsulate the data contained in a derived type? Assume I defined a derived type MY_TYPE which contains a number of variables. I do not want the user of my libraries to have direct access to the internal content of structures of type MY_TYPE. Instead, they should only access this data through dedicated subroutines associated to MY_TYPE.

It doesn't seem possible to do this so far. Is there a workaround?

Olivier
0 Kudos
5 Replies
Steven_L_Intel1
Employee
707 Views
Sure there is. Just add the PRIVATE keyword on the line following TYPE. For example:

[plain]type mytype
  private
  integer cant_see_me
  end type mytype[/plain]
Now users of the module can see the type, but they can't see the components. Only the module in which mytype is declared can see the components.
0 Kudos
OP1
New Contributor III
707 Views
Yes, this is true - but doesn't that mean that all the subroutines that access this type need to be declared in the same module as well? Or maybe only their interface needs to be defined in the private section of the module?

If you have many big subroutines that handle instances of the derived type, and if you need to write them in the module where the type is defined, then the module becomes unwieldy.

Do I understand this correctly?

Thanks Steve,

Olivier
0 Kudos
John4
Valued Contributor I
707 Views
Quoting - opmkl
Yes, this is true - but doesn't that mean that all the subroutines that access this type need to be declared in the same module as well? Or maybe only their interface needs to be defined in the private section of the module?

If you have many big subroutines that handle instances of the derived type, and if you need to write them in the module where the type is defined, then the module becomes unwieldy.

Do I understand this correctly?

Thanks Steve,

Olivier

I don't see the problem here, but you could try something like:

[vb]module mod1

    implicit none
    private
    save

    type, public :: mytype
        private
        integer :: hidden
    end type

    public set_mytype, handle_mytype, unset_mytype

contains
    include './set_mytype.f90'
    include './handle_mytype.f90'
    include './unset_mytype.f90'
end module mod1[/vb]

0 Kudos
IanH
Honored Contributor III
707 Views
Quoting - opmkl
Yes, this is true - but doesn't that mean that all the subroutines that access this type need to be declared in the same module as well? Or maybe only their interface needs to be defined in the private section of the module?

I think you mean't this but being a little more specific: all the procedures that want to access private components of a type need to be in the same module that declares the type. Procedures that access only public components can be in different modules or sub-programs.

In addition to INCLUDE, nesting of types might help. Your top level derived type has a private component that only things in its enclosing module can access, but that component is itself a user defined type with all components having public accessibility.

Some (not tested) code to demonstrate...

[cpp]MODULE ImplentationMod
  IMPLICIT NONE
  ! Everything is public, anyone can play with everything
  TYPE :: NestedStuff
    INTEGER :: member1
    REAL :: member2
    ! etc...
  END TYPE NestedStuff
END MODULE ImplementationMod

MODULE LibraryMod
  USE ImplementationMod
  IMPLICIT NONE
  PRIVATE
  PUBLIC :: Op1, Op2
  TYPE, PUBLIC :: MyOpaqueHandle
    PRIVATE
    ! Routines that access implementation have to be in this module
    TYPE(NestedStuff) :: implementation
    ! ...perhaps other private members, or some public ones
    ! that we don't mind the users fiddling with...
  END TYPE MyOpaqueHandle
CONTAINS
  ! perhaps this creates/initialises a MyOpaqueHandle object
  SUBROUTINE Op1(this)
    USE AnotherModule
    TYPE(MyOpaqueHandle), INTENT(OUT) :: this
    !******
    ! Procedures outside this module can't access implementation in 
    ! MyOpaqueHandle variables directly.  So we have a series of little 
    ! forwarding routine to expose that implementation member.
    CALL Sub1InAnotherModule(this%implementation)
  END SUBROUTINE Op1

  ! Perhaps this does some calcs with a MyOpaqueHandleObject
  SUBROUTINE Op2(this, a, b)
    USE ADifferentModule
    TYPE(MyOpaqueHandle), INTENT(IN) :: this
    REAL, INTENT(OUT) :: a, b
    !******
    ! Another forwarding piece
    CALL Sub2InADifferentModule(this%implementation, a, b)
    ! Of course, could be calls of the form...
    CALL Sub3InADifferentModule(this%implementation%member1, a, b)
  END SUBROUTINE Op2

END MODULE LibraryMod

PROGRAM UserCode
  USE LibraryMod
  IMPLICIT NONE
  TYPE(MyOpaqueHandle) :: thing
  REAL arg1, arg2
  !******
  ! Following would give a compile time error message...
!  thing%implementation%member1 = ...	
  ! this too...
!  call Sub1InAnotherModule(thing%implementation)
  ! But these are ok, library writer has control
  CALL Op1(thing)   
  CALL Op2(thing, arg1, arg2)
END PROGRAM

[/cpp]
Your gazillions of worker subroutines (in AnotherModule and ADifferentModule and ...) just USE ImplementationMod and can do whatever they please, user code works with the wrapper type MyOpaqueHandle, and must work through the forwarding routines defined in LibraryMod. You do need to write/have the overhead of the LibraryMod forwarding/wrapper layer though.

IanH
0 Kudos
Steven_L_Intel1
Employee
707 Views
Please don't use INCLUDE.

If you have a set of routines to access an opaque type, they must all be contained in the module that declares the type. This is the recommended way of "encapsulating" in Fortran. Other program units that USE the module can declare objects of the type and can pass them as arguments, but can't see inside them.
0 Kudos
Reply