Problem:
Fortran’s OOP model makes it somewhat awkward to expose array components through accessor methods using pointers. Returning a pointer to an array component requires either the calling object to carry the target attribute, or the component itself to be declared as a pointer — both of which come with non-trivial trade-offs. There is no clean, zero-overhead way to simply hand out a reference to an internal array the way you might in C++.
I’ve come up with two reasonable approaches to work around this, and I’d love to hear from the community on best practices. Have you run into this pattern before? Which option do you prefer and why? Is there a third option I’m missing entirely?
Option 1: Allocatable array component + target attribute on the object
type, public :: mytype_t
!! Any instantiated object must have the target or pointer attribute for the accessor function to work correctly.
private
real(RWP), dimension(:), allocatable :: myarray
contains
procedure, pass(self), public :: get
end type mytype_t
contains
function get(self) result(myarray)
implicit none
class(mytype_t), target, intent(in) :: self
real(RWP), dimension(:), pointer, contiguous :: myarray
myarray => self%myarray
end function get
Pros:
- No manual memory management
Cons:
- The class is not truly self-contained — any object instantiated from
mytype_tde facto requires thetarget(orpointer) attribute for the accessor to work correctly. This compounds whenmytype_tobjects are nested inside other objects, because thetargetattribute must propagate all the way up to the outermost object.
Option 2: Pointer array component
type, public :: mytype_t
private
real(RWP), dimension(:), pointer, contiguous :: myarray => null()
contains
procedure, pass(self), public :: get
final :: finalize
end type mytype_t
contains
function get(self) result(myarray)
implicit none
class(mytype_t), intent(in), target :: self
real(RWP), dimension(:), pointer, contiguous :: myarray
myarray => self%myarray
end function get
subroutine finalize(self)
implicit none
type(mytype_t), intent(inout) :: self
if (associated(self%myarray)) then
deallocate (self%myarray)
end if
end subroutine finalize
Pros:
- Truly self-contained — instantiated objects require no
targetorpointerattribute on the caller’s side
Cons:
- Pointer arrays generally have worse performance than allocatables due to the possibility of non-contiguity and aliasing
- Requires finalizer to avoid memory leaks
Questions:
- Which of these two patterns do you reach for in practice, and why? Are there other approaches I haven’t considered?
- Does marking something as
contiguousactually improve performance in practice—specifically, does it let the compiler optimize it as well as allocatable arrays (e.g., for aliasing and stride-1 access), and is there any benchmark evidence showing it removes the performance gap? - For those working on large codebases: does the self-containment of Option 2 outweigh the performance and memory-management overhead in your experience? I’m particularly interested in hearing from people who have used either pattern in performance-sensitive code. Any insights or references are welcome!