Skip to content

Commit d2d347e

Browse files
authored
Merge pull request #136 from swig-fortran/improve-memory
Fix reference checking and improve documentation
2 parents 517540c + 5c37f62 commit d2d347e

File tree

7 files changed

+96
-56
lines changed

7 files changed

+96
-56
lines changed

Doc/Manual/src/Fortran.md

Lines changed: 46 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,20 +1013,49 @@ the call to `SWIG_check_unhandled_exception` ensures that no previous unhandled
10131013
error exists. If you wish to wrap only a few functions with only specific
10141014
exceptions, use the ["throws" typemap](SWIG.html#throws_typemap).
10151015

1016+
The error codes (``SWIG_RuntimeError``, etc.) above will be generated as
1017+
public Fortran parameter constants when using the `<exception.i>` header. Thus
1018+
you can check for more specific errors as needed:
1019+
```fortran
1020+
b = get_from_reference(a)
1021+
if (ierr == SWIG_NullReferenceError) then
1022+
write(0,*) "'a' must be allocated before passing to 'get_from_reference'"
1023+
stop 1
1024+
endif
1025+
```
1026+
1027+
### Using exceptions in larger projects or software libraries
1028+
10161029
When exception handling code is used, SWIG generates a few internal data
10171030
structures as well as two externally accessible symbols with external C linkage
10181031
(`ierr` and `get_serr`). Fortran bindings are generated to make the integer and
10191032
function accessible from the Fortran module.
10201033

10211034
The names of the integer and string accessor have C linkage and thus must
1022-
be unique in a compiled program. Since other translation units might have
1035+
be unique in a compiled program *and* to all downstream codes linked against
1036+
it. Since other translation units might have
10231037
symbols that share the default exception handling names, the user can provide
1024-
custom names before including the exception handling file:
1038+
custom names before including the exception handling file. A `%rename`
1039+
directive can then reset the Fortran proxy name to something simpler while
1040+
retaining the scoped C linkage variable names.
1041+
1042+
In this example, the C-linkage variables generated will be `_scoped_ierr` and `_scoped_get_serr`:
10251043
```swig
1026-
#define SWIG_FORTRAN_ERROR_INT my_ierr
1027-
#define SWIG_FORTRAN_ERROR_STR get_my_serr
1044+
%module foo;
1045+
1046+
#define SWIG_FORTRAN_ERROR_INT scoped_ierr
1047+
#define SWIG_FORTRAN_ERROR_STR scoped_get_serr
1048+
%rename(ierr) scoped_ierr;
1049+
%rename(get_serr) scoped_get_serr;
10281050
%include <std_except.i>
10291051
```
1052+
but because of the %rename directives, they can still be accessed from Fortran
1053+
with simpler names since they are "scoped" to the generated module:
1054+
```fortran
1055+
use foo, only : ierr, get_serr
1056+
```
1057+
1058+
### Exceptions with multiple modules
10301059

10311060
If you're linking multiple modules together (using %import or otherwise), only
10321061
one of those modules should define the error integer and accessor by including
@@ -1552,7 +1581,8 @@ A single Fortran proxy class must be able to act as a value, a pointer, or a
15521581
reference to a C++ class instance.
15531582
When stored as a value, a method must be put in place to deallocate the
15541583
associated memory; if the instance is a reference, that same method cannot
1555-
double-delete the associated memory. Finally, C++ functions
1584+
double-delete the associated memory. An additional complication is that C++
1585+
functions
15561586
must be able to send Fortran pointers both *with and without* owning the
15571587
associated memory, depending on the function. Finally,
15581588
assignment between Fortran classes must preserve memory association.
@@ -1595,23 +1625,14 @@ temporary's memory. Unfortunately, only the very latest compilers (as of 2018,
15951625
14 years after the standard was ratified) have full support for the `FINAL` keyword.
15961626

15971627
Our solution to this limitation is to have the `Foo` proxy class store not only
1598-
a pointer to the C data but also a state enumeration `self%swigdata%mem` that
1599-
describes memory ownership. The enumeration needs to have at least three options:
1600-
1601-
- The memory is *owned* by the proxy class (and must be deleted when calling
1602-
`release()`);
1603-
- The proxy class is a *reference* to memory owned by C/C++ (returned by either
1604-
a raw pointer or a reference);
1605-
- The memory is being allocated and returned from a function, but it must be
1606-
captured by the left hand side.
1607-
1608-
This last option is roughly analogous to the behavior of the deprecated
1609-
`std::auto_ptr`, which was the predecessor to C++11's `move` semantics.
1610-
Besides the above flags, we also define an uninitialized state `NULL` for
1611-
convenience, and a "const reference" state to enable const correctness. These
1612-
flags are set by the SWIG `out` typemaps in the C wrapper code: if memory is
1613-
being allocated, the return flag is `MOVE`; if a pointer is being returned,
1614-
`REF` (or `CREF` in the const case) is used.
1628+
a pointer to the C data (`self%swigdata%cptr`) but also a set of state flags
1629+
(`self%swigdata%cmemflags`) that describes memory ownership.
1630+
Currently there are two flags:
1631+
1632+
- Ownership (the `swig_cmem_own_bit` in Fortran wrapper code) is true if
1633+
freeing the wrapper should destroy and free the corresponding C/C++ memory.
1634+
- If ownership of the class instance is being transferred from a function, the
1635+
`rvalue` bit is set (`swig_cmem_rvalue_bit`).
16151636

