Structure handles (C interopability)
Chapter 14 of Metcalf, Reid and Cohen describes the following code sample ("Figure 14.5"):
module
lib_code use iso_c_binding type :: pass integer :: n
real, allocatable :: a(:) : ! more stuff end type pass type(pass),
pointer :: struct contains subroutine initialize(n, data) bind(c) integer(c_int),
value :: n type(c_ptr), intent(out) :: data allocate(struct)
struct%n = n allocate (struct%a(n)) data=c_loc(structt) end
subtouine initialize subroutine add(data, ...) bind(c) type(c_ptr), intent(in) ::
data : call c_f_pointer(data, struct) :
end subroutine add end module lib_code
...and comments:
"When the C code calls initialize, it passes the size. The Fortran code allocates a
structure and inside it stores the size and allocates an array component; it then returns a pointer to the structure.
The C code later calls add and passes additional data together with the pointer that it received from initialize. The
Fortran procedure add uses c_f_pointer to establish a Fortran pointer for the relevant structure. Note that C may call
initialize several times if it wishes to work simultaneously with several problems; each will have a separate structure
of type pass and be accessible through its own 'handle' of type (c_ptr)."
Well and good; the explanation is
clear enough, and this is exactly what I want to do. Looking at the code, though, makes me feel uneasy, as it's not
obvious (a) how the compiler ensures that memory occupied by a previously-defined pointer is not overwritten, as no
variables here have the 'save' attribute, or (b) assuming that they are somehow automatically saved, how then does one
deallocate them later and so avoid a memory leak? Will a simple deallocate(struct) do the trick?
In C++ and
other languages, this situation is a little clearer because one creates a named object, based on a class, one can store
the object (or a pointer to it) in an array, and knows to call the object destructor when one is done. Here, things
don't look so safe: I picture a number of disembodied structs floating around in memory with nothing to keep track of
them except some handles that, one hopes, the C application is looking after properly.
Am I being too
pessimistic here, or are there some other elements I need to add to the template to ensure that nothing gets overwritten
or lost?
Thank you, Stephen.
| |
Re: Structure handles (C interopability)
According to Fortran 95 standard, allocatable arrays/objects are deallocated automatically at subroutine exit, when they
go out of scope. For the previous 5 years, it was necessary to deallocate explicitly. You are entitled to deallocate
explicitly if you wish. The Fortran system is entitled also to deallocate other local variables which don't have SAVE
declarations, as a C compiler would do with auto variables. If you had SAVE variables and called multiple
concurrent instances of the subroutine, you would have a storage clash, analogous to C static variables.
| |
Re: Structure handles (C interopability)
According to Fortran 95 standard, allocatable arrays/objects are
deallocated automatically at subroutine exit, when they go out of scope. For the previous 5 years, it was necessary to
deallocate explicitly. You are entitled to deallocate explicitly if you wish. The Fortran system is entitled also to
deallocate other local variables which don't have SAVE declarations, as a C compiler would do with auto variables. If you had SAVE variables and called multiple concurrent instances of the subroutine, you would have a storage clash,
analogous to C static variables.
So I guess that in this case, as the pointer that has been allocated resides in the module rather than in the
'initialize' subroutine, it stays allocated until explicitly deallocated just like an allocatable array would if held in
the module.
Presumably, the fact that 'struct' is a pointer, and there is no static variable of type(pass)
defined, is the mechanism that lets the compiler avoid overwriting the same 'struct' over and over again. I haven't used
pointers like this before; normally I create a bunch of allocatable arrays, and create a pointer to point at one of
them. If I then try to allocate an array that has already been allocated, I get an 'already allocated' error message.
The concept of being able to allocate multiple structures with the same name but different pointers is unfamiliar,
but I can see its very powerful.
Stephen.
| |
Re: Structure handles (C interopability)
So I guess that in this case, as the pointer that has been allocated
resides in the module rather than in the 'initialize' subroutine, it stays allocated until explicitly deallocated just
like an allocatable array would if held in the module.
I might be missing something here - but the "struct" fortran pointer (it is not an allocatable) being at module
scope is not relevant - you could (should? see below) have it as a local variable of each subroutine and you would get
the same behaviour. I think the only reason its been put at module scope here is that it saves a bit of typing.
I'd prefer "type(pass), pointer :: struct" local to each routine simply because that's the appropriate scope for
the lifetime of the information that's associated with that variable. I also strongly suspect a module scope variable
would have issues if this code was ever used in a multi-threaded situation, without additional code, but I don't play in
that park.
Each time you call allocate on a pointer, you get a new object, in new storage. If you want to
lose/leak memory, there's nothing stopping you. This is different to objects with the allocatable attribute. There's
no automatic clean-up/deallocate for pointers either - the programmer needs to look after that (though I think a vendor
could offer this as an extension?). The analogy with c++ is a pointer to an object allocated with new and then nuked
with delete.
So normally this sort of setup would have an uninitialise/delete/destruct/free/release function
that calls DEALLOCATE. For every time you "C" client code calls initialise, it must eventually call this cleanup
function or the allocated object will sit around until the program terminates.
The attached might be of
interest. It is not necessarily compilable/runable, but hopefully still useful. Interested in any alternative
approaches you might have.
Edit: to fix file attachment problem.
| |
Re: Structure handles (C interopability)
Thanks, IanH, that's really useful. I was interested by the MATLAB example (your attached .m
files - although I'm afraid that DLLReference.m wouldn't open) as well; I'm still working with an older version of
MATLAB which doesn't support classes yet, but you've provoked me to look up what's available in the current version and
it actually looks very capable.
Stephen.
| | |