|
33 | 33 |
|
34 | 34 | #include "opencv2/core/utils/configuration.private.hpp"
|
35 | 35 | #include "opencv2/core/utils/logger.hpp"
|
| 36 | +#include "opencv2/core/utils/tls.hpp" |
36 | 37 |
|
37 | 38 | #include "pyopencv_generated_include.h"
|
38 | 39 | #include "opencv2/core/types_c.h"
|
@@ -138,6 +139,51 @@ class PyEnsureGIL
|
138 | 139 | PyGILState_STATE _state;
|
139 | 140 | };
|
140 | 141 |
|
| 142 | +/** |
| 143 | + * Light weight RAII wrapper for `PyObject*` owning references. |
| 144 | + * In comparisson to C++11 `std::unique_ptr` with custom deleter, it provides |
| 145 | + * implicit conversion functions that might be useful to initialize it with |
| 146 | + * Python functions those returns owning references through the `PyObject**` |
| 147 | + * e.g. `PyErr_Fetch` or directly pass it to functions those want to borrow |
| 148 | + * reference to object (doesn't extend object lifetime) e.g. `PyObject_Str`. |
| 149 | + */ |
| 150 | +class PySafeObject |
| 151 | +{ |
| 152 | +public: |
| 153 | + PySafeObject() : obj_(NULL) {} |
| 154 | + |
| 155 | + explicit PySafeObject(PyObject* obj) : obj_(obj) {} |
| 156 | + |
| 157 | + ~PySafeObject() |
| 158 | + { |
| 159 | + Py_CLEAR(obj_); |
| 160 | + } |
| 161 | + |
| 162 | + operator PyObject*() |
| 163 | + { |
| 164 | + return obj_; |
| 165 | + } |
| 166 | + |
| 167 | + operator PyObject**() |
| 168 | + { |
| 169 | + return &obj_; |
| 170 | + } |
| 171 | + |
| 172 | + PyObject* release() |
| 173 | + { |
| 174 | + PyObject* obj = obj_; |
| 175 | + obj_ = NULL; |
| 176 | + return obj; |
| 177 | + } |
| 178 | + |
| 179 | +private: |
| 180 | + PyObject* obj_; |
| 181 | + |
| 182 | + // Explicitly disable copy operations |
| 183 | + PySafeObject(const PySafeObject*); // = delete |
| 184 | + PySafeObject& operator=(const PySafeObject&); // = delete |
| 185 | +}; |
| 186 | + |
141 | 187 | static void pyRaiseCVException(const cv::Exception &e)
|
142 | 188 | {
|
143 | 189 | PyObject_SetAttrString(opencv_error, "file", PyString_FromString(e.file.c_str()));
|
@@ -290,6 +336,74 @@ bool parseNumpyScalar(PyObject* obj, T& value)
|
290 | 336 | return false;
|
291 | 337 | }
|
292 | 338 |
|
| 339 | +TLSData<std::vector<std::string> > conversionErrorsTLS; |
| 340 | + |
| 341 | +inline void pyPrepareArgumentConversionErrorsStorage(std::size_t size) |
| 342 | +{ |
| 343 | + std::vector<std::string>& conversionErrors = conversionErrorsTLS.getRef(); |
| 344 | + conversionErrors.clear(); |
| 345 | + conversionErrors.reserve(size); |
| 346 | +} |
| 347 | + |
| 348 | +void pyRaiseCVOverloadException(const std::string& functionName) |
| 349 | +{ |
| 350 | + const std::vector<std::string>& conversionErrors = conversionErrorsTLS.getRef(); |
| 351 | + const std::size_t conversionErrorsCount = conversionErrors.size(); |
| 352 | + if (conversionErrorsCount > 0) |
| 353 | + { |
| 354 | + // In modern std libraries small string optimization is used = no dynamic memory allocations, |
| 355 | + // but it can be applied only for string with length < 18 symbols (in GCC) |
| 356 | + const std::string bullet = "\n - "; |
| 357 | + |
| 358 | + // Estimate required buffer size - save dynamic memory allocations = faster |
| 359 | + std::size_t requiredBufferSize = bullet.size() * conversionErrorsCount; |
| 360 | + for (std::size_t i = 0; i < conversionErrorsCount; ++i) |
| 361 | + { |
| 362 | + requiredBufferSize += conversionErrors[i].size(); |
| 363 | + } |
| 364 | + |
| 365 | + // Only string concatenation is required so std::string is way faster than |
| 366 | + // std::ostringstream |
| 367 | + std::string errorMessage("Overload resolution failed:"); |
| 368 | + errorMessage.reserve(errorMessage.size() + requiredBufferSize); |
| 369 | + for (std::size_t i = 0; i < conversionErrorsCount; ++i) |
| 370 | + { |
| 371 | + errorMessage += bullet; |
| 372 | + errorMessage += conversionErrors[i]; |
| 373 | + } |
| 374 | + cv::Exception exception(CV_StsBadArg, errorMessage, functionName, "", -1); |
| 375 | + pyRaiseCVException(exception); |
| 376 | + } |
| 377 | + else |
| 378 | + { |
| 379 | + cv::Exception exception(CV_StsInternal, "Overload resolution failed, but no errors reported", |
| 380 | + functionName, "", -1); |
| 381 | + pyRaiseCVException(exception); |
| 382 | + } |
| 383 | +} |
| 384 | + |
| 385 | +void pyPopulateArgumentConversionErrors() |
| 386 | +{ |
| 387 | + if (PyErr_Occurred()) |
| 388 | + { |
| 389 | + PySafeObject exception_type; |
| 390 | + PySafeObject exception_value; |
| 391 | + PySafeObject exception_traceback; |
| 392 | + PyErr_Fetch(exception_type, exception_value, exception_traceback); |
| 393 | + PyErr_NormalizeException(exception_type, exception_value, |
| 394 | + exception_traceback); |
| 395 | + |
| 396 | + PySafeObject exception_message(PyObject_Str(exception_value)); |
| 397 | + std::string message; |
| 398 | + getUnicodeString(exception_message, message); |
| 399 | +#ifdef CV_CXX11 |
| 400 | + conversionErrorsTLS.getRef().push_back(std::move(message)); |
| 401 | +#else |
| 402 | + conversionErrorsTLS.getRef().push_back(message); |
| 403 | +#endif |
| 404 | + } |
| 405 | +} |
| 406 | + |
293 | 407 | } // namespace
|
294 | 408 |
|
295 | 409 | typedef std::vector<uchar> vector_uchar;
|
|
0 commit comments