Reputation: 305
The context to my question isn't really easy to describe, so suffice it to say that I have good reasons for attempting to structure a DLL F90 project such that -
A simplified version of my target structure would end up with something like:
- module1 (no dependencies)
- module2 (no dependencies)
- module3 (USEs module1)
- module4 (USEs module2)
- module5 (USEs module1 parameters and module3 methods)
- module6 (USEs module2 parameters and module4 methods)
- main DLL code (USEs all of the above modules)
Question:
Do I need to USE all of the modules explicitly in the main DLL code, or will variables and methods from the 'lowest' levels be inherited by simply using module5 and module6?
Or, does module5 need two USE statements (module1 AND module3) OR just module3?
And, I also want to access some of the global constants in, for example, module1 in my main DLL code (e.g. pi), so I need the module1 public variables to be in the global namespace.
Upvotes: 1
Views: 139
Reputation: 8566
Do I need to USE all of the modules explicitly in the main DLL code, or will variables and methods from the 'lowest' levels be inherited by simply using module5 and module6?
You don't need to USE` the modules all-the-way-down to dependencies just to access an entity if that entity was made public in the used module.
Or, does module5 need two USE statements (module1 AND module3) OR just module3?
Just by using module5
you can have access to:
module5
itself, marked as public
or protected
module5
, marked as public
by module5
(actually, public is the default accessibility if none was specified). If an entity is accessed by USE association from module1
or module3
, but marked as private
by module5
, it won't be accessible.I tried to cover as many situations as I could figure out in the following example. I used only variable declarations, but same would apply for variables, procedures, user defined types, operators, interfaces...
module module0
! Utiliy module with global constants, could be used at any level.
character(2) :: w = 'w0'
end
module module1
! Low level module. Exposes all the symbols that are not marked as private,
! both defined locally or accessed by use from 'module0'.
use :: module0
private :: z
character(2) :: x1 = 'x1', y1 = 'y1', z = 'z1'
! defined entities: w, x1, y1, z
! public entities : w, x1, y1
end
module module2
! Also low level module, but default access modifier was changed to private,
! so it exposes only symbols marked as public ('z' isn't).
use :: module0
public :: w, x2, y2
private
character(2) :: x2 = 'x2', y2 = 'y2', z = 'z2'
! defined entities: w, x2, y2, z
! public entities : w, x2, y2
end
module module3
! Uses all public names from 'module1' (including 'w' that is from 'module0'),
! but only exposes some of them. Also, defines and exposes local symbols.
use :: module1
private :: x3, y1
character(2) :: x3 = 'x3', y3 = 'y3'
! defined entities: w, x1, y1, x3, y3
! public entities : w, x1, y3
end
module module4
! Here, only selected symbols are accessed from 'module2', and 'w' has been
! renamed into 'w2' to avoid name-conflict with locally defined name 'w'
! (a compile error would had been thrown).
use :: module2, only: w2 => w, x2
public :: w, x2, y4
private
character(2) :: w = 'w4', x4 = 'x4', y4 = 'y4'
! defined entities: w, w2, x2, x4, y4
! public entities : w, x2, y4
end
module module5
! This module can use symbols from lowest level modules that are made public
! by 'module3', without explicitly using those modules.
use :: module3
character(2) :: z = 'z5'
! defined entities: w, x1, y3, z
! public entities : w, x1, y3, z
end
module module6
! As 'y2' is not exposed by 'module4', we could have access to it by using
! 'module2' directly. There is no name-conflict between 'w' from 'module0'
! and from 'module2' because both relate to the same entity. There would be
! conflict with 'w' from 'module4' though, hence the rename.
use :: module0
use :: module2
use :: module4, w4 => w
public :: w, x2, y4, z
private
character(2) :: z = 'z6'
! defined entities: w, w4, x2, y2, y4, z
! public entities : w, x2, y4, z
end
I strongly advise you to try to use explicit imports whenever posible, as it makes your code more clear and avoid name clashes. As a general rule, try to use distictive names for public entities in modules, or use rename clauses in USE statements.
This is a example of usage of the previous modules:
program main
! There aren't namespaces in Fortran (yet), so attention shall be paid when
! naming symbols meant to be accessed by use association, to avoid conflicts.
! That's why explicit imports are encouraged, as well as implicit none.
use :: module5
use :: module6, z6 => z
implicit none
character(2) :: v = 'v#'
call test_a
call test_b
call test_c
contains
subroutine test_a
! You can access used and defined names in procedures by host association.
print '(5a3)', v, w, x1, y3, z ! output: v# w0 x1 y3 z5
print '(5a3)', v, w, x2, y4, z6 ! output: v# w0 x2 y4 z6
end
subroutine test_b
! Also, you can use modules locally inside procedures. In this case, local
! names "shadow" host associated names, as with 'z' from 'module6' here.
use :: module6
print '(5a3)', v, w, x2, y4, z ! output: v# w0 x2 y4 z6
end
subroutine test_c
! There is no name-conflict error between host and local symbols; the local
! definition (or use association) always "shadows" the host associated name.
use :: module4, only: w
character(2) :: v = 'v_', z = 'z_'
print '(5a3)', v, w, x1, y3, z ! output: v_ w4 x1 y3 z_
end
end
For the sake of completeness, I would mention that the Fortran 2018 standard includes a new feature named Default accessibility for entities accessed from a module, that allows you to put a module name in a public
or private
statement, applying said accessibility modifier to all used entities from that module.
If a module a uses module b, the default accessibility for entities it accesses from b is public. Specifying another accessibility for each entity is awkward and error prone. It is now possible for the name of a module to be included in the list of names of entities made public or private on a public or private statement. This sets the default for all entities accessed from that module.
The only compiler I know today (2018) that includes this feature is Intel Visual Fortran 19.0.
Upvotes: 1