Skip to content

Commit f32f624

Browse files
committed
Allocatable classes for factories
1 parent 69b61af commit f32f624

File tree

13 files changed

+346
-67
lines changed

13 files changed

+346
-67
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ build
66
*.mod
77
*.a
88
*.so
9+
*.x
910
f90wrap*.f90
1011
*.pyc
1112
.pydevproject
@@ -20,3 +21,4 @@ src.*
2021
.ipynb_checkpoints
2122
.idea/
2223
*.swp
24+
itest/

examples/Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,10 @@ EXAMPLES = arrayderivedtypes \
3131
intent_out_size \
3232
output_kind \
3333
remove_pointer_arg \
34-
fortran_oo
34+
fortran_oo \
35+
issue206_subroutine_oldstyle \
36+
issue227_allocatable \
37+
issue235_allocatable_classes
3538

3639
PYTHON = python
3740

examples/issue227_allocatable/run.py

Lines changed: 26 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,41 @@
11
#!/usr/bin/env python
2-
import os
2+
import unittest
33
import gc
44
import tracemalloc
55

66
import itest
77

8-
9-
def main():
10-
test_type_output_is_wrapped()
11-
test_intrinsic_output_is_not_wrapped()
12-
test_array_output_is_not_wrapped()
13-
test_type_output_wrapper()
14-
test_memory_leak()
15-
16-
17-
def test_type_output_is_wrapped():
18-
assert hasattr(itest.alloc_output, 'alloc_output_type_func')
19-
20-
21-
def test_intrinsic_output_is_not_wrapped():
22-
assert (not hasattr(itest.alloc_output, 'alloc_output_intrinsic_func'))
23-
24-
25-
def test_array_output_is_not_wrapped():
26-
assert (not hasattr(itest.alloc_output, 'alloc_output_array_func'))
27-
28-
298
VAL = 10.0
309
TOL = 1e-13
3110

11+
class TestAllocOutput(unittest.TestCase):
12+
13+
def test_type_output_is_wrapped(self):
14+
self.assertTrue(hasattr(itest.alloc_output, 'alloc_output_type_func'))
3215

33-
def test_type_output_wrapper():
34-
t = itest.alloc_output.alloc_output_type_func(VAL)
35-
assert(abs(t.a - VAL) < TOL)
16+
def test_intrinsic_output_is_not_wrapped(self):
17+
self.assertFalse(hasattr(itest.alloc_output, 'alloc_output_intrinsic_func'))
3618

19+
def test_array_output_is_not_wrapped(self):
20+
self.assertFalse(hasattr(itest.alloc_output, 'alloc_output_array_func'))
3721

38-
def test_memory_leak():
39-
gc.collect()
40-
t = []
41-
tracemalloc.start()
42-
start_snapshot = tracemalloc.take_snapshot()
43-
for i in range(2048):
44-
t.append(itest.alloc_output.alloc_output_type_func(VAL))
45-
del t
46-
gc.collect()
47-
end_snapshot = tracemalloc.take_snapshot()
48-
tracemalloc.stop()
49-
stats = end_snapshot.compare_to(start_snapshot, 'lineno')
50-
assert sum(stat.size_diff for stat in stats) < 1024
22+
def test_type_output_wrapper(self):
23+
t = itest.alloc_output.alloc_output_type_func(VAL)
24+
self.assertAlmostEqual(t.a, VAL, delta=TOL)
5125

26+
def test_memory_leak(self):
27+
gc.collect()
28+
t = []
29+
tracemalloc.start()
30+
start_snapshot = tracemalloc.take_snapshot()
31+
for i in range(8192):
32+
t.append(itest.alloc_output.alloc_output_type_func(VAL))
33+
del t
34+
gc.collect()
35+
end_snapshot = tracemalloc.take_snapshot()
36+
tracemalloc.stop()
37+
stats = end_snapshot.compare_to(start_snapshot, 'lineno')
38+
self.assertLess(sum(stat.size_diff for stat in stats), 4096)
5239

