Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Trying to use an extended type

Status
Not open for further replies.

narlytep

Programmer
Apr 18, 2006
10
GB
Hi.
I have a piece of code that has several libraries sharing a common code base. The libraries are compiled from this common base plus their own bits of unique code.
One piece of data that all the libraries share is a "Phase Type", a derived type containing several common pieces of data. However each library needs to extend this out for their own extra data. We'd created the datatype to hold another extendable datatype that could hold this extra information. The idea was that the other libraries would contain code that would extend the type and set the data themselves. The set routines would also call teh base routines in the common code to set the common variables.

BUT. passing the extended type back into the common code is poving tricker than we hoped.
The problem seems to be that we can't pass the extended type back to the base routine that expects the original abstract base type.

Here's an example of what we're trying, with the compiler output below that. Does anyone have any suggestions on how to get this working please? (or alternatives that would acieve the same thing :)

Cheers.
Duncan

!------------------------------------
module phase_mod
implicit none
save
type, abstract :: PHASEINFO_TYPE
integer(kind=4) :: TYPE_ID
end type PHASEINFO_TYPE
type :: Phase_tp
integer(kind=4) :: nghosts
class (PHASEINFO_TYPE), pointer :: phaseinfo => null()
end type Phase_tp

! This bit is unique to one of the libraries
type, extends(PHASEINFO_TYPE) :: Ext_Phase_tp
integer(kind=4) :: keySetID ! KeySet associated with this
integer(kind=4) :: ghostsMin ! Default Ghost layer range
integer(kind=4) :: ghostsMax ! Default Ghost layer range
end type Ext_Phase_tp

type(Phase_tp), target :: iPhase
type(Ext_Phase_tp), pointer :: PhaseInfo=>null()

contains
! sets up a base Phase type. Calling program must pass in the phaseinfo type
integer function addBasePhase(nghosts, aPhaseInfo)
integer, intent(in) :: nghosts
class (PHASEINFO_TYPE), pointer, optional :: aPhaseInfo ! intent(in)

iPhase%nghosts = nghosts
iPhase%phaseinfo => aPhaseInfo

addBasePhase = 0
return
end function addBasePhase

! The unique library will call this routine to set up the phase info type and pass it back
integer function addPhase(nghosts, keysetid, gmin, gmax)
integer, intent(in) :: nghosts
integer, intent(in) :: keysetid
integer, intent(in) :: gmin
integer, intent(in) :: gmax
integer :: irc
allocate(PhaseInfo)
PhaseInfo%KeysetID = keysetid
PhaseInfo%ghostsMin = gmin
PhaseInfo%ghostsMax = gmax
! Two ways we tried this bit:
irc=addBasePhase(nghosts,PhaseInfo)
! irc=addBasePhase(nghosts,PhaseInfo%PHASEINFO_TYPE)
addPhase = 0
return
end function addPhase

! Trying to see if what we had worked!
integer function getPhase(phase, phaseInfo)
type(Phase_tp), pointer :: phase
type(Ext_Phase_tp), pointer :: phaseInfo

phase => iPhase
select type (p=>iPhase%phaseinfo)
type is (Ext_Phase_tp)
print*,"Type found!"
phaseInfo=>p
class is (PHASEINFO_TYPE)
print*,"Error! Type is wrong!"
class default
print *,"Error! Type not found"
end select
getPhase = 0
return
end function getPhase

end module phase_mod

program extend

use phase_mod
implicit none
integer :: irc
integer :: ID
type(Phase_tp), pointer :: myphase
type(Ext_Phase_tp), pointer :: myphaseInfo
irc = addPhase(3,1,0,3)
irc = getPhase(myphase, myphaseInfo)
print*,myphaseInfo%KeysetID
print*,myphaseInfo%ghostsMin
print*,myphaseInfo%ghostsMax

end program extend
!------------------------

With the intel compiler we get:
using: irc=addBasePhase(nghosts,PhaseInfo)

ifort extend.f90

extend.f90(XX): error #6633: The type of the actual argument differs from the type of the dummy argument. [PHASEINFO]
irc=addBasePhase(nghosts,PhaseInfo)
-----------------------------^

