Conventions¶
Since the C++ and Fortran binding code are generated by SWIG-Fortran, most conventions are based on its defaults and built-in wrappers as described in the SWIG+Fortran manual page. This section attempts to describe all conventions used by Flibcpp that may be ambiguous to either a C++ or Fortran user. Since Flibcpp’s target audience is Fortran users, the library tries to follows existing Fortran conventions, and this document attempts to remain true to the official Fortran nomenclature as described in the ISO standards.
Basic types¶
The C++ standard library features many numeric classes and functions that take a template parameter corresponding to the type of a single element used by the class or algorithm. All such classes or functions that Flibcpp includes are templated on:
- 32-bit integers (
integer(4)
orinteger(C_INT32_T)
), with a suffix ofInt4
where applicable;- 64-bit integers (
integer(8)
orinteger(C_INT64_T)
), with a suffix ofInt8
where applicable; and- 64-bit reals (
real(8)
orreal(C_DOUBLE)
), with a suffix ofReal8
where applicable.
In general, most templated C++ functions use Fortran generic procedures to
allow a single procedure name to operate on multiple types, and only the
derived types (such as VectorInt4
) require the suffix.
Error handling¶
Some modules support error handling for checking input values. In all cases,
the error status and a message can be accessed and cleared through the main
flc
module:
use flc, only : ierr, get_serr
! <snip>
if (ierr /= 0) then
write(1,*) "Error", ierr, ":", get_serr()
! Reset the error flag to indicate that the error has been successfully
! handled.
ierr = 0
fi
Since errors do not immediately exit, it is up to the application code to check for and clear them. Failing to clear the error code may cause the application to exit on a subsequent Flibcpp procedure call.
Indexing¶
C and C++ use the convention that 0 corresponds to the first element in an array: specifically, it indicates an offset of zero from the array’s beginning. In Fortran, the convention is for the first element to have an index of 1, so Flibcpp has the same convention.
This convention makes native array integration straightforward. For example,
when the binary_search algorithm is used with any
value val
in the sorted array arr
, the following logical expression
will be true:
arr(binary_search(arr, val)) == val
An additional convention Flibcpp uses is for an invalid index to have a value of zero. Thus, the binary_search algorithm returns zero if asked to find an element that’s not present.
Derived type behavior¶
The derived types defined by Flibcpp are all “proxy” objects that operate on pointers to C++-owned objects. Some derived type values (i.e. class instances) will “own” the associated data, while some will merely reference data owned by other C++ objects.
Note
Memory management in SWIG-Fortran-generated code is unintuitive and could change. If you have feedback, please contact the author.
Construction¶
Although Fortran separates the processes of allocation and initialization, C++ combines them into the single act of construction. Thus the proxy objects in Flibcpp can either be in an unassigned, deallocated state or in an allocated and internally consistent state.
Each derived type, such as the String type, is constructed using
a module procedure interface with the same name as the type. This procedure,
defined in the same module as the derived type, is a function
that returns
a “constructed” type:
use flc_string, only : String
type(String) :: s
s = String()
Most classes have more than one constructor (i.e. procedure interface) that gives the object a more substantial initial state. For example, numeric vectors can be constructed directly from a Fortran array:
use flc_vector, only : Vector => VectorInt4
type(Vector) :: v, v2
v = Vector([1, 2, 3, 5, 8, 13])
Assignment¶
SWIG-Fortran, and consequently Flibcpp, defines assignment operators for its types that control memory ownership. Assignment for these types can be grouped into two categories: assignment directly from a Flibcpp function return value, and assignment from an existing Flibcpp derived type value.
Flibcpp (or any other SWIG-Fortran) wrapper code sets the correct ownership flag on a return value: non-owning for raw pointers and references; owning for return-by-value. When the left-hand side of an assignment is uninitialized, it captures the returned value and obtains the correct ownership flag. If the left-hand side is initialized, it is automatically destroyed first.
Assignment from within Fortran is like pointer assignment. The left-hand side becomes a non-owning reference to the right-hand side.
Destruction¶
Unlike native allocatable Fortran types, Flibcpp derived types are not automatically deallocated when ending a procedure. Therefore to avoid leaking memory, these derived type values must be explicitly cleaned up and
released. This is done by the type-bound subroutine named release
:
type(Vector), intent(inout) :: v
call v%release()
If the value v
above owns the associated memory (i.e. if it was constructed
in user code), then Flibcpp cleans up and deallocates the C++ instance, and
sets v
to an uninitialized state. If v
merely points to existing C++
data, i.e. if it was assigned to the return result of a C++ accessor, then
Flibcpp will simply set v
to an uninitialized state.