How to use derived type in a derived type?

I am extremely new to OOP in Fortran, so I need guidance. I started to code a blackjack game for practice in Fortran OOP. I have a Card_Class and a Hand_Class which holds a variable amount of cards (2 cards and up). The plan is to initialize the hand with two default cards so I need to allocate 2 cards initially to the hand, I am having trouble with this. Also, I have errors when adding cards to the hand, my plan was to have subroutine add_to_hand take in a hand and a card and return a newly instantiated hand allocated with one more spot in the array, placing the new card in that spot. Please have a look at my code and propose some fixes for my mistakes.

Card_Class module Card_Class

implicit none

type, public :: card_obj
private
character(len=3) :: face = “NOF”
integer :: rank = 0
integer :: hardValue = 0
integer :: softValue = 0

contains
procedure :: setFace => set_face
procedure :: setRank => set_rank
procedure :: setHardVal => set_hard_val
procedure :: setSoftVal => set_soft_val
procedure :: getFace => get_face
procedure :: getRank => get_rank
procedure :: getHardVal => get_hard_val
procedure :: getSoftVal => get_soft_val
end type

contains
…

Hand_Class

module Hand_Class

use Card_Class

implicit none

integer :: istat

type, public :: hand_obj
type(card_obj), dimension(:), allocatable :: hand
allocate(hand(2), STAT=istat)
contains
procedure :: addToHand => add_to_hand
procedure :: resetHand => reset_hand
end type

contains

subroutine add_to_hand(this, hand, card, modHand)
class(hand_obj) :: this
type(card_obj) :: card
type(hand_obj), dimension(: ) :: hand
type(hand_obj), dimension(:), allocatable :: modHand
integer :: handSize, newHandSize, i

handSize = size(hand)
newHandSize = handSize + 1

allocate(modHand(newHandSize), STAT=istat)

do i = 1, handSize
modHand(i) = hand(i)
end do

modHand(newHandSize) = card

return
end subroutine add_to_hand

subroutine reset_hand(this, blankHand)
class(hand_obj) :: this
type(hand_obj), dimension(:), allocatable :: blankHand

deallocate(this, STAT=istat)
allocate(blankHand(2), STAT=istat)
end subroutine reset_hand

end module Hand_Class

The errors I am getting are from gcc 12.2. Line 11 where I am first allocating the hand, I get the following error, Allocate-object at (1) is neither a data pointer nor an allocatable variable. Line 35 where I am putting the new card into the new modified hand, I get the following error, Cannot convert TYPE(card_obj) to TYPE(hand_obj) at (1). And on line 44 where I am deallocating the old hand, I get the following same error as line 11, Allocate-object at (1) is not a nonprocedure pointer nor an allocatable variable.

Thanks, William

@William_S ,

If you can make your whole code available, readers might be able to provide specific suggestions.

A very quick perusal shows the so-called executable instructions such an ALLOCATE statement is interspersed with the so-called type declaration section which is not supported by the language. So you can consider moving such statements to a subprogram that can be invoked for an object of said type.

1 Like

I had mixed up what was an array and what was not, the variable hand and the class hand respectively. I solved my problems by getting that straight in my head. However, for the initial allocation, I created an init_hand subroutine to handle it like you suggested. This iteration of the code actually compiles and runs correctly.

module Hand_Class

use Card_Class

implicit none

integer :: istat

type, public :: hand_obj
type(card_obj), dimension(:), allocatable :: hand

contains
procedure :: initHand => init_hand
procedure :: addToHand => add_to_hand
procedure :: resetHand => reset_hand
end type

contains

subroutine init_hand(this)
class(hand_obj), intent(INOUT) :: this

allocate(this%hand(2), STAT=istat)

return
end subroutine init_hand

subroutine add_to_hand(this, card)
class(hand_obj), intent(INOUT) :: this
type(hand_obj) :: temp
type(card_obj), intent(IN) :: card
integer :: handSize, newHandSize, i

handSize = size(this%hand)
newHandSize = handSize + 1
temp = this

deallocate(this%hand, STAT=istat)
allocate(this%hand(newHandSize), STAT=istat)

do i = 1, handSize
this%hand(i) = temp%hand(i)
end do

this%hand(newHandSize) = card

return
end subroutine add_to_hand

subroutine reset_hand(this)
class(hand_obj), intent(INOUT) :: this

deallocate(this%hand, STAT=istat)
allocate(this%hand(2), STAT=istat)

return
end subroutine reset_hand

end module Hand_Class

Thank you for your help.