Using irc=addBasePhase(nghosts,PhaseInfo%PHASEINFO_TYPE)
!
! ifort extend.f90
!
! extend.f90(XX): error #8307: If the rightmost part-name is of abstract type, data-ref shall be polymorphic [PHASEINFO_TYPE]
! irc=addBasePhase(nghosts,PhaseInfo%PHASEINFO_TYPE)
! ---------------------------------------^
! extend.f90(XX): error #8292: If a dummy argument is allocatable or a pointer, the associated actual argument shall be polymorphic if and only if the dummy argument is polymorphic. [PHASEINFO_TYPE]
! irc=addBasePhase(nghosts,PhaseInfo%PHASEINFO_TYPE)
! ---------------------------------------^
! extend.f90(47): error #6691: A pointer dummy argument may only be argument associated with a pointer. [PHASEINFO_TYPE]
! irc=addBasePhase(nghosts,PhaseInfo%PHASEINFO_TYPE)
! ---------------------------------------^
!
 
I admit this is way over my head as I haven't had to define types just yet or do the kind of trickery you are showing...I haven't even tried to fully understand it...and that's one more reason to visit the forums...to learn.

So, forgive me if my comments are not applicable...but here I go.

You talk about common code, so, I hope you have the source code and not the pre-compiled library of the library that you are trying to extend...so, you can actually modify such source code of the 'parent' library, right? :

do you have to extend the parent type?
what if the parent type was already implemented the way the child library needs it?
can you include an include into the parent library to customize it at compile type?
the include would come from the child library
in other words, when you are compiling the child library, your make file could setup the paths to the include files, etc; and, so, the parent file could include a (custom) different include file depending on which child library is compiling them all.

sorry for my ignorance and resorting to good old 'include'

 
Try this
Code:
   ! sets up a base Phase type. Calling program must pass in the phaseinfo type
   integer function addBasePhase(nghosts, aPhaseInfo)
      integer, intent(in) :: nghosts
      class (PHASEINFO_TYPE), [COLOR=red]target[/color] :: aPhaseInfo ! intent(in)

      iPhase%nghosts = nghosts
      iPhase%phaseinfo => aPhaseInfo

      addBasePhase = 0
      return
   end function addBasePhase
The problem is that if you pass pointers, they must be of the same type but if you pass types, they can be either base classes or derived classes.
 
slagerman: welcome to the world of OO. The coding is new in the 2003 standard. Basically the old way of doing it would involve a lot of transfers or use of equivalence. It would be something like this: not as elegant or as efficient as the OO solution. If I were using F90 or F95, I'd have to find a way around polymorphism using equivalence and it gets really messy with compilers not wanting to play ball. On the old DEC/MS compilers, you could use unions to get around it but it is still quite messy.

Having said that, I don't quite agree with having the select type clause. It defeats the whole purpose of OO. Unfortunately, like Java text books, all the books have it so everyone follows.
Code:
module phase_mod
   implicit none
   type :: PHASEINFO_TYPE
      integer:: TYPE_ID
   end type PHASEINFO_TYPE

   type:: PHASEINFO_GENERIC
      type (PHASEINFO_TYPE):: base
      integer, dimension(10):: whatever
   end type
   
   type :: Phase_tp
      integer :: nghosts
      type (PHASEINFO_GENERIC), pointer :: phaseinfo => null()
   end type Phase_tp

   ! This bit is unique to one of the libraries
   type :: Ext_Phase_tp
      type(PHASEINFO_TYPE):: base
      integer :: keySetID ! KeySet associated with this
      integer :: ghostsMin ! Default Ghost layer range
      integer :: ghostsMax ! Default Ghost layer range
   end type Ext_Phase_tp

   type(Phase_tp), target :: iPhase

