Skip to content

Commit cdfe886

Browse files
authored
Merge pull request #141 from swig-fortran/abstract-interfaces
Automated generation of *abstract* bind(C) interface
2 parents d88c95a + f613c83 commit cdfe886

File tree

15 files changed

+570
-303
lines changed

15 files changed

+570
-303
lines changed

Doc/Manual/src/Fortran.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1857,6 +1857,7 @@ Fortran standard's specification and naming of the components of a module.
18571857
<tr><td><code>fbegin </code></td><td>Code before the `module` statement </td></tr>
18581858
<tr><td><code>fuse </code></td><td>"use" statements </td></tr>
18591859
<tr><td><code>fdecl </code></td><td>Module declarations </td></tr>
1860+
<tr><td><code>fabstract</code></td><td>Abstract interfaces for callbacks</td></tr>
18601861
<tr><td><code>finterfaces</code></td><td>Procedure interfaces for C code </td></tr>
18611862
<tr><td><code>fsubprograms</code></td><td>Fortran module subprograms </td></tr>
18621863
</tbody>

Examples/fortran/funcptr/example.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
/* File : example.c */
22

3-
int do_op(int a, int b, int (*op)(int,int)) {
4-
return (*op)(a,b);
3+
int do_op(int a, int b, int (*op)(int, int)) {
4+
return (*op)(a, b);
55
}
66

77
int add(int a, int b) {
8-
return a+b;
8+
return a + b;
99
}
1010

1111
int sub(int a, int b) {
12-
return a-b;
12+
return a - b;
1313
}
1414

1515
int mul(int a, int b) {
16-
return a*b;
16+
return a * b;
1717
}
1818

19-
int (*funcvar)(int,int) = add;
19+
int (*funcvar)(int, int) = add;

Examples/fortran/funcptr/example.h

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
/* file: example.h */
22

3-
extern int do_op(int,int, int (*op)(int,int));
4-
extern int add(int,int);
5-
extern int sub(int,int);
6-
extern int mul(int,int);
3+
typedef int (*binary_func)(int, int);
74

8-
extern int (*funcvar)(int,int);
5+
extern int do_op(int, int, binary_func);
6+
extern int add(int, int);
7+
extern int sub(int, int);
8+
extern int mul(int, int);
9+
10+
extern int (*funcvar)(int, int);
911

Examples/fortran/funcptr/example.i

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,14 @@
44
#include "example.h"
55
%}
66

7+
%fortrancallback("%s") binary_op;
8+
int binary_op(int a, int b);
9+
10+
/* Function pointer: f(int, int) -> int */
11+
typedef int (*binary_func)(int, int);
12+
713
/* Wrap a function taking a pointer to a function */
8-
extern int do_op(int a, int b, int (*op)(int, int));
14+
extern int do_op(int a, int b, binary_func op);
915

1016
/* Now install a bunch of "ops" as constants */
1117
%constant int (*ADD)(int,int) = add;

Examples/fortran/funcptr/runme.f90

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,6 @@ module fortran_ops
44
implicit none
55
public
66

7-
abstract interface
8-
! Interface corresponding to the C typedef "BinaryOp"
9-
function binary_op(aa, bb) bind(C) result(cc)
10-
use, intrinsic :: ISO_C_BINDING
11-
integer(C_INT), intent(in), value :: aa
12-
integer(C_INT), intent(in), value :: bb
13-
integer(C_INT) :: cc
14-
end function
15-
end interface
16-
177
contains
188
! Fortran function that we can export so that
199
! it's available to C code
@@ -39,19 +29,16 @@ program fortran_funptr_runme
3929
integer, parameter :: STDOUT = OUTPUT_UNIT
4030
integer(C_INT) :: a = 4
4131
integer(C_INT) :: b = 3
42-
type(C_FUNPTR) :: temp_funptr
43-
procedure(binary_op), pointer :: my_ffunc => null()
32+
procedure(binary_op), pointer :: fptr => null()
4433

