Interface Blocks
Overview
Teaching: 10 min
Exercises: 5 minQuestions
What is an interface block?
How can one be used to create a constructor for a derived type?
Objectives
How do you create an interface block.
As mentioned previously it is a very common task to create new objects of a derived type and perform some initialization of the members. We have already created some functions to do this. One of the functions, create_empty_vector
takes no arguments and creates a new empty vector, while create_sized_vector
creates a new vector of a specific size passed to the function. Both of these functions do the same thing, create a new t_vector
object and initialize it. One might imagine many different such initialization routines, perhaps ones that take another t_vector
or t_vector_3
objects to use to initialize a new t_vector
object as a copy of the passed vector. All of these creation, or initialization, functions do basically the same thing but in a slightly different way depending on the arguments passed to them. It starts to get a bit tedious to have to remember all the names of these different initialization functions. If the compiler could somehow distinguish these functions automatically based on the number and type of arguments rather than the procedure name so that we could call the same generic procedure name and it would pick the correct procedure implementation based on the arguments we passed it.
It turns out there was a feature added to Fortran 2003, called interface blocks which allow multiple procedures to be mapped to one name. The basic syntax of an interface block is as follows.
interface <new-procedure-name>
procedure:: <existing-procedure-name-1>
procedure:: <existing-procedure-name-2>
...
end interface
This allows one to call the procedure <new-procedure-name>
and it will be mapped onto different procedure implementations <existing-procedure-name1>
, <existing-procedure-name-2>
, etc. based on the type, number, and order of arguments passed to the procedure when calling it. Since the number, type, and order of arguments is the only way for the compiler to know which procedure to call, all procedures listed in the interface block must have different types and or number of arguments.
Lets use interface blocks to group our creation functions for t_vector
and t_vector_3
into one procedure name to initialize each of the derived types. It is common to use the name of the derived type as the name of the creation function which returns a new object of that type. Functions defined in this way are referred to as constructors as they construct new objects of the derived type.
$ cp type_extension.f90 interface_blocks.f90
$ nano interface_blocks.f90
module m_vector
implicit none
type t_vector
...
end type
interface t_vector
procedure:: create_empty_vector
procedure:: create_sized_vector
end interface
type,extends(t_vector):: t_vector_3
end type
interface t_vector_3
procedure:: create_size_3_vector
end interface
contains
type(t_vector) function create_empty_vector()
...
end function
type(t_vector) function create_sized_vector(vec_size)
...
end function
type(t_vector_3) function create_size_3_vector()
...
end function
end module
program main
use m_vector
implicit none
type(t_vector) numbers_none,numbers_some
type(t_vector_3) location
numbers_none=t_vector()
print*, "numbers_none%num_elements=",numbers_none%num_elements
numbers_some=t_vector(4)
numbers_some%elements(1)=2
print*, "numbers_some%num_elements=",numbers_some%num_elements
print*, "numbers_some%elements(1)=",numbers_some%elements(1)
location=t_vector_3()
location%elements(1)=1.0
print*, "location%elements(1)=",location%elements(1)
end program
$ gfortran interface_blocks.f90 interface_blocks
$ ./interface_blocks
numbers_none%num_elements= 0
numbers_some%num_elements= 4
numbers_some%elements(1)= 2.00000000
location%elements(1)= 1.00000000
The way we have used interface blocks above is what is called a generic interface as it maps one generic procedure name we specified to multiple specific procedures. This is very similar to what other object oriented languages call overloading.
There is another way to use interface blocks without specifying a generic procedure name to map the listed procedures to, referred to as explicit interfaces. Explicit interface blocks can be used to define a procedure without actually listing the implementation of it. These are useful when using procedures declared in different compilation units which will be linked into the final program later. This is a bit like a forward declaration or a function prototype in C/C++. If your procedures are declared inside a module, as we have been doing, these explicit interfaces are created for you.
Add a procedure to an interface
What happens if we add the
create_size_3_vector
to thet_vector
generic interface? Make a copy of our last source file and add the lineprocedure:: create_size_3_vector
to thet_vector
interface as shown below.$ cp interface_blocks.f90 interface_test.f90 $ nano interface_test.f90
module m_vector implicit none type t_vector ... end type interface t_vector procedure:: create_empty_vector procedure:: create_sized_vector procedure:: create_size_3_vector end interface ...
$ gfortran -o interface_test interface_test.f90
What happens when you try to compile and run it and why?
Solution
During compilation the following error message is printed out
... Error: Ambiguous interfaces in generic interface 't_vector' for ‘create_empty_vector’ at (1) and ‘create_size_3_vector’ at (2) ...
This is because the
create_empty_vector
and thecreate_size_3_vector
functions both have no arguments. Since the compiler uses the number and type of arguments to decide which function to call there is no way for the compiler to know which function should be called when no arguments are given.
Key Points
An interface block can act as either a block of definitions of your procedures (explicit interface) or as a means of associating different procedures with one common name (generic interface).
Procedures that are part of the same generic interface block must be distinguishable from each other based on the number, order, and type of arguments passed.