contains
   ! sets up a base Phase type. Calling program must pass in the phaseinfo type
   integer function addBasePhase(nghosts, aPhaseInfo)
      integer, intent(in) :: nghosts
      type (PHASEINFO_GENERIC), target :: aPhaseInfo ! intent(in)

      iPhase%nghosts = nghosts
      iPhase%phaseinfo => aPhaseInfo

      addBasePhase = 0
      return
   end function addBasePhase

   ! The unique library will call this routine to set up the phase info type and pass it back
   integer function addPhase(nghosts, keysetid, gmin, gmax)
      integer, intent(in) :: nghosts
      integer, intent(in) :: keysetid
      integer, intent(in) :: gmin
      integer, intent(in) :: gmax
      integer :: irc
      type (Ext_Phase_tp) :: PhaseInfo
      type (PHASEINFO_GENERIC), pointer:: generic
      allocate(generic)
      PhaseInfo%base%type_id = 1
      PhaseInfo%KeysetID = keysetid
      PhaseInfo%ghostsMin = gmin
      PhaseInfo%ghostsMax = gmax
      generic = transfer(PhaseInfo, generic)
      irc=addBasePhase(nghosts, generic)
      addPhase = 0
      return
   end function addPhase

   ! Trying to see if what we had worked!
   integer function getPhase(phase, phaseInfo)
      type(Phase_tp), pointer :: phase
      type(Ext_Phase_tp) :: phaseInfo

      phase => iPhase
      select case (iPhase%phaseinfo%base%type_id)
      case (1)
         print*,"Type found!"
         phaseInfo = transfer(iPhase%phaseinfo, phaseInfo)      
      case default
         print *,"Error! Type not found"
      end select
      getPhase = 0
      return
   end function getPhase

end module phase_mod

program extend

   use phase_mod
   implicit none
   integer :: irc
   type(Phase_tp), pointer :: myphase
   type(Ext_Phase_tp), pointer :: myphaseInfo
   irc = addPhase(3,1,0,3)
   irc = getPhase(myphase, myphaseInfo)
   print*,myphaseInfo%KeysetID
   print*,myphaseInfo%ghostsMin
   print*,myphaseInfo%ghostsMax

end program extend
 
Yeah, thanks...we shall see when I adopt OO in Fortran...to tell you the truth, one reason why I liked Fortran so much is for its simplicity...now that they have introduced all sorts of features to make it look like C, C++ or Java, I am not liking so much...or maybe I just need to get with the program. For now, I can thankfully at least stay away from it...unlike Java where you simply can't stay away from objects.

I did buy and read a Fortran 90 book, and I am taking advantage of it and moved to F90...I guess I need to buy a 2003/2008 book, now.

 
Which compiler compiles the example above to runnable executable?

g95 doesn't compile it:
Code:
$ g95 extend.f95 -o extend
In file extend.f95:25

   type(Phase_tp), target :: iPhase
                             1
Error: Module variable 'iphase' at (1) with a component initialization must have the SAVE attribute

gfortran compiles it, but trying to run it I got only an message box with error:
Code:
[b]extend.exe has stopped working[/b]

A problem caused the program to stop working correctly. Windows will 
close the program and notify you if a solution is available.
 
Hi.
Thanks for the replies. xwb, your first suggestion of setting it as a target seemed to do the trick.

Your longer example just segfaults for me when it tries to access the data within the type.

salgerman, yes, it's all common source code.
We're writing two libraries that share a lot of common code, but have their own additional code. One of the routines that is common is a registry routine. Some registy data is common to both libraries, but each library also requires some extra data. Hence the need to extend the type. Ideally we'd like to avoid having to alter the base common registry and simply have the extended routines separate in the relevant librariy's subdir of code.

Plan B (if the OO thing didn't work) was to set up the base type as:

Code:
type reg_data
 integer :: ID
 type(lib1_tp) :: reg_data_lib1
 type(lib2_tp) :: reg_data_lib2
end type_reg_data

and then pass in the relevant type. If we added a third library to the set down the line then we'd need to edit this routine again, which we wouldn't have to do for the OO case.

Having said all that, plan B is still looking tempting as the select type approach is getting very complicated :)
 
You could set it as
Code:
type reg_data
   integer::ID
   integer::reg_data_type
   type(lib1_tp), pointer:: reg_data_lib1
   type(lib2_tp), pointer:: reg_data_lib2
end reg_data
That would save a bit of space. Also, reg_data_type would tell you which one was populated so you wouldn't have to guess.
 
Ah, yes. I did mean for them to be pointers of course. :)
Good idea about the reg_data_type flag.
Thanks!
 
Let us know how you get on with the pointer technique. If it gets too complex, then you possibly need to change your design.

OO is one of those things that you don't just write programs without thinking it through and drawing a few designs on paper first, especially when it is something complex. It might be worth your while planning it out in UML first.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top