Reputation: 13
I'm a C programmer who has to update a huge Fortran 2003 program by adding a single call to a C function.
First, I need to write a minimal Fortran wrapper (in modern, free-form Fortran, no shouting) that will correctly call the C function with a string that contains a loop counter (and the date/time, if possible), from within a loop.
This should be "easy", but none of the searches I've done yielded enough snippets for me to create a working program.
I'm using recent 64-bit versions of gfortran and the Intel ifort compiler under 64-bit Linux, and the test code needs to compile using both compilers.
Here's the C definition, in the file send_to_port.c:
int send_to_port(int port, char *data, unsigned int length);
The last parameter was added to permit Fortran to have to not worry about the trailing null (I handle it in C: data[length] = '\0';). I understand the length parameter is added "automatically" by Fortran, so the Fortran call will have just two parameters, the integer port number and the string to send.
I hope to compile the code with the following gfortran line, plus the equivalent for ifort:
gfortran -ffree-form test.f -o test send_to_port.o
I'm looking for minimal code: I'm thinking it should be around 10-20 lines, but I don't know Fortran. Here's my current edit buffer for test.f (which doesn't compile):
use iso_c_binding
use iso_fortran_env, stdout => output_unit
implicit none
! Fortran interface to C routine:
! int send_to_port(int port, char *data, unsigned int length);
interface
integter(c_int) function send_to_port(port, data) bind(C)
integer(c_int), value :: port
character(kind=c_char) :: data(*)
end interface
integer(c_int) retval, cnt, port
character(1024) str
cnt = 0
port = 5900
do ! Infinite loop (^C to exit)
call fdate(date)
cnt = cnt + 1
write(str, "(A,A,I8)") date, ": Iteration = ", cnt
write(stdout, *) str ! Show what's about to be sent
retval = send_to_port(port, str) ! Send it
write(stdout, *) retval ! Show result
call sleep(1)
end do
end
Help?
Upvotes: 0
Views: 1827
Reputation: 1643
(Yes, going against the fortran-iso-c-binding question tag...)
If you cannot make the iso-c-binding work... As depending of the compiler version, I got some issues and i prefer going raw to the metal when mixing C and FORTRAN: avoiding interfaces simply create a "wrapper" for your C function.
Wrapper guidelines are roughly:
The function name must end with _ (on linux. on windows the function name must be ALL_CAPS with no trailing _)
If compiled in a C++ program, define as extern "C"
All arguments are pointers
In memory, multi-dimensional array indexes are reversed [i][j] is [j][i]
So the C code would be:
extern "C"
void send_to_port_fort_(int* port, char *data, int* length, int *result)
{
*result = send_to_port(*port,data,*length);
}
Then from fortran
call send_to_port_fort(port,data,size(data),retval)
Since there is no interface statement, there is no or argument size/type checking nor conversion
Upvotes: 0
Reputation: 21441
In addition to M. S. B.'s comment about argument count, your syntax for the interface block is out and there are some items that you need to introduce into the scope of the interface body.
Note that *
as the unit in a write statement is a synonym (and far more typical) for OUTPUT_UNIT (and the PRINT statement writes to that unit too - if you want minimal code). I also suggest a TRIM() around the str output item. Similarly, LEN_TRIM(str) is probably a reasonable length to pass as the third argument to send_to_port
.
Don't use .f as the extension for free form Fortran source - use .f90 instead.
Your code contains a reference to fdate
and sleep
, which are not standard intrinsics. Be mindful that the behaviour of these may be different between your compilers (I think you are ok - but you should check. The first of these can probably be replaced by the DATE_AND_TIME intrinsic along with some appropriate text formatting code for a more portable solution.
use, intrinsic :: iso_c_binding, only: c_int, c_char
implicit none
interface
function send_to_port(port, data, length) bind(C)
use, intrinsic :: iso_c_binding, only: c_int, c_char
implicit none
integer(c_int), value :: port
character(kind=c_char) :: data(*)
integer(c_int), value :: length
integer(c_int) :: send_to_port
end function send_to_port
end interface
integer(c_int) :: retval, port
integer :: cnt
character(len=1024,kind=c_char) :: str
character(30) :: date
cnt = 0
port = 5900_c_int
do ! Infinite loop (^C to exit)
call fdate(date)
cnt = cnt + 1
write(str, "(A,A,I8)") trim(date), ": Iteration = ", cnt
print *, trim(str) ! Show what's about to be sent
retval = send_to_port(port, str, len_trim(str)) ! Send it
print *, retval ! Show result
call sleep(1)
end do
end
Upvotes: 1