I am looking into the HYPRE library for sparse linear solvers. It is a C library with Fortran calling conventions documented here. Notably, they recommend the use of integer*8
to represent a pointer to a C struct. I would guess that one of the integer kinds defined in iso_c_binding
should serve this purpose better. Reading the GCC documentation, three standouts were size_t
, ptrdiff_t
, and intptr_t
. Is there a clear best choice? My other hunch is that this integer stuff is nonsense and I should just use type(c_ptr)
. Any guidance from seasoned C-interoperators is welcome.
type(c_ptr)
is the way to interact with C pointers (for functions, there is also c_funptr
). It is just an integer to store an address, 8 bytes on nowadays PCs.
The null values are c_null_ptr
and c_null_funptr
.
Why do they do that?
Type(c_ptr) is the way to go. A pointer is 8 bytes, so I suppose it probably doesn’t really matter what fortran type is used, as long as it is 8 bytes.
I have used 8 byte integers to represent pointers from python before. It has worked well.
It is always better to make no supposition when possible, and thus use c_ptr
.
Lol QED.
Normally type(c_ptr)
would be the way to go, unless you have to represent your pointer as integer (for whatever reason, usually (un)safe pointer arithmetic). There are dedicated integer kinds for this purpose, both in C and Fortran/C interop (iso_c_binding
module).
See Fixed width integer types (since C99) - cppreference.com
-
intptr_t: (optional)
signed integer type capable of holding a pointer to void (typedef) -
uintptr_t: (optional)
unsigned integer type capable of holding a pointer to void (typedef)
As well as ptrdiff_t - cppreference.com for holding differences between pointer addresses
-
ptrdiff_t:
signed integer type returned when subtracting two pointers (typedef)
You should find kind parameters for c_intptr_t, c_uintptr_t, and c_ptrdiff_t type in iso_c_binding
, just use c_loc as usual, transfer the pointer to the respective integer value and do (un)responsible pointer arithmetic as needed.
I just recently realized that a null pointer in C is very unlikely to be a literal zero, and that the actual byte representation of the null pointer can be different between C compilers, like 0x55555555
as shown here:
Checking for the association status should therefore best be done using type(c_ptr)
and c_associated
rather than some heuristic based on its integer representation.
Interesting (it confirms it’s better to use those iso_c_binding parameters instead of making assumptions).
It’s a strange value because 0x55555555 is only ~1.4E9 and the Intel 80386 in the example could address up to 4 GB…
I think it is the particular choice of / available with the TenDRA C compiler, a more common one would be 0xFFFFFFFF
, which kind of makes sense because there cannot be a value at this address.
Thanks all for the replies here. In this application, these pointers are just handles to the structs used internally in the library, so there is no need for pointer arithmetic. It seems like type(c_ptr)
(along with c_associated
if necessary) should in principle be the safest way to work with HYPRE from Fortran.
The library predates iso_c_binding
by several years. My guess is that integer*8
is just a holdover from this time, and there hasn’t been a perceived need to update the documentation and example programs. Once I get a little experience with the library to confirm that type(c_ptr)
works as expected, I’ll open an issue with the project to see if they’re interested in updating.
It seems like
type(c_ptr)
(along withc_associated
if necessary) should in principle be the safest way to work with HYPRE from Fortran.
Even safer would be to have a dedicated wrapper interface with a Fortran derived type hiding the raw C pointers (beware of shallow copies…).
My guess is that
integer*8
is just a holdover from this time, and there hasn’t been a perceived need to update the documentation and example programs.
It could also be a project requirement from the time Hypre was developed that the code must be compatible with certain (out-of-date) compilers. The tests of the “Fortran interface” are all written in Fortran 77. From a cursory look through the git commit history, the Fortran files haven’t been touched in a decade, other than to update copyright and license statements.
Indeed, this morning I wasted a few hours on silly name-mangling issues… Thankfully, the API is pretty uniform and doesn’t do anything “tricky” as far as I can tell. If I decide to become a real user, I think it will not be too hard to automatically generate bind(C)
interfaces (I wonder if the tool @vmagnin uses for GTK is general enough to use here…).
I think it will not be too hard to automatically generate
bind(C)
interfaces (I wonder if the tool @vmagnin uses for GTK is general enough to use here…).
It could probably be adapted, but it was not conceived as a general tool. The regular expressions were polished only on the GTK / GLib coding conventions. Note also that it parses only the C functions, not the C structs.
There is general tools like Swig. See Generating C Interfaces in Fortran Wiki
It seems like
type(c_ptr)
(along withc_associated
if necessary) should in principle be the safest way to work with HYPRE from Fortran.
Yes, this works fine. The Truchas code uses such a (partial) interface to Hypre. You can see it here in case you’re interested.
It seems like
type(c_ptr)
(along withc_associated
if necessary) should in principle be the safest way to work with HYPRE from Fortran.
This is also how I use hypre in my fluid dynamics codes (example).