Reputation: 13
This question is reproducible because if the below program is compiled the below error message is output. There's no typographical errors.
My real life program uses integer*8 variables and does not compile with this specific iso_c_binding patch, see below. My program works fine when the iso_c_binding package is not used.
With the iso_c_binding package in my program, I get the following error message:
Error: Type mismatch in argument 'test_variable' at (1); passed INTEGER(8) to TYPE(c_ptr)
I’ve created the below testcase from my program. My program cannot be published. The testcase demonstrates the error that I’m getting.
Question 1: How do you fix this? The following is what I would like if at all possible! The two structs in the test.c file actually exist in my program. The game function formal arguments also actually exist in my real world program. My program passes integer*8 variables to the game routine. Everything else besides these three things should be able to be modified.
Other Questions: Does the C code have to change? Not counting the iso_c_binding interface code, does the Fortran code have to change? What has to change?
Can iso_c_binding do this?
Thank you,
File the_game.f:
module the_game use, intrinsic :: iso_c_binding, only: c_int, c_float, c_ptr type, bind(c) :: myfloat real(c_float) :: float_tmp end type type, bind(c) :: myint integer(c_int) :: int_tmp end type interface function game(myint1, myfloat2) bind(c, name="game_") import :: c_int, c_ptr implicit none type(c_ptr) :: myint1 type(c_ptr) :: myfloat2 integer(c_int) :: game end function end interface end module the_game
File main.f:
program main use the_game integer*8 test_variable integer*8 test_variable2 integer ans ans=game (test_variable,test_variable2) end program main
File test.c:
#include <stdio.h>
struct myfloat
{
float float_tmp;
};
struct myint
{
int int_tmp;
};
int game_(myint1,myfloat2)
struct myint **myint1;
struct myfloat **myfloat2;
{/* The following shows how the code will eventually be used */
/* after malloc etc. */
/* printf ("(*myint1)->int_tmp = %d\n",(*myint1)->int_tmp); */
/* printf ("(*myfloat2)->float_tmp = %f\n",(*myfloat2)->float_tmp); */
/* (*myint1)->int_tmp = 1; */
/* (*myfloat2)->float_tmp = 100.1; */
return(0);
}
Compilation:
gcc -g -c test.c -o test.o
gfortran -g -c the_game.f -o the_game.o
gfortran -g -c main.f -o main.o
gfortran -g main.o test.o -o main.exe -lgfortran
Output:
main.f:9.19:
ans=game (test_variable,test_variable2)
1
Error: Type mismatch in argument 'myint1' at (1); passed INTEGER(8) to TYPE(c_ptr)
Upvotes: -6
Views: 808
Reputation: 180058
Error: Type mismatch in argument 'test_variable' at (1); passed INTEGER(8) to TYPE(c_ptr)
Yes, INTEGER(8)
is a different type from TYPE(c_ptr)
. Associating an argument of the former type with a subprogram parameter of the latter type is not allowed.
Question 1: How do you fix this? The following is what I would like if at all possible! The two structs in the test.c file actually exist in my program.
There aren't any struct objects in test.c. There are two structure type declarations, but no objects of those types in evidence. This is consequential, because it is unclear where you expect any such objects actually to be defined, or by whom allocated.
The game function formal arguments also actually exist in my real world program.
Of course the formal arguments to the game_()
function exist. Their declarations in the definition of that function are that is required for that. But perhaps you're talking about the objects to which they point?
My program passes integer*8 variables to the game routine.
Probably not without your compiler at least complaining. The parameters are pointers, not integers. I would expect a C compiler to emit an analogous complaint when processing an attempt to call the game_()
function in C with arguments that were integers of any size. C pointers are not integers, neither in C nor in Fortran.
Everything else besides these three things should be able to be modified.
There's been a lot of noise and lack of clarity here, but I think what you actually want is for
game_()
function,You have put a lot of attention on integer*8
and integer(8)
(which are not necessarily the same thing), when these just aren't the right tools for the job. I suspect that you have also engaged one more level of indirection on the C side than is needed or helpful.
Since you're using the GNU compiler suite, you should consider reading its documentation of the Fortran / C interoperability features, which is not half bad.
You have at least these considerations to deal with:
I think you're actually making it harder than it needs to be on both sides.
You do seem to have the interoperable type declarations sorted out.
The C function declaration appears to be another story. I don't see any plausible reason why you would want to receive double pointers to the structures, and the result is probably not what you think. Whether you expect the Fortran code to associate a single structure with each parameter or an array of them, you probably want the C side to declare the parameters as single pointers. Note also that although you can use a C function name with a trailing underscore, you are not required to do. Just specify whatever name you choose in the Fortran interface to it.
Example:
test.c
#include <stdio.h>
struct myfloat {
float float_tmp;
};
struct myint {
int int_tmp;
};
int game_(struct myint *myint1, struct myfloat *myfloat2) {
puts("C says:");
printf ("myint1->int_tmp = %d\n", myint1->int_tmp);
printf ("myfloat2->float_tmp = %f\n", myfloat2->float_tmp);
myint1->int_tmp = 1;
myfloat2->float_tmp = 2.0;
return 0;
}
Note in particular that only one level of indirection is used for the arguments.
The Fortran interface is also pretty straightforward:
the_game.f90
module the_game
use, intrinsic :: iso_c_binding, only: c_int, c_float, c_ptr
type, bind(c) :: myfloat
real(c_float) :: float_tmp
end type
type, bind(c) :: myint
integer(c_int) :: int_tmp
end type
interface
function game(myint1, myfloat2) bind(c, name="game_")
import :: c_int, myfloat, myint
implicit none
type(myint) :: myint1 ! not a pointer!
type(myfloat) :: myfloat2 ! not a pointer!
integer(c_int) :: game
end function
end interface
end module the_game
Note that the function parameters are not defined as pointers. Fortran will pass pointers by default because its default subprogram invocation semantics use (approximately) pass by reference. It's mainly when you want to pass by value (as you often might do, because those are C's semantics) that you need to need to do anything special in the declared interface.
Having that in hand, it's not hard to call it from Fortran. Example:
main.f90
program main
use the_game
type(myint) :: test_variable
type(myfloat) :: test_variable2
integer ans
test_variable%int_tmp = 7
test_variable2%float_tmp = 3.14
ans = game(test_variable,test_variable2)
write(*, *)
write(*, *) 'Fortran says:'
write(*, *) 'Result = ', ans, '; my int = ', test_variable%int_tmp, &
'; my_float = ', test_variable2%float_tmp
end program main
Note that there is neither any c_ptr
nor any corresponding integer
in the Fortran code that calls the C function. The Fortran side just works in terms of the (interoperable) Fortran derived types. And here's the output, showing that both sides are accessing the same objects:
$ ./main.exe C says: myint1->int_tmp = 7 myfloat2->float_tmp = 3.140000 Fortran says: Result = 0 ; my int = 1 ; my_float = 2.00000000
ALTERNATIVELY
Later comments suggest that perhaps you intend to use the parameters to game_()
to pass C pointers back to the Fortran caller, perhaps pointers to dynamically allocated space. That, too, can be accommodated, and in that case you do have to deal with c_ptr
, because that's what C will emit. But you do have to take care with level of indirection -- Fortran will pass an object of type c_ptr
to C by reference, which C will receive as a double pointer.
Once it gets the C pointer values back, Fortran will use the c_f_pointer()
subroutine to associate appropriately typed Fortran pointers with the allocated space, through which it can access the contents. Example:
alttest.c
#include <stdio.h>
#include <stdlib.h>
struct myfloat {
float float_tmp;
};
struct myint {
int int_tmp;
};
int game_(struct myint **myint1, struct myfloat **myfloat2) {
*myint1 = malloc(sizeof(**myint1));
*myfloat2 = malloc(sizeof(**myfloat2));
if (!*myint1 || !*myfloat2) {
return -1;
}
(*myint1)->int_tmp = 1;
(*myfloat2)->float_tmp = 2.0;
return 0;
}
the_game_alt.f90
module the_game_alt
use, intrinsic :: iso_c_binding, only: c_int, c_float, c_ptr
type, bind(c) :: myfloat
real(c_float) :: float_tmp
end type
type, bind(c) :: myint
integer(c_int) :: int_tmp
end type
interface
function game(myint1_p, myfloat2_p) bind(c, name="game_")
import :: c_int, c_ptr
implicit none
type(c_ptr), intent(out) :: myint1_p
nd in this case you do have to deal with `c_ptr` type(c_ptr), intent(out) :: myfloat2_p
integer(c_int) :: game
end function
end interface
end module the_game_alt
altmain.f90
program main
use the_game_alt
use, intrinsic :: iso_c_binding
type(myint), pointer :: intvar
type(myfloat), pointer :: floatvar
type(c_ptr) :: myint_ptr, myfloat_ptr
integer ans
ans = game(myint_ptr, myfloat_ptr)
if (ans .ne. 0) then
write(*, *) 'error: C returned ', ans
else
call c_f_pointer(myint_ptr, intvar)
call c_f_pointer(myfloat_ptr, floatvar)
write(*, *)
write(*, *) 'Fortran says:'
write(*, *) 'Result = ', ans, '; my int = ', intvar%int_tmp, &
'; my_float = ', floatvar%float_tmp
end if
end program main
Program output:
$ ./altmain.exe Fortran says: Result = 0 ; my int = 1 ; my_float = 2.00000000
NOTE WELL:
It remains unclear what you actually want to do. It may be that something different from either of the above approaches is in fact required.
Upvotes: 1