|
| 1 | +module c_interface_poly |
| 2 | +!! top-level procedures that C will call to manipulate a Fortran object |
| 3 | +!! from https://github.com/mattzett/fortran_tests |
| 4 | + |
| 5 | + use datamod_poly, only: dataobj_poly,data1,data2 |
| 6 | + use, intrinsic :: iso_c_binding, only: c_loc,c_ptr,c_f_pointer,c_int, c_float, c_size_t |
| 7 | + implicit none (type, external) |
| 8 | + |
| 9 | +contains |
| 10 | + |
| 11 | +subroutine objconstruct_C(objtype,cptr_f90obj,cptr_indata,lx,ly) bind(C, name='objconstruct_C') |
| 12 | +!! return a c pointer to a fortran polymorphic object that is created by this routine; in this case we |
| 13 | +!! are passing the object type to be created from the C main program |
| 14 | + integer(c_int), intent(in) :: objtype |
| 15 | + type(c_ptr), intent(inout) :: cptr_f90obj |
| 16 | + type(c_ptr), intent(in) :: cptr_indata |
| 17 | + integer(c_size_t), intent(in) :: lx,ly |
| 18 | + class(dataobj_poly), pointer :: obj |
| 19 | + type(data1), pointer :: tmpobj1 !< declared as pointers so they don't auto-allocate when we return |
| 20 | + type(data2), pointer :: tmpobj2 !< ditto |
| 21 | + real(c_float), dimension(:,:), pointer :: fortdata |
| 22 | + |
| 23 | + !> nullify for sake of clarity and good practice |
| 24 | + nullify(tmpobj1, tmpobj2) |
| 25 | + |
| 26 | + !> allocate derived type, note that c_loc only works on a static type (i.e. not polymorphic class), hence the tmpobj's |
| 27 | + select case (objtype) |
| 28 | + case (1) |
| 29 | + allocate(data1::obj) |
| 30 | + allocate(tmpobj1) |
| 31 | + cptr_f90obj=c_loc(tmpobj1) |
| 32 | + obj=>tmpobj1 |
| 33 | + case (2) |
| 34 | + allocate(data2::obj) |
| 35 | + allocate(tmpobj2) |
| 36 | + cptr_f90obj=c_loc(tmpobj2) |
| 37 | + obj=>tmpobj2 |
| 38 | + case default |
| 39 | + error stop 'unable to identify object type during construction' |
| 40 | + end select |
| 41 | + |
| 42 | + !> initialize some test data, and call methods to print |
| 43 | + print*, 'Initializing test data...' |
| 44 | + call c_f_pointer(cptr_indata,fortdata,shape=[lx,ly]) |
| 45 | + call obj%set_data(fortdata) |
| 46 | + |
| 47 | + !! note lack of deallocate and nullify here; we need memory allocated to persist past the return. |
| 48 | +end subroutine objconstruct_C |
| 49 | + |
| 50 | + |
| 51 | +!> Use some of the polymorphic object methods and data |
| 52 | +subroutine objuse_C(objtype,objC) bind(C, name='objuse_C') |
| 53 | + type(c_ptr), intent(in) :: objC |
| 54 | + integer(c_int), intent(in) :: objtype |
| 55 | + class(dataobj_poly),pointer :: obj |
| 56 | + |
| 57 | + obj=>set_pointer_dyntype(objtype,objC) |
| 58 | + call obj%print_data() |
| 59 | +end subroutine objuse_C |
| 60 | + |
| 61 | + |
| 62 | +!> set fortran object pointer dynamic type to what is indicated in objtype. Convert C pointer using |
| 63 | +! declared static types (c_f_pointer will not work on a polymorphic object). |
| 64 | +function set_pointer_dyntype(objtype, objC) result(obj) |
| 65 | + type(c_ptr), intent(in) :: objC |
| 66 | + integer(c_int), intent(in) :: objtype |
| 67 | + class(dataobj_poly), pointer :: obj |
| 68 | + type(data1), pointer :: obj1 |
| 69 | + type(data2), pointer :: obj2 |
| 70 | + |
| 71 | + select case (objtype) |
| 72 | + case (1) |
| 73 | + call c_f_pointer(objC,obj1) |
| 74 | + obj=>obj1 |
| 75 | + case (2) |
| 76 | + call c_f_pointer(objC,obj2) |
| 77 | + obj=>obj2 |
| 78 | + case default |
| 79 | + error stop 'unable to identify object type during conversion from C to fortran class pointer' |
| 80 | + end select |
| 81 | +end function set_pointer_dyntype |
| 82 | + |
| 83 | +end module c_interface_poly |
0 commit comments