|
4 | 4 | !> Generation to be automated in future (including docstrings of some sort). |
5 | 5 | module m_error_v_ptr_based_w |
6 | 6 |
|
7 | | - use iso_c_binding, only: c_ptr, c_f_pointer |
| 7 | + use iso_c_binding, only: c_f_pointer, c_loc, c_null_ptr, c_ptr |
8 | 8 |
|
9 | 9 | use m_error_v, only: ErrorV |
10 | 10 |
|
11 | 11 | implicit none |
12 | 12 | private |
13 | 13 |
|
14 | | - public :: iget_code, iget_message |
| 14 | + public :: get_instance_ptr, instance_build, instance_finalise, is_associated, & |
| 15 | + iget_code, iget_message |
15 | 16 |
|
16 | 17 | contains |
17 | 18 |
|
| 19 | + subroutine get_instance_ptr(res_instance_ptr) |
| 20 | + !> Get a pointer to a new instance |
| 21 | + ! |
| 22 | + ! Needs to be subroutine to have the created instance persist I think |
| 23 | + ! (we can check) |
| 24 | + ! function create_error(inv) result(res_instance_index) |
| 25 | + |
| 26 | + !f2py integer(8), intent(out) :: res_instance_ptr |
| 27 | + type(c_ptr), intent(out) :: res_instance_ptr |
| 28 | + !! Pointer to the resulting instance |
| 29 | + ! |
| 30 | + ! This is the major trick for wrapping. |
| 31 | + ! We return pointers (passed as integers) to Python rather than the instance itself. |
| 32 | + |
| 33 | + type(ErrorV), pointer :: res |
| 34 | + ! Question is: when does this get deallocated? |
| 35 | + ! When we go out of scope? |
| 36 | + ! If yes, that will be why we had to do this array thing. |
| 37 | + allocate(res) |
| 38 | + |
| 39 | + res_instance_ptr = c_loc(res) |
| 40 | + |
| 41 | + end subroutine get_instance_ptr |
| 42 | + |
| 43 | + subroutine instance_build(instance_ptr, code, message) |
| 44 | + !> Build an instance |
| 45 | + |
| 46 | + !f2py integer(8), intent(in) :: instance_ptr |
| 47 | + type(c_ptr), intent(in) :: instance_ptr |
| 48 | + !! Pointer to the instance |
| 49 | + ! |
| 50 | + ! This is the major trick for wrapping. |
| 51 | + ! We pass pointers (passed as integers) to Python rather than the instance itself. |
| 52 | + |
| 53 | + integer, intent(in) :: code |
| 54 | + character(len=*), optional, intent(in) :: message |
| 55 | + |
| 56 | + type(ErrorV), pointer :: inst |
| 57 | + |
| 58 | + call c_f_pointer(instance_ptr, inst) |
| 59 | + |
| 60 | + call inst % build(code, message) |
| 61 | + |
| 62 | + end subroutine instance_build |
| 63 | + |
| 64 | + subroutine instance_finalise(instance_ptr) |
| 65 | + !> Finalise an instance |
| 66 | + |
| 67 | + !f2py integer(8), intent(inout) :: instance_ptr |
| 68 | + type(c_ptr), intent(inout) :: instance_ptr |
| 69 | + !! Pointer to the instance |
| 70 | + ! |
| 71 | + ! This is the major trick for wrapping. |
| 72 | + ! We pass pointers (passed as integers) to Python rather than the instance itself. |
| 73 | + |
| 74 | + type(ErrorV), pointer :: inst |
| 75 | + |
| 76 | + call c_f_pointer(instance_ptr, inst) |
| 77 | + |
| 78 | + ! This may be why we used the array approach. |
| 79 | + ! The issue here is that, if you call this method twice, |
| 80 | + ! there is no way to work out that you're the 'second caller'. |
| 81 | + ! When the first call calls `deallocate(inst)`, |
| 82 | + ! this puts any other pointers to the instance in an undefined status |
| 83 | + ! (https://www.ibm.com/docs/en/xl-fortran-aix/16.1.0?topic=attributes-deallocate). |
| 84 | + ! The result of calling associated on an undefined pointer |
| 85 | + ! can be anything (https://stackoverflow.com/questions/72140217/can-you-test-for-nullpointers-in-fortran), |
| 86 | + ! i.e. there is no way to tell that someone else |
| 87 | + ! has already called finalise before you have. |
| 88 | + ! This also explains the undefined status issue nicely: |
| 89 | + ! community.intel.com/t5/Intel-Fortran-Compiler/DEALLOCATING-DATA-TYPE-POINTERS/m-p/982338#M100027 |
| 90 | + ! |
| 91 | + ! We'd have to introduce some reference counter to make this work I think. |
| 92 | + ! Probably better advice for now, don't share pointer values |
| 93 | + ! on the Python side, you have to be super careful about uninitialising if you do. |
| 94 | + ! Avoiding pointers and using allocatable instead |
| 95 | + ! was probably the other reason we did it how we did |
| 96 | + ! community.intel.com/t5/Intel-Fortran-Compiler/how-to-test-if-pointer-array-is-allocated/m-p/1138643#M136486. |
| 97 | + if (associated(inst)) then |
| 98 | + call inst % finalise() |
| 99 | + deallocate(inst) |
| 100 | + end if |
| 101 | + |
| 102 | + end subroutine instance_finalise |
| 103 | + |
| 104 | + subroutine is_associated(instance_ptr, res) |
| 105 | + !> Check if a pointer is associated with an instance |
| 106 | + |
| 107 | + !f2py integer(8), intent(in) :: instance_ptr |
| 108 | + type(c_ptr), intent(in) :: instance_ptr |
| 109 | + !! Pointer to the instance |
| 110 | + ! |
| 111 | + ! This is the major trick for wrapping. |
| 112 | + ! We pass pointers (passed as integers) to Python rather than the instance itself. |
| 113 | + |
| 114 | + logical, intent(out) :: res |
| 115 | + !! Whether `instance_ptr` is associated or not |
| 116 | + |
| 117 | + type(ErrorV), pointer :: inst |
| 118 | + |
| 119 | + call c_f_pointer(instance_ptr, inst) |
| 120 | + |
| 121 | + print *, instance_ptr |
| 122 | + print *, inst |
| 123 | + res = associated(inst) |
| 124 | + print *, res |
| 125 | + |
| 126 | + end subroutine is_associated |
| 127 | + |
18 | 128 | ! Full set of wrapping strategies to pass different types in e.g. |
19 | 129 | ! https://gitlab.com/magicc/fgen/-/blob/switch-to-uv/tests/test-data/exposed_attrs/src/exposed_attrs/exposed_attrs_wrapped.f90 |
20 | 130 | ! (we will do a full re-write of the code which generates this, |
|
0 commit comments