16161637
The crucial trick is to implement an assignment operator that correctly copies,
16171638
allocates, or moves memory based on the flags on the left- and right-hand sides,
@@ -1633,19 +1654,15 @@ omitted.
16331654
| NULL | NULL | (none) |
16341655
| NULL | MOVE | `pself = pother;` |
16351656
| NULL | OWN | `pself = new This(pother);` |
1636-
| NULL | REF/CREF | `pself = pother;` |
1657+
| NULL | REF | `pself = pother;` |
16371658
| OWN | NULL | `delete pself; pself = NULL;` |
16381659
| OWN | MOVE | `*pself = move(*pother); delete pother;`|
16391660
| OWN | OWN | `*pself = *pother;` |
1640-
| OWN | REF/CREF | `*pself = *pother;` |
1661+
| OWN | REF | `*pself = *pother;` |
16411662
| REF | NULL | `pself = NULL;` |
16421663
| REF | MOVE | `*pself = move(*pother); delete pother;`|
16431664
| REF | OWN | `*pself = *pother; ` |
1644-
| REF | REF/CREF | `*pself = *pother;` |
1645-
| CREF | NULL | `pself = NULL;` |
1646-
| CREF | MOVE | (error) |
1647-
| CREF | OWN | (error) |
1648-
| CREF | REF/CREF | (error) |
1665+
| REF | REF | `*pself = *pother;` |
16491666

16501667
The above operations are designed to preserve C++ semantics: if an proxy object
16511668
owning memory is assigned, then any existing objects pointing to that memory

Examples/test-suite/fortran/fortran_ownership_runme.F90

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,12 @@ subroutine test_standard
8484
call a%release() ! Free data pointed to by 'a'
8585
ASSERT(foo_counter == 1)
8686

87+
! Test error checking: passing a null value as a reference
88+
ASSERT(ierr == 0)
89+
c = const_reference(a)
90+
ASSERT(ierr == SWIG_NullReferenceError)
91+
ierr = 0
92+
8793
call c%set_val(5)
8894
! c = empty ! Release by assigning 'null'
8995
! ASSERT(foo_counter == 0 + num_leaks)

Examples/test-suite/fortran_ownership.i

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
#include "boost/shared_ptr.hpp"
55
%}
66

7+
// Enable error handling in Fortran code
8+
%include <exception.i>
9+
710
%fortranbindc foo_counter;
811
%fortranbindc bar_counter;
912

@@ -55,3 +58,20 @@ struct Bar {
5558
boost::shared_ptr<Bar> share(boost::shared_ptr<Bar> other) { return other; }
5659
int use_count(const boost::shared_ptr<Bar>& sp) { return sp.use_count(); }
5760
%}
61+
62+
// Test that autofree mangling works with namespaced values
63+
%fortran_autofree_rvalue(myns::Tricky)
64+
%fortran_autofree_rvalue(myns::TemplTricky<int>)
65+
%inline %{
66+
namespace myns {
67+
struct Tricky{};
68+
69+
template<class T>
70+
struct TemplTricky{};
71+
72+
void do_nothing(Tricky) {};
73+
void do_nothing(TemplTricky<int>) {};
74+
}
75+
%}
76+
77+
%template(TemplTrickyInt) myns::TemplTricky<int>;

