1
+ """Fortran binding generation code.
2
+
3
+ This takes as input a *.in file containing the prototype of a Fortran function
4
+ with generic types. Both the Fortran subroutine and a wrapping C function can
5
+ generated from this file.
6
+ """
1
7
from abc import ABC , abstractmethod
2
8
import argparse
3
9
import re
4
10
5
11
12
+ C_ERROR_TEMP_NAME = 'c_ierr'
13
+ GENERATED_MESSAGE = 'THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT BY HAND.'
14
+ PROTOTYPE_RE = re .compile (r'^\w+\((\s*\w+\s+\w+\s*,?)+\)$' )
15
+
16
+
6
17
class FortranType (ABC ):
7
18
8
19
def __init__ (self , name , ** kwargs ):
@@ -56,6 +67,12 @@ def c_post(self):
56
67
return []
57
68
58
69
70
+ #
71
+ # Definitions of generic types in Fortran and how these can be converted
72
+ # to and from C.
73
+ #
74
+
75
+
59
76
@FortranType .add ('BUFFER' )
60
77
class BufferType (FortranType ):
61
78
def declare (self ):
@@ -84,11 +101,16 @@ def c_argument(self):
84
101
return f'*{ self .name } ' if self .bigcount else f'OMPI_FINT_2_INT(*{ self .name } )'
85
102
86
103
87
- def tmp_c_type (name ):
104
+ def tmp_c_name (name ):
88
105
"""Return a temporary name for use in C."""
89
106
return f'c_{ name } '
90
107
91
108
109
+ def tmp_c_name2 (name ):
110
+ """Return a secondary temporary name for use in C."""
111
+ return f'c_{ name } 2'
112
+
113
+
92
114
@FortranType .add ('DATATYPE' )
93
115
class DatatypeType (FortranType ):
94
116
def declare (self ):
@@ -107,10 +129,10 @@ def c_parameter(self):
107
129
return f'MPI_Fint *{ self .name } '
108
130
109
131
def c_prepare (self ):
110
- return [f'MPI_Datatype { tmp_c_type (self .name )} = PMPI_Type_f2c(*{ self .name } );' ]
132
+ return [f'MPI_Datatype { tmp_c_name (self .name )} = PMPI_Type_f2c(*{ self .name } );' ]
111
133
112
134
def c_argument (self ):
113
- return tmp_c_type (self .name )
135
+ return tmp_c_name (self .name )
114
136
115
137
116
138
class IntType (FortranType ):
@@ -152,13 +174,35 @@ def c_parameter(self):
152
174
return f'MPI_Fint *{ self .name } '
153
175
154
176
def c_prepare (self ):
155
- return [f'MPI_Comm { tmp_c_type (self .name )} = PMPI_Comm_f2c(*{ self .name } );' ]
177
+ return [f'MPI_Comm { tmp_c_name (self .name )} = PMPI_Comm_f2c(*{ self .name } );' ]
156
178
157
179
def c_argument (self ):
158
- return tmp_c_type (self .name )
180
+ return tmp_c_name (self .name )
159
181
160
182
161
- PROTOTYPE_RE = re .compile (r'^\w+\((\s*\w+\s+\w+\s*,?)+\)$' )
183
+ @FortranType .add ('STATUS' )
184
+ class StatusType (FortranType ):
185
+ def declare (self ):
186
+ return f'TYPE(MPI_Status), INTENT(OUT) :: { self .name } '
187
+
188
+ def use (self ):
189
+ return [('mpi_f08_types' , 'MPI_Status' )]
190
+
191
+ def c_parameter (self ):
192
+ # TODO: Is this correct? (I've listed it as TYPE(MPI_Status) in the binding)
193
+ return f'MPI_Fint *{ self .name } '
194
+
195
+ def c_prepare (self ):
196
+ return [
197
+ f'OMPI_FORTRAN_STATUS_DECLARATION({ tmp_c_name (self .name )} , { tmp_c_name2 (self .name )} );' ,
198
+ f'OMPI_FORTRAN_STATUS_SET_POINTER({ tmp_c_name (self .name )} , { tmp_c_name2 (self .name )} , { self .name } );'
199
+ ]
200
+
201
+ def c_argument (self ):
202
+ return tmp_c_name (self .name )
203
+
204
+ def c_post (self ):
205
+ return [f'OMPI_FORTRAN_STATUS_RETURN({ tmp_c_name (self .name )} , { tmp_c_name2 (self .name )} , { self .name } , { C_ERROR_TEMP_NAME } );' ]
162
206
163
207
164
208
class PrototypeParseError (Exception ):
@@ -186,10 +230,6 @@ def print_header():
186
230
print ('#include "mpi-f08-rename.h"' )
187
231
188
232
189
-
190
- GENERATED_MESSAGE = 'THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT BY HAND.'
191
-
192
-
193
233
class FortranBinding :
194
234
195
235
def __init__ (self , fname ):
@@ -272,7 +312,7 @@ def print_f_source(self):
272
312
# Add the integer error manually
273
313
print (' INTEGER, OPTIONAL, INTENT(OUT) :: ierror' )
274
314
# Temporaries
275
- print (' INTEGER :: c_ierror ' )
315
+ print (f ' INTEGER :: { C_ERROR_TEMP_NAME } ' )
276
316
277
317
# Interface for call to C function
278
318
print ()
@@ -281,9 +321,9 @@ def print_f_source(self):
281
321
282
322
# Call into the C function
283
323
args = ',' .join (param .argument () for param in self .parameters )
284
- print (f' call { c_func } ({ args } ,c_ierror )' )
324
+ print (f' call { c_func } ({ args } ,{ C_ERROR_TEMP_NAME } )' )
285
325
# Convert error type
286
- print (' if (present(ierror)) ierror = c_ierror ' )
326
+ print (f ' if (present(ierror)) ierror = { C_ERROR_TEMP_NAME } ' )
287
327
288
328
print (f'end subroutine { sub_name } ' )
289
329
@@ -292,6 +332,7 @@ def print_c_source(self):
292
332
print (f'/* { GENERATED_MESSAGE } */' )
293
333
print ('#include "ompi_config.h"' )
294
334
print ('#include "mpi.h"' )
335
+ print ('#include "ompi/mpi/fortran/mpif-h/status-conversion.h"' )
295
336
print ('#include "ompi/mpi/fortran/base/constants.h"' )
296
337
print ('#include "ompi/mpi/fortran/base/fint_2_int.h"' )
297
338
c_func = c_func_name (self .fn_name )
@@ -303,19 +344,19 @@ def print_c_source(self):
303
344
print (f'void { c_func } ({ parameters } );' )
304
345
print (f'void { c_func } ({ parameters } )' )
305
346
print ('{' )
306
- print (' int c_ierr ; ' )
347
+ print (f ' int { C_ERROR_TEMP_NAME } ; ' )
307
348
for param in self .parameters :
308
349
for line in param .c_prepare ():
309
350
print (f' { line } ' )
310
351
c_api_func = c_api_func_name (self .fn_name )
311
352
arguments = [param .c_argument () for param in self .parameters ]
312
353
arguments = ', ' .join (arguments )
313
- print (f' c_ierr = { c_api_func } ({ arguments } );' )
354
+ print (f' { C_ERROR_TEMP_NAME } = { c_api_func } ({ arguments } );' )
314
355
for param in self .parameters :
315
356
for line in param .c_post ():
316
357
print (f' { line } ' )
317
358
# TODO: Is this NULL check necessary for mpi_f08?
318
- print (' if (NULL != ierr) *ierr = OMPI_INT_2_FINT(c_ierr );' )
359
+ print (f ' if (NULL != ierr) *ierr = OMPI_INT_2_FINT({ C_ERROR_TEMP_NAME } );' )
319
360
print ('}' )
320
361
321
362
0 commit comments