5340
if __name__ == '__main__':
54-
main()
41+
unittest.main()
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
FC = gfortran
2+
FCFLAGS = -fPIC
3+
PYTHON = python
4+
5+
all: wrapper
6+
7+
test: wrapper
8+
$(PYTHON) run.py
9+
10+
wrapper: f90wrapper mytype.o myclass.o myclass_factory.o
11+
f2py-f90wrap --build-dir . -c -m _itest --opt="-O0 -g" \
12+
f90wrap_mytype.f90 f90wrap_myclass.f90 f90wrap_myclass_factory.f90 \
13+
mytype.o myclass.o myclass_factory.o
14+
15+
f90wrapper: mytype.f90 myclass.f90 myclass_factory.f90
16+
f90wrap -m itest -P mytype.f90 myclass.f90 myclass_factory.f90 -v
17+
18+
%.o : %.f90
19+
$(FC) $(FCFLAGS) -c -g -O0 $< -o $@
20+
21+
clean:
22+
rm -f *.o f90wrap*.f90 *.so *.mod
23+
rm -rf src.*/
24+
rm -rf itest/
25+
-rm -rf src.*/ .f2py_f2cmap .libs/ __pycache__/
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
include ../make.meson.inc
2+
3+
NAME := itest
4+
5+
test: build
6+
$(PYTHON) run.py
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
module myclass
2+
3+
implicit none
4+
5+
integer :: create_count = 0
6+
integer :: destroy_count = 0
7+
8+
type :: myclass_t
9+
real :: val
10+
contains
11+
procedure :: get_val => myclass_get_val
12+
procedure :: set_val => myclass_set_val
13+
final :: myclass_destroy
14+
end type myclass_t
15+
16+
contains
17+
18+
subroutine myclass_get_val(self, val)
19+
class(myclass_t), intent(in) :: self
20+
real, intent(out) :: val
21+
22+
val = self%val
23+
end subroutine myclass_get_val
24+
25+
subroutine myclass_set_val(self, val)
26+
class(myclass_t), intent(inout) :: self
27+
real, intent(in) :: val
28+
29+
self%val = val
30+
end subroutine myclass_set_val
31+
32+
subroutine myclass_destroy(self)
33+
type(myclass_t), intent(inout) :: self
34+
35+
destroy_count = destroy_count + 1
36+
print *, 'Destroying class_t with val = ', self%val
37+
end subroutine myclass_destroy
38+
39+
end module myclass
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
module myclass_factory
2+
3+
use myclass, only: myclass_t, create_count
4+
implicit none
5+
6+
contains
7+
8+
function myclass_create(val) result(myobject)
9+
class(myclass_t), allocatable :: myobject
10+
real, intent(in) :: val
11+
12+
allocate(myclass_t :: myobject)
13+
call myobject%set_val(val)
14+
create_count = create_count + 1
15+
16+
end function myclass_create
17+
18+
end module myclass_factory
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
module mytype
2+
3+
implicit none
4+
5+
integer :: create_count = 0
6+
integer :: destroy_count = 0
7+
8+
type :: mytype_t
9+
real :: val
10+
contains
11+
final :: mytype_destroy
12+
end type mytype_t
13+
14+
contains
15+
16+
function mytype_create(val) result(self)
17+
type(mytype_t) :: self
18+
real, intent(in) :: val
19+
20+
self%val = val
21+
create_count = create_count + 1
22+
end function mytype_create
23+
24+
subroutine mytype_destroy(self)
25+
type(mytype_t), intent(inout) :: self
26+
27+
destroy_count = destroy_count + 1
28+
print *, 'Destroying mytype_t with val = ', self%val
29+
end subroutine mytype_destroy
30+
31+
end module mytype
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#!/usr/bin/env python
2+
import unittest
3+
from itest import mytype, myclass, myclass_factory
4+
5+
REF = 3.1415
6+
TOL = 1.0e-6
7+
8+
class TestMyType(unittest.TestCase):
9+
10+
def test_create_destroy_type_object(self):
11+
"""Object creation and destruction should happen only once."""
12+
mytype.set_create_count(0)
13+
mytype.set_destroy_count(0)
14+
15+
obj = mytype.mytype_create(REF)
16+
17+
self.assertEqual(mytype.get_create_count(), 1)
18+
19+
self.assertTrue(abs(obj.val - REF) < TOL)
20+
21+
del obj
22+
23+
self.assertEqual(mytype.get_create_count(), 1)
24+
self.assertGreaterEqual(mytype.get_destroy_count(), 1)
25+
26+
def test_type_member_access(self):
27+
"""Direct access of member variables."""
28+
obj = mytype.mytype_create(REF)
29+
30+
self.assertTrue(abs(obj.val - REF) < TOL)
31+
32+
obj.val = 2.0 * REF
33+
34+
self.assertTrue(abs(obj.val - 2.0 * REF) < TOL)
35+
36+
del obj
37+
38+
39+
class TestMyClass(unittest.TestCase):
40+
41+
def test_create_destroy_class_object(self):
42+
"""Object creation and destruction should happen only once."""
43+
myclass.set_create_count(0)
44+
myclass.set_destroy_count(0)
45+
46+
obj = myclass_factory.myclass_create(REF)
47+
48+
self.assertEqual(myclass.get_create_count(), 1)
49+
50+
self.assertTrue(abs(obj.get_val() - REF) < TOL)
51+
52+
del obj
53+
54+
self.assertEqual(myclass.get_create_count(), 1)
55+
self.assertGreaterEqual(myclass.get_destroy_count(), 1)
56+
57+
def test_class_getter_setter(self):
58+
"""Getters and setters defined in Fortran should work."""
59+
obj = myclass_factory.myclass_create(REF)
60+
61+
self.assertTrue(abs(obj.get_val() - REF) < TOL)
62+
63+
obj.set_val(2.0 * REF)
64+
65+
self.assertTrue(abs(obj.get_val() - 2.0 * REF) < TOL)
66+
67+
del obj
68+
69+
def test_class_member_access(self):
70+
"""Direct access of member variables."""
71+
obj = myclass_factory.myclass_create(REF)
72+
73+
self.assertTrue(abs(obj.val - REF) < TOL)
74+
75+
obj.val = 2.0 * REF
76+
77+
self.assertTrue(abs(obj.val - 2.0 * REF) < TOL)
78+
79+
del obj
80+
81+
82+
if __name__ == "__main__":
83+
unittest.main()

0 commit comments

Comments
 (0)