45-
write(STDOUT,*) "SWIG-wrapped C function pointer:", do_op(a,b,add)
46-
write(STDOUT,*) "SWIG-wrapped C function pointer:", do_op(a,b,sub)
47-
write(STDOUT,*) "SWIG-wrapped C function pointer:", do_op(a,b,mul)
34+
call c_f_procpointer(add, fptr)
35+
write(STDOUT,*) "SWIG-wrapped C function pointer:", do_op(a, b, fptr)
36+
call c_f_procpointer(sub, fptr)
37+
write(STDOUT,*) "SWIG-wrapped C function pointer:", do_op(a, b, fptr)
38+
call c_f_procpointer(mul, fptr)
39+
write(STDOUT,*) "SWIG-wrapped C function pointer:", do_op(a, b, fptr)
4840

4941
! Convert Fortran function to C function pointer
50-
temp_funptr = c_funloc(fortran_mul)
51-
write(STDOUT,*) "C call to Fortran function:", do_op(a,b,temp_funptr)
52-
53-
! Convert C function pointer to a Fortran function pointer
54-
call c_f_procpointer(add, my_ffunc)
55-
write(STDOUT,*) "Fortran direct call to C function:", my_ffunc(a,b)
42+
write(STDOUT,*) "C call to Fortran function:", do_op(a, b, fortran_mul)
5643

5744
end program

Examples/test-suite/fortran/Makefile.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ top_builddir = @top_builddir@
2222

2323
CPP_TEST_CASES = \
2424
fortran_bindc \
25+
fortran_callback \
2526
fortran_naming \
2627
fortran_onlywrapped \
2728
fortran_openacc \
@@ -31,6 +32,7 @@ CPP_TEST_CASES = \
3132
C_TEST_CASES = \
3233
fortran_array_typemap \
3334
fortran_bindc_c \
35+
fortran_callback_c \
3436
fortran_global_const \
3537

3638
FAILING_CPP_TESTS += \
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
! File : fortran_callback_runme.F90
2+
3+
#include "fassert.h"
4+
5+
module fortran_callback_mod
6+
use, intrinsic :: ISO_C_BINDING
7+
use ISO_FORTRAN_ENV
8+
implicit none
9+
integer, parameter :: STDOUT = OUTPUT_UNIT
10+
integer(C_INT), save, public :: module_int
11+
contains
12+
13+
function myexp(left, right) bind(C) &
14+
result(fresult)
15+
use, intrinsic :: ISO_C_BINDING
16+
integer(C_INT), intent(in), value :: left
17+
integer(C_INT), intent(in), value :: right
18+
integer(C_INT) :: fresult
19+
20+
fresult = left ** right
21+
end function
22+
23+
function mywrite() bind(C) &
24+
result(fresult)
25+
use, intrinsic :: ISO_C_BINDING
26+
integer(C_INT) :: fresult
27+
! write(STDOUT,*) "Hi there"
28+
fresult = 256_c_int
29+
end function
30+
31+
subroutine store_an_int(i) bind(C)
32+
use, intrinsic :: ISO_C_BINDING
33+
integer(C_INT), value, intent(in) :: i
34+
! write(STDOUT,*) "Got an integer: ", i
35+
module_int = i
36+
end subroutine
37+
38+
end module
39+
40+
program fortran_callback_runme
41+
call test_callback
42+
contains
43+
44+
subroutine test_callback
45+
use fortran_callback
46+
use fortran_callback_mod
47+
integer(C_INT) :: i
48+
procedure(call_binary_cb), pointer :: unused => NULL()
49+
procedure(binary_op), pointer :: bin_op_ptr => NULL()
50+
51+
! Use callbacks with Fortran module pointers
52+
bin_op_ptr => myexp
53+
i = call_binary(myexp, 2, 3)
54+
ASSERT(i == 8)
55+
i = call_things(mywrite)
56+
ASSERT(i == 256)
57+
call also_call_things(store_an_int, 999)
58+
ASSERT(module_int == 999)
59+
60+
! Get a C callback
61+
bin_op_ptr => get_a_callback("mul")
62+
ASSERT(associated(bin_op_ptr))
63+
ASSERT(bin_op_ptr(2, 5) == 10)
64+
65+
bin_op_ptr => get_a_callback("add")
66+
ASSERT(associated(bin_op_ptr))
67+
ASSERT(bin_op_ptr(2, 5) == 7)
68+
69+
bin_op_ptr => get_a_callback("nopenope")
70+
ASSERT(.not. associated(bin_op_ptr))
71+
72+
end subroutine
73+
end program
74+
75+

