The new implemention of the Fortran binding of PLplot takes full
advantage of the ISO_C_BINDING
feature of the
Fortran 2003 standard, which is supported by all current compilers.
The advantage of this approach is that the entire binding is now
written in Fortran, so that there is only one library that calling
programs need to link against. Furthermore, the binding defines
overloaded routines for the case of either single- or double-precision
arguments supplied by the calling programme regardless of the floating-point
precision of the underlying C library. That makes this binding much
easier to use than our previous implementation of the Fortran binding
where calling routines were forced to use the same floating-point precision that was
configured for the underlying PLplot C library.
Note: in this chapter “Fortran” stands for “Fortran as defined by the Fortran 2003 standard”. Older versions of PLplot supported FORTRAN 77, but the binding for this 40 years old version has been abandoned for quite a few years now. As we now use features from the Fortran 2003 standard, it is no longer appropriate to refer to the language as Fortran 95.
We illustrate the implementation of our Fortran binding using the
API as an example. The
summary of the C
API for that routine which best serves our purposes here is
plstring
void plstring( PLINT n, const PLFLT *x, const PLFLT *y, const char *string );
The arguments n
, x
,
y
, and string
represent the
number of times the string is plotted, the arrays of length
n
which contain the x, y
values where that string is plotted, and
the NULL-terminated C string that contains the ordinary (not
wide) characters in the UTF-8 encoding of a unicode glyph to be
plotted. The PLplot PLINT
type is normally defined
as the C fundamental type int32_t
, and the PLplot
PLFLT
type is defined to be one of the two C
fundamental types float
or
double
depending on how the C PLplot library
is configured.
Here is an example of one fairly typical Fortran call of plstring
.
program test_plplot use plplot implicit none integer, parameter :: my_real = kind(1.0) real(kind=my_real), dimension(6) :: x, y ... x = ... y = ... ... call plstring(x,y,"+") ... end program test_plplot
where for this particular case x
and y
are arrays with 6
elements defined and the points are to be plotted using the ascii "+"
symbol (although if you are using a unicode-aware PLplot device, then
you can try many other unicode possibilities for the symbol such as
the U+22C5 DOT OPERATOR, "⋅"). Note that our Fortran binding
implementation below allows use of the kind(1.0d0)
choice of
my_real
precision as well.
The plstring
-relevant parts of the plplot
module used above are
module plplot ... use plplot_single use plplot_double ... end module plplot
The redacted part of the plplot
module implements the interfaces to
the PLplot C library routines that happen to have no floating-point
arguments very similarly to the way that the plplot_single
and
plplot_double
modules interface the PLplot C routines like plstring
that do include floating-point arguments. The plstring
-relevant parts of the
plplot_single
module are
module plplot_single ... integer, parameter :: wp = private_single ... interface plstring module procedure plstring_impl end interface plstring private :: plstring_impl ... contains ... subroutine plstring_impl( x, y, string ) real(kind=wp), dimension (:), intent(in) :: x, y character(len=*), intent(in) :: string integer(kind=private_plint) :: n_local interface subroutine interface_plstring( n, x, y, string ) bind(c,name='c_plstring') import :: private_plint, private_plflt implicit none integer(kind=private_plint), value, intent(in) :: n real(kind=private_plflt), dimension(*), intent(in) :: x, y character(len=1), dimension(*), intent(in) :: string end subroutine interface_plstring end interface n_local = size(x, kind=private_plint) if(n_local /= size(y, kind=private_plint) ) then write(error_unit,"(a)") "Plplot Fortran Warning: plstring: inconsistent sizes for x and y" end if call interface_plstring( n_local, real(x,kind=private_plflt), real(y,kind=private_plflt), & trim(string)//c_null_char ) end subroutine plstring_impl ... end module plplot_single
The plstring
-relevant parts of the plplot_double
module are defined
identically (in fact that identicality is guaranteed by using the
same included file to define the identical parts) except for
integer, parameter :: wp = private_double
Here are some notes on the above implementation of our Fortran
binding for plstring
. The
plplot_single
and plplot_double
modules implement two versions of the Fortran
plstring
subroutine which are identical except one
subroutine has floating-point arguments with a kind value of
wp = private_single = kind(1.0)
and one subroutine
has floating-point arguments with kind value of wp =
private_double = kind(1.0d0)
. The result is the Fortran
compiler automatically chooses the correct overloaded version of
plstring
that corresponds to the precision of the
floating-point arguments used by the program (e.g., like
test_plplot
above) that is being compiled. The
intrinsic function size()
is used to determine the
size of arrays and allows checking that their dimensions are
consistent with each other when the C implementation uses a common
size for the arrays as in the plstring
case. (See also,
bindings/fortran/README_array_sizes
.) The
intrinsic function real()
is used to convert
floating-point data between the type used by the calling routine and
the type used by the underlying PLplot C library, and the intrinsic
function int()
(not used in the above example) is
used for similarly converting integer data. The intrinsic function
trim()
and the ISO_C_BINDING
parameter c_null_char
are used to help convert
a Fortran character string into a NULL-terminated C string.
Also note the above interface block defining
subroutine interface_plstring
is the
Fortran representation of the exact C API of plstring
.
Here is a table summarizing how C data types correspond to
Fortran data types in the arguments of functions defined by our
Fortran binding. Consult the Fortran code in
bindings/fortran/*
for further details of how the
conversion is done between our private Fortran types that are
equivalent to the corresponding C types, and the public Fortran types
that are available for Fortran function arguments in our Fortran
binding. Note the my_flt
kind value used in this
table is not provided by our Fortran binding. Instead it merely
indicates that the calling routine (e.g., the
test_plplot
example routine above) has the choice
of either kind(1.0)
or
kind(1.0d0)
for the kind values of the
floating-point arguments of the PLplot functions defined by our
Fortran binding.
C type | Private Fortran type | Public Fortran type |
---|---|---|
PLFLT | real(kind=private_plflt) | real(kind=my_flt) |
PLFLT * | real(kind=private_plflt), dimension(*) | real(kind=my_flt), dimension(:) |
PLFLT ** | type(c_ptr), dimension(*) | real(kind=my_flt), dimension(:, :) |
PLINT | integer(kind=private_plint) | integer |
PLINT * | integer(kind=private_plint), dimension(*) | integer, dimension(:) |
PLBOOL | integer(kind=private_plbool) | logical |
char * | character(len=1), dimension(*) | character(len=*) |
In C there are two ways to pass a variable --- by value (the default)
or by reference (pointer), whereas in Fortran this difference is not
visible in the call, only in the interface definition via the
value
attribute. Therefore when you see references
in the documentation of our C API to either an
ordinary argument or a pointer argument (e.g.
*data
), you simply use an ordinary Fortran variable
or array name. The new Fortran binding automatically takes care of
any conversion that may be necessary.
In sum, the plstring
example above
illustrates the way our Fortran binding makes the PLplot C API
conveniently accessible from Fortran while letting the C binding and
overloading features of the Fortran compiler hide the complexities of
the name mangling that occurs.
Users should be aware that there are a few cases with our new Fortran binding where we provide double-precision floating-point entities but no equivalent single-precision floating-point alternative.
The Fortran standard dictates that compilers cannot disambiguate
overloaded functions based on the type of their return value. This
means that the plrandd
function cannot be
disambiguated because it has no arguments. For this reason we have
decided to provide only one version of this function that
returns a double-precision random value.
The Fortran standard dictates that compilers cannot disambiguate
overloaded routines based on the types of arguments to callback
routines that appear as arguments to those routines. This means that
the plstransform
and plslabelfunc
routines cannot be disambiguated
because they have no direct floating-point
arguments. For this reason we have decided that for the case where
plstransform
uses a
transform_coordinate
callback as its first
argument, that callback will be allowed to only have double-precision
arguments. And similarly for plslabelfunc
and the
label_func
callback.
The new Fortran binding defines a derived PLGraphicsIn
type as follows:
type :: PLGraphicsIn integer :: type ! of event (CURRENTLY UNUSED) integer :: state ! key or button mask integer :: keysym ! key selected integer :: button ! mouse button selected integer :: subwindow ! subwindow (alias subpage, alias subplot) number character(len=16) :: string ! Fortran character string integer :: pX, pY ! absolute device coordinates of pointer real(kind=private_double) :: dX, dY ! relative device coordinates of pointer real(kind=private_double) :: wX, wY ! world coordinates of pointer end type PLGraphicsIn
This is the type that should be used for the argument of the Fortran
plGetCursor
routine. We provide no alternative
plGetCursor
routine whose argument is similar to
PLGraphicsIn
but with single-precision
dX
, dY
, wX
,
and wY
components.
The new Fortran binding provides three auxiliary floating-point parameters,
PL_PI
, PL_TWOPI
and PL_NOTSET
which are
all defined in double precision. If the calling routine requires single precision instead it
should define a local parameter as in the following code fragment:
use plplot ... integer, parameter :: my_flt = kind(1.0) real(kind=my_flt), parameter :: my_NOTSET = PL_NOTSET
Users should be aware that the new Fortran binding for PLplot enforces the following interfacing rules:
The new Fortran binding interfaces Fortran functions/subroutines with
C routines if the C routines provide/do not provide a return value. For example, this rule means
that the C plparseopts
routine that returns an error code must be invoked at the
Fortran level similarly to the following:
integer :: plplarseopts_rc ... plparseopts_rc = plparseopts(...)
Of course, this rule means it is possible for Fortran routines invoking functions like plparseopts to respond properly to error conditions returned by the corresponding C routine.
Only the redacted form of Fortran API (with all redundant dimension arguments removed) is supported.
If the C API for the function being interfaced
includes a size value corresponding to identical sizes of dimension(s)
of multiple array arguments. then the sizes of the corresponding
dimensions of the Fortran arrays must also be identical. The complete
list of these adopted rules for consistently sized array arguments for
our Fortran binding are given at bindings/fortran/README_array_sizes
.
These rules are enforced in a user-friendly way by issuing a run-time
warning whenever these rules have been violated. For those cases which
generate such warnings because the calling
routine has specified static or allocatable arrays whose
relevant defined areas are smaller than their size, use the normal
Fortran rules for array slicing to specify completely defined arrays
with consistent sizes that comply with this interfacing rule.
Fortran logical arguments are used for all cases where
the corresponding C argument is PLBOOL
.
All floating-point arguments for a given function call
must have consistent kind values (either kind(1.0)
or kind(1.0.d0)
).
For more information on calling PLplot from Fortran, please consult
the example Fortran programs in examples/fortran
that are
distributed with PLplot. For more information on building your own
PLplot-related Fortran routines, please consult either the traditional
(Makefile + pkg-config) or CMake-based build systems that are created
as part of the install step for our Fortran (and other language)
examples.