Skip to content
This repository was archived by the owner on Dec 30, 2023. It is now read-only.

Commit da50cdd

Browse files
committed
Merge pull request #32 from larsmans/exceptions
Get some exception safety, using Cython's built-in exception translation
2 parents 126b4cf + 4ee7461 commit da50cdd

File tree

5 files changed

+63
-15
lines changed

5 files changed

+63
-15
lines changed

Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
pcl/_pcl.so: pcl/_pcl.pyx setup.py pcl/pcl_defs.pxd pcl/minipcl.cpp
1+
pcl/_pcl.so: pcl/_pcl.pyx setup.py pcl/pcl_defs.pxd pcl/minipcl.cpp \
2+
pcl/indexing.hpp
23
python setup.py build_ext --inplace
34

45
test: pcl/_pcl.so tests/test.py

pcl/_pcl.pyx

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ cdef class PointCloud:
174174

175175
cdef cpp.PointXYZ *p
176176
for i in range(npts):
177-
p = &self.thisptr.at(i)
177+
p = cpp.getptr(self.thisptr, i)
178178
p.x, p.y, p.z = arr[i, 0], arr[i, 1], arr[i, 2]
179179

180180
@cython.boundscheck(False)
@@ -190,7 +190,7 @@ cdef class PointCloud:
190190
result = np.empty((n, 3), dtype=np.float32)
191191

192192
for i in range(n):
193-
p = &self.thisptr.at(i)
193+
p = cpp.getptr(self.thisptr, i)
194194
result[i, 0] = p.x
195195
result[i, 1] = p.y
196196
result[i, 2] = p.z
@@ -206,8 +206,8 @@ cdef class PointCloud:
206206
self.resize(npts)
207207
self.thisptr.width = npts
208208
self.thisptr.height = 1
209-
for i,l in enumerate(_list):
210-
p = &self.thisptr.at(i)
209+
for i, l in enumerate(_list):
210+
p = cpp.getptr(self.thisptr, i)
211211
p.x, p.y, p.z = l
212212

213213
def to_list(self):
@@ -223,11 +223,11 @@ cdef class PointCloud:
223223
"""
224224
Return a point (3-tuple) at the given row/column
225225
"""
226-
cdef cpp.PointXYZ *p = &self.thisptr.at(row, col)
226+
cdef cpp.PointXYZ *p = cpp.getptr_at(self.thisptr, row, col)
227227
return p.x, p.y, p.z
228228

229229
def __getitem__(self, cnp.npy_intp idx):
230-
cdef cpp.PointXYZ *p = &self.thisptr.at(idx)
230+
cdef cpp.PointXYZ *p = cpp.getptr_at(self.thisptr, idx)
231231
return p.x, p.y, p.z
232232

233233
def from_file(self, char *f):
@@ -566,15 +566,21 @@ cdef class OctreePointCloud:
566566
Octree pointcloud
567567
"""
568568
cdef cpp.OctreePointCloud_t *me
569-
569+
570570
def __cinit__(self, double resolution):
571+
self.me = NULL
572+
if resolution <= 0.:
573+
raise ValueError("Expected resolution > 0., got %r" % resolution)
574+
575+
def __init__(self, double resolution):
571576
"""
572577
Constructs octree pointcloud with given resolution at lowest octree level
573578
"""
574579
self.me = new cpp.OctreePointCloud_t(resolution)
575-
580+
576581
def __dealloc__(self):
577582
del self.me
583+
self.me = NULL # just to be sure
578584

579585
def set_input_cloud(self, PointCloud pc):
580586
"""
@@ -637,9 +643,6 @@ cdef class OctreePointCloudSearch(OctreePointCloud):
637643
"""
638644
self.me = <cpp.OctreePointCloud_t*> new cpp.OctreePointCloudSearch_t(resolution)
639645

640-
def __dealloc__(self):
641-
del self.me
642-
643646
def radius_search (self, point, double radius, unsigned int max_nn = 0):
644647
"""
645648
Search for all neighbors of query point that are within a given radius.

pcl/indexing.hpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
namespace {
2+
// Workaround for a Cython bug in operator[] with templated types and
3+
// references. Let's hope the compiler optimizes these functions away.
4+
template <typename T>
5+
T *getptr(pcl::PointCloud<T> *pc, size_t i)
6+
{
7+
return &(*pc)[i];
8+
}
9+
10+
template <typename T>
11+
T *getptr_at(pcl::PointCloud<T> *pc, size_t i)
12+
{
13+
return &(pc->at(i));
14+
}
15+
16+
template <typename T>
17+
T *getptr_at(pcl::PointCloud<T> *pc, int i, int j)
18+
{
19+
return &(pc->at(i, j));
20+
}
21+
}

pcl/pcl_defs.pxd

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,17 @@ cdef extern from "pcl/point_cloud.h" namespace "pcl":
1616
bool is_dense
1717
void resize(size_t) except +
1818
size_t size()
19-
T& operator[](size_t)
20-
T& at(size_t)
21-
T& at(int, int)
19+
#T& operator[](size_t)
20+
#T& at(size_t) except +
21+
#T& at(int, int) except +
2222
shared_ptr[PointCloud[T]] makeShared()
2323

24+
cdef extern from "indexing.hpp":
25+
# Use these instead of operator[] or at.
26+
PointXYZ *getptr(PointCloud[PointXYZ] *, size_t)
27+
PointXYZ *getptr_at(PointCloud[PointXYZ] *, size_t) except +
28+
PointXYZ *getptr_at(PointCloud[PointXYZ] *, int, int) except +
29+
2430
cdef extern from "pcl/point_types.h" namespace "pcl":
2531
cdef struct PointXYZ:
2632
PointXYZ()

tests/test.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,20 @@ def testExtractNeg(self):
208208
self.assertNotEqual(self.p, p2)
209209
self.assertEqual(p2.size, self.p.size - 3)
210210

211+
class TestExceptions(unittest.TestCase):
212+
def setUp(self):
213+
self.p = pcl.PointCloud(np.arange(9, dtype=np.float32).reshape(3, 3))
214+
215+
def testIndex(self):
216+
self.assertRaises(IndexError, self.p.__getitem__, self.p.size)
217+
self.assertRaises(Exception, self.p.get_point, self.p.size, 1)
218+
219+
def testResize(self):
220+
# XXX MemoryError isn't actually the prettiest exception for a
221+
# negative argument. Don't hesitate to change this test to reflect
222+
# better exceptions.
223+
self.assertRaises(MemoryError, self.p.resize, -1)
224+
211225
class TestSegmenterNormal(unittest.TestCase):
212226

213227
def setUp(self):
@@ -312,6 +326,9 @@ def setUp(self):
312326
self.t.define_bounding_box()
313327
self.t.add_points_from_input_cloud()
314328

329+
def testConstructor(self):
330+
self.assertRaises(ValueError, pcl.OctreePointCloudSearch, 0.)
331+
315332
def testRadiusSearch(self):
316333
good_point = (0.035296999, -0.074322999, 1.2074)
317334
rs = self.t.radius_search(good_point, 0.5, 1)

0 commit comments

Comments
 (0)