Examples/test-suite/fortran/funcptr_runme.F90

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,39 @@
22

33
#include "fassert.h"
44

5+
module funcptr_mod
6+
use, intrinsic :: ISO_C_BINDING
7+
implicit none
8+
contains
9+
10+
function myexp(left, right) bind(C) &
11+
result(fresult)
12+
use, intrinsic :: ISO_C_BINDING
13+
integer(C_INT), intent(in), value :: left
14+
integer(C_INT), intent(in), value :: right
15+
integer(C_INT) :: fresult
16+
17+
fresult = left ** right
18+
end function
19+
20+
end module
21+
522
program funcptr_runme
623
use funcptr
24+
use funcptr_mod
725
use ISO_C_BINDING
826
implicit none
9-
type(C_FUNPTR) :: fp
27+
procedure(SWIGTYPE_f_int_int__int), pointer :: fp
1028

11-
! Add
12-
call set_handle(0, fp)
29+
! Get the C++ function handle, which starts out as 'add'
30+
fp => get_funcvar()
31+
ASSERT(fp(2, 3) == 5)
1332
ASSERT(do_op(2, 3, fp) == 5)
1433

15-
! Subtract
16-
call set_handle(1, fp)
17-
call set_funcvar(fp)
18-
ASSERT(do_op(2, 3, get_funcvar()) == -1)
34+
! Set the function handle
35+
call set_funcvar(myexp)
36+
! Get it back, then call it from C++
37+
ASSERT(do_op(2, 3, get_funcvar()) == 8)
1938

2039
end program
2140

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
%module fortran_callback
2+
3+
#ifndef __cplusplus
4+
// Directly bind all functions: don't create proxy wrappers
5+
%fortranbindc;
6+
#endif
7+
8+
// Declare callback signature
9+
%fortrancallback("%s");
10+
#ifdef __cplusplus
11+
extern "C" {
12+
#endif
13+
int binary_op(int left, int right);
14+
void stupid_op(int left, int right);
15+
void stupider_op();
16+
#ifdef __cplusplus
17+
} // end extern
18+
#endif
19+
%nofortrancallback;
20+
21+
// Create callbacks and define functions
22+
%callback("%s_cb");
23+
24+
%inline %{
25+
#ifdef __cplusplus
26+
extern "C" {
27+
#endif
28+
29+
int add(int left, int right) { return left + right; }
30+
int mul(int left, int right) { return left * right; }
31+
32+
#ifdef __cplusplus
33+
} // end extern
34+
#endif
35+
%}
36+
37+
%nocallback;
38+
39+
// Declare callback signature *and* create function with wrapper
40+
%fortrancallback("%s_cb") call_binary;
41+
42+
%{
43+
#include <string.h>
44+
#include <stdio.h>
45+
%}
46+
47+
%inline %{
48+
#ifdef __cplusplus
49+
extern "C" {
50+
#endif
51+
52+
typedef int also_an_int;
53+
54+
typedef int (*binary_op_cb)(int, int);
55+
int call_binary(binary_op_cb fptr, int left, also_an_int right)
56+
{ return (*fptr)(left, right); }
57+
58+
#ifdef __cplusplus
59+
} // end extern
60+
#endif
61+
62+
typedef int (*noarg_cb)(void);
63+
int call_things(noarg_cb fptr)
64+
{ return (*fptr)(); }
65+
66+
typedef void (*one_int_cb)(int);
67+
void also_call_things(one_int_cb fptr, int val)
68+
{ return (*fptr)(val); }
69+
70+
binary_op_cb get_a_callback(const char* name) {
71+
if (strcmp(name, "add") == 0) {
72+
return &add;
73+
} if (strcmp(name, "mul") == 0) {
74+
return &mul;
75+
}
76+
printf("Invalid callback name '%s'\n", name);
77+
return NULL;
78+
}
79+
80+
%}
81+
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
%module fortran_callback_c
2+
3+
%include "fortran_callback.i"
4+

0 commit comments

Comments
 (0)