Skip to content

Commit 00cd93b

Browse files
committed
add c++ Fortran polymorph example
1 parent 82389ce commit 00cd93b

File tree

6 files changed

+221
-1
lines changed

6 files changed

+221
-1
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
cmake_minimum_required(VERSION 3.14...3.21)
1+
cmake_minimum_required(VERSION 3.14...3.23)
22

33
project(Fortran_C_CXX_interface
44
LANGUAGES C CXX Fortran

src/cxx/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ target_include_directories(struct_cxx PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../c)
66
add_library(error_cxx OBJECT error_lib.cxx)
77

88
# -- C++ calling Fortran
9+
add_executable(cpp_poly_fcn poly_fcn_main.cpp)
10+
target_link_libraries(cpp_poly_fcn PRIVATE poly_fcn_fortran)
11+
add_test(NAME C++_Fortran_poly_fcn COMMAND cpp_poly_fcn)
12+
913
add_executable(cxx_fortran_bool bool_main.cxx)
1014
target_link_libraries(cxx_fortran_bool PRIVATE bool_fortran)
1115
add_test(NAME C++_Fortran_bool COMMAND cxx_fortran_bool)

src/cxx/poly_fcn_main.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// C++ calling polymorphic Fortran object
2+
// based on https://github.com/mattzett/fortran_tests
3+
#include <array>
4+
#include <iostream>
5+
6+
extern "C" void objconstruct_C(int*, void**, float**, const size_t*, const size_t*);
7+
extern "C" void objuse_C(int*, void*);
8+
9+
int main() {
10+
11+
// pointers for various fortran data
12+
void* objptr1;
13+
void* objptr2;
14+
int objtype;
15+
float* arrptr;
16+
17+
const size_t lx1=2;
18+
const size_t ly1=2;
19+
20+
// Object 1`
21+
objtype=1;
22+
std::array<float, lx1*ly1> x1;
23+
x1 = {1, 2, 3, 4};
24+
arrptr = &x1.front();
25+
objconstruct_C(&objtype, &objptr1, &arrptr, &lx1, &ly1);
26+
std::cout << "Use object 1" << std::endl;
27+
objuse_C(&objtype, &objptr1);
28+
29+
// Object 2
30+
objtype=2;
31+
const size_t lx2=2;
32+
const size_t ly2=3;
33+
std::array<float, lx2*ly2> x2;
34+
x2 = {6,5,4,3,2,1};
35+
arrptr = &x2.front();
36+
objconstruct_C(&objtype, &objptr2, &arrptr, &lx2, &ly2);
37+
std::cout << "Use object 2" << std::endl;
38+
objuse_C(&objtype,&objptr2);
39+
40+
// show that objects persist
41+
std::cout << "Use object 1 again" << std::endl;
42+
objtype=1;
43+
objuse_C(&objtype, &objptr1);
44+
45+
std::cout << "Use object 2 again" << std::endl;
46+
objtype=2;
47+
objuse_C(&objtype, &objptr2);
48+
49+
return EXIT_SUCCESS;
50+
}

src/fortran/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ add_library(error_fortran OBJECT error_lib.f90)
66

77
add_library(pointer_fortran OBJECT pointer_lib.f90)
88

9+
add_library(poly_fcn_fortran OBJECT poly_data_lib.f90 poly_fcn_lib.f90)
10+
911
add_library(struct_fortran OBJECT struct_lib.f90)
1012

1113
add_library(submodule_fortran OBJECT module.f90 submodule.f90)

src/fortran/poly_data_lib.f90

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
module datamod_poly
2+
!! from https://github.com/mattzett/fortran_tests
3+
4+
use, intrinsic :: iso_c_binding, only : c_float, c_int
5+
6+
implicit none (type, external)
7+
8+
type, abstract :: dataobj_poly
9+
!! derived type definition containing data and procedures
10+
real(c_float), dimension(:,:), pointer :: dataval
11+
integer(c_int) :: lx,ly
12+
13+
contains
14+
procedure :: set_data
15+
procedure :: print_data
16+
! final :: destructor
17+
end type dataobj_poly
18+
19+
type, extends(dataobj_poly) :: data1
20+
integer :: datstat
21+
contains
22+
procedure :: print_data=>print_data1
23+
end type data1
24+
25+
type, extends(dataobj_poly) :: data2
26+
real(c_float) :: datmag
27+
contains
28+
procedure :: print_data=>print_data2
29+
end type data2
30+
31+
contains
32+
33+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
34+
!! type-bound procedures
35+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
36+
subroutine set_data(self,array)
37+
class(dataobj_poly), intent(inout) :: self
38+
real(c_float), dimension(:,:), intent(in) :: array
39+
40+
self%lx=size(array,1)
41+
self%ly=size(array,2)
42+
allocate(self%dataval(self%lx,self%ly))
43+
self%dataval(:,:)=array(:,:)
44+
end subroutine set_data
45+
46+
47+
subroutine print_data(self)
48+
!! the default print is as a row vector
49+
class(dataobj_poly), intent(inout) :: self
50+
51+
print*, 'Stored data: '
52+
print*, self%dataval(:,:)
53+
end subroutine print_data
54+
55+
56+
subroutine print_data1(self)
57+
!! objects of type1 will print out as a matrix on the console
58+
class(data1), intent(inout) :: self
59+
integer :: ix
60+
61+
print*, 'Stored data: '
62+
do ix=1,self%lx
63+
print '(100F4.1)', self%dataval(ix,:)
64+
end do
65+
end subroutine print_data1
66+
67+
68+
subroutine print_data2(self)
69+
!! objects of type2 will print out as a column vector on the console
70+
class(data2), intent(inout) :: self
71+
integer :: ix,iy
72+
73+
print*, 'Stored data: '
74+
do ix=1,self%lx
75+
do iy=1,self%ly
76+
print '(f4.1)', self%dataval(ix,iy)
77+
end do
78+
end do
79+
end subroutine print_data2
80+
81+
end module datamod_poly

src/fortran/poly_fcn_lib.f90

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
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

Comments
 (0)