Lib/fortran/boost_shared_ptr.i

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
%fragment("SWIG_check_sp_nonnull", "runtime", fragment="SwigMemState") %{
1313
#define SWIG_check_sp_nonnull(INPUT, TYPENAME, FNAME, FUNCNAME, RETURNNULL) \
1414
if (!(INPUT)) { \
15-
SWIG_exception_impl(FUNCNAME, SWIG_TypeError, \
15+
SWIG_exception_impl(FUNCNAME, SWIG_NullReferenceError, \
1616
"Cannot pass null " TYPENAME " (class " FNAME ") " \
1717
"as a reference", RETURNNULL); \
1818
}

Lib/fortran/classes.swg

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,24 +24,22 @@
2424
%define %fortran_autofree_rvalue(CLASS...)
2525

2626
%typemap(in) CLASS = SWIGTYPE;
27-
%typemap(argout, noblock=1, match="in") CLASS {
27+
%typemap(argout, noblock=1, fragment="SWIG_assign", match="in") CLASS {
2828
SWIG_free_rvalue< $1_ltype, SWIGPOLICY_ ## %mangle(CLASS) >(*$input);
2929
}
3030

3131
%typemap(in) CLASS* = SWIGTYPE*;
32-
%typemap(argout, noblock=1, match="in") CLASS* {
32+
%typemap(argout, noblock=1, fragment="SWIG_assign", match="in") CLASS* {
3333
SWIG_free_rvalue< $*1_ltype, SWIGPOLICY_ ## %mangle(CLASS) >(*$input);
3434
}
3535

3636
%typemap(in) CLASS[] = SWIGTYPE*;
3737
%typemap(argout, match="in") CLASS[] = CLASS*;
3838

39-
%typemap(in) CLASS& = SWIGTYPE*;
39+
%typemap(in) CLASS& = SWIGTYPE&;
4040
%typemap(argout, match="in") CLASS& = CLASS*;
4141

4242
// Restore special assignment/destruct typemaps...
43-
%typemap(in) CLASS& = SWIGTYPE*;
44-
4543
%typemap(in) CLASS *self = SWIGTYPE *self;
4644
%typemap(in) CLASS *ASSIGNMENT_SELF = SWIGTYPE *ASSIGNMENT_SELF;
4745
%typemap(in) CLASS &ASSIGNMENT_OTHER = SWIGTYPE &ASSIGNMENT_OTHER;
@@ -121,7 +119,7 @@ SWIGINTERN SwigClassWrapper SwigClassWrapper_uninitialized() {
121119
%fragment("SWIG_check_nonnull", "runtime") %{
122120
#define SWIG_check_nonnull(SWIG_CLASS_WRAPPER, TYPENAME, FNAME, FUNCNAME, RETURNNULL) \
123121
if (!(SWIG_CLASS_WRAPPER).cptr) { \
124-
SWIG_exception_impl(FUNCNAME, SWIG_TypeError, \
122+
SWIG_exception_impl(FUNCNAME, SWIG_NullReferenceError, \
125123
"Cannot pass null " TYPENAME " (class " FNAME ") " \
126124
"as a reference", RETURNNULL); \
127125
}

Lib/fortran/thrust.i

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828

2929
%typemap(check, noblock=1) (PTRTYPE DATA, size_t SIZE) {
3030
if ((thrust::raw_pointer_cast($1) == NULL) && ($2 != 0)) {
31-
SWIG_exception_impl("$decl", SWIG_TypeError, \
31+
SWIG_exception_impl("$decl", SWIG_NullReferenceError, \
3232
"Encountered null device pointer", return $null); \
3333
}
3434
}

Source/Modules/fortran.cxx

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2291,6 +2291,24 @@ int FORTRAN::classDeclaration(Node *n) {
22912291
}
22922292
}
22932293

2294+
// Define policy
2295+
if (CPlusPlus) {
2296+
if (SwigType *name = Getattr(n, "name")) {
2297+
String *policystr = Swig_string_mangle(name);
2298+
Insert(policystr, 0, "SWIGPOLICY_");
2299+
Setattr(n, "fortran:policy", policystr);
2300+
2301+
// Define policies for the class
2302+
const char *policy = "swig::ASSIGNMENT_DEFAULT";
2303+
if (Getattr(n, "feature:smartptr")) {
2304+
policy = "swig::ASSIGNMENT_SMARTPTR";
2305+
} else if (!GetFlag(n, "allocate:default_destructor")) {
2306+
policy = "swig::ASSIGNMENT_NODESTRUCT";
2307+
}
2308+
Printv(f_policies, "#define ", policystr, " ", policy, "\n", NULL);
2309+
}
2310+
}
2311+
22942312
return Language::classDeclaration(n);
22952313
}
22962314

@@ -2346,25 +2364,6 @@ int FORTRAN::classHandler(Node *n) {
23462364
}
23472365
Printv(f_class, ", public :: ", fsymname, "\n", NULL);
23482366

2349-
// Define policy
2350-
if (CPlusPlus)
2351-
{
2352-
SwigType *name = Getattr(n, "name");
2353-
ASSERT_OR_PRINT_NODE(name, n);
2354-
String *policystr = SwigType_manglestr(name);
2355-
Insert(policystr, 0, "SWIGPOLICY");
2356-
Setattr(n, "fortran:policy", policystr);
2357-
2358-
// Define policies for the class
2359-
const char *policy = "swig::ASSIGNMENT_DEFAULT";
2360-
if (Getattr(n, "feature:smartptr")) {
2361-
policy = "swig::ASSIGNMENT_SMARTPTR";
2362-
} else if (!GetFlag(n, "allocate:default_destructor")) {
2363-
policy = "swig::ASSIGNMENT_NODESTRUCT";
2364-
}
2365-
Printv(f_policies, "#define ", policystr, " ", policy, "\n", NULL);
2366-
}
2367-
23682367
if (!bindc) {
23692368
if (!base_fsymname) {
23702369
// Insert the class data if this doesn't inherit from anything

0 commit comments

Comments
 (0)