Forum Jump

Select Group :
Select Forum :
Sorted By :
Sort Order :
From The :
 
Thread Tools  Search this thread 
eos pengwern
Total Points:
1,895
Status Points:
1,395
Brown Belt
July 7, 2009 3:52 PM PDT
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.
tim18
Total Points:
66,397
Status Points:
66,397
Black Belt
July 7, 2009 5:46 PM PDT
Rate
 
#1
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.



eos pengwern
Total Points:
1,895
Status Points:
1,395
Brown Belt
July 8, 2009 2:26 AM PDT
Rate
 
#2 Reply to #1
Quoting - tim18
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.



IanH
Total Points:
2,120
Status Points:
1,620
Brown Belt
July 8, 2009 6:06 PM PDT
Rate
 
|Best Answer
#3 Reply to #2
Quoting - eos pengwern

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.


 Attachments 
eos pengwern
Total Points:
1,895
Status Points:
1,395
Brown Belt
July 9, 2009 3:23 AM PDT
Rate
 
#4 Reply to #3
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.





Intel Software Network Forums Statistics

8285 users have contributed to 31229 threads and 99107 posts to date.
In the past 24 hours, we have 7 new thread(s) 35 new posts(s), and 47 new user(s).

In the past 3 days, the most popular thread for everyone has been comparison cilk++, openmp, pthreads first results The most posts were made to comparison cilk++, openmp, pthreads first results The post with the most views is Very amusing...  Escalated as

Please welcome our newest member tvinni