-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Description
Consider the following class in C++. Note that the class is not copy constructable as it contains a managed pointer. The constructor of the function may throw an exception.
#pragma once
#include <iostream>
#include <memory>
class TestClass
{
public:
std::unique_ptr<int> i;
TestClass() : i(new int) {};
TestClass(int i_) : i(new int)
{
if ( i_ == 0 )
throw 0;
*i = i_;
}
};
inline void cpp_function(TestClass t)
{
std::cout << *(t.i) << "\n";
}This exception should be propagated to Cython, which should be possible via except +.
cdef extern from "test.hpp":
cppclass TestClass:
TestClass(int) except +
void cpp_function(TestClass t)
def interface(i):
cpp_function(TestClass(i))Unfortunately, the generated code cannot be compiled. Let’s take a look why. The relevant part is this (shortened) bit:
static PyObject *__pyx_pf_4test_interface(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_i) {
/* ... some more stuff ... */
TestClass __pyx_t_2;
__Pyx_RefNannySetupContext("interface", 0);
/* "test.pyx":8
*
* def interface(i):
* cpp_function(TestClass(i)) # <<<<<<<<<<<<<<
*/
__pyx_t_1 = __Pyx_PyInt_As_int(__pyx_v_i); if (unlikely((__pyx_t_1 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 8, __pyx_L1_error)
try {
__pyx_t_2 = TestClass(__pyx_t_1);
} catch(...) {
__Pyx_CppExn2PyErr();
__PYX_ERR(0, 8, __pyx_L1_error)
}
cpp_function(__pyx_t_2);
/* ... some more stuff ... */
}First of all we notice the line
TestClass __pyx_t_2;which implies that the object is default constructable. If not, the code will not compile. A meaningless default constructor can in most cases be achieved but puts some additional bookkeeping work on the C++ programmer’s shoulders. This is also an issue but can be circumvented.
More crucial is the line
cpp_function(__pyx_t_2);where the object is attempted to be copied. This is not possible as the object is not copyable. The code does not compile, because error: use of deleted function ‘TestClass::TestClass(const TestClass&)’.
With C++11 this situation can be handled though, simply using
cpp_function(std::move(__pyx_t_2));In case that the object can be moved this is done, otherwise it is copied nevertheless.
Please add std::move to function calls.
Addendum:
It would be best to use a pointer for the object to be created in combination with the move. This way it does neither need to be default constructable nor copyable.
static PyObject *__pyx_pf_4test_interface(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_i) {
/* ... some more stuff ... */
std::unique_ptr<TestClass> __pyx_t_2;
__Pyx_RefNannySetupContext("interface", 0);
/* "test.pyx":8
*
* def interface(i):
* cpp_function(TestClass(i)) # <<<<<<<<<<<<<<
*/
__pyx_t_1 = __Pyx_PyInt_As_int(__pyx_v_i); if (unlikely((__pyx_t_1 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 8, __pyx_L1_error)
try {
__pyx_t_2.reset(new TestClass(__pyx_t_1));
} catch(...) {
__Pyx_CppExn2PyErr();
__PYX_ERR(0, 8, __pyx_L1_error)
}
cpp_function(std::move(*__pyx_t_2));
/* ... some more stuff ... */
}