pointers - Upgrading Fortran/C program from 32-bit to multi-architecture -
i'm looking @ old fortran program c calls works 32-bit systems, raises warnings , concerns 64-bit compilers. program stores address of c pointer dynamically allocated memory int
, shared on fortran-side integer
. concern on 64-bit integer systems, cast c pointer larger can stored int
/integer
. i've simplified existing program example 2 files.
fortran: this.f
program integer,pointer :: iptr allocate(iptr) call that_allocate(iptr) write(*,'(a, z12)') 'fortran: iptr address', iptr call that_assemble(iptr) call that_free(iptr) end program
c: that.c
#include <stdlib.h> #include <stdio.h> typedef struct data { int a; float b; } data; void that_allocate_(int* iptr) { data *pdata = calloc(1, sizeof(data)); *iptr = (int)pdata; printf("c: allocated address %p (or %d)\n", pdata, pdata); return; } void that_assemble_(int* iptr) { data *pdata = (data *) *iptr; pdata->a = 42; pdata->b = 3.1415926; return; } void that_free_(int* iptr) { data *pdata = (data *) *iptr; printf("c: freeing data %d , %g @ %p\n", pdata->a, pdata->b, pdata); free(pdata); return; }
compile
the program can compiled gnu compilers -m32
32-bit (no issue here), , -m64
64-bit. compiling c code raises warnings:
$ gcc -m64 -c that.c that.c: in function ‘that_allocate_’: that.c:12: warning: cast pointer integer of different size that.c: in function ‘that_assemble_’: that.c:19: warning: cast pointer integer of different size that.c: in function ‘that_free_’: that.c:27: warning: cast pointer integer of different size
while remaining compilation , linking fine, , program works:
$ gfortran -m64 -o prog this.f that.o $ ./prog c: allocated address 0x1130b40 (or 18025280) fortran: iptr address 1130b40 c: freeing data 42 , 3.14159 @ 0x1130b40
questions
while see calloc
returned address can fit within data limits of 4-byte integer, there risk of calloc
returning address larger integer? casting (intptr_t)
silence the compile warnings, suspect truncate higher bits , "segmentation fault" if pointer attempted cast truncated address. correct?
what should do? fix need go on fortran code?
yes, there potential bitness problem. if want code robust in face of compiler , platform changes, there number of things should do, of rely on c interoperability features of fortran 2003. these language features supported recent gfortran , actively maintained fortran compilers.
it not clear example whether fortran code needs know value of pointer data
struct integer (in example print value, suspect debugging). if fortran code needs regard pointer opaque handle, c_ptr type in iso_c_binding intrinsic module appropriate equivalent c pointer. if reason fortran code need know value of pointer integer, integer of kind c_intptr_t (again iso_c_binding intrinsic module) appropriate. going further still, if want fortran code able play actual structure itself, can define bind(c) derived type , use in various ways.
further, c code assumes fortran compiler uses calling convention, including way procedure names mangled form linker name. can use bind(c,name='xxx') attribute in interface block on fortran side specify fortran compiler use calling convention compatible companion c compiler , specify c name of procedure.
note pointer declaration on integer , subsequent allocation not relevant, given rest of example.
all (in free form, has been while since fixed form appropriate):
program use, intrinsic :: iso_c_binding, only: c_ptr, c_intptr_t implicit none ! interfaces required due bind(c). allows fortran ! compiler better error checking. note default ! binding label lowercase variant of fortran name, ! specify explicitly here anyway clarity. interface subroutine that_allocate(the_c_ptr) & bind(c,name='that_allocate') use, intrinsic :: iso_c_binding, only: c_ptr implicit none ! note passing pointer reference. type(c_ptr), intent(out) :: the_c_ptr end subroutine that_allocate subroutine that_assemble(the_c_ptr) & bind(c,name='that_assemble') use, intrinsic :: iso_c_binding, only: c_ptr implicit none ! note passing pointer value. type(c_ptr), intent(in), value :: the_c_ptr end subroutine that_assemble subroutine that_free(the_c_ptr) & bind(c,name='that_free') use, intrinsic :: iso_c_binding, only: c_ptr implicit none ! note passing pointer value. type(c_ptr), intent(in), value :: the_c_ptr end subroutine that_free end interface type(c_ptr) :: the_c_ptr call that_allocate(the_c_ptr) ! use transfer convert c address integer value. print "('fortran: ptr address',z12)", & transfer(the_c_ptr, 0_c_intptr_t) call that_assemble(the_c_ptr) call that_free(the_c_ptr) end program
and simplifying on c side:
#include <stdlib.h> #include <stdio.h> typedef struct data { int a; float b; } data; void that_allocate(data** pdata) { *pdata = (data*) calloc(1, sizeof(data)); printf("c: allocated address %p\n", *pdata); return; } void that_assemble(data* pdata) { pdata->a = 42; pdata->b = 3.1415926; return; } void that_free(data* pdata) { printf("c: freeing data %d , %g @ %p\n", pdata->a, pdata->b, pdata); free(pdata); return; }
Comments
Post a Comment