Reputation: 65
I have some python functions which I run from C++ side and I need them to return std::expected objects. What I did is this:
def do_operation(self):
print("Operation done")
return void_expected()
In Swig code I have something like:
%inline %{
std::expected<void, OperationError> void_expected()
{
return std::expected<void, OperationError>{};
}
%}
%typemap(out) std::expected<void, OperationError> {
if (!$1.has_value()) {
SWIG_exception(SWIG_RuntimeError, "Unexpected error!");
}
Py_RETURN_NONE;
}
But I am getting all these warnings running the python script:
swig/python detected a memory leak of type 'std::expected< void,OperationError > *', no destructor found.
Upvotes: 1
Views: 410
Reputation: 88711
The error message you're seeing hints towards what's going on. Although my older answer on that warning does apply here it shouldn't and that hints as to what's gone wrong. Your expected behaviour is that for the case where a value is returned in C++ None
is return in Python. Conversely if there isn't a value in the expected object then a Python exception is raised. In neither case are you expecting SWIG to create a Proxy object on the Python side for the std::expected
result.
So the fact that you've seen this warning indicate that something's not as expected in your interface as written. And it turns out to be fairly simple - the typemap you showed comes after the place where it's expected to be used. So it doesn't get applied.
We can confirm this by constructing a small, complete example:
%module test
%include <exception.i>
%{
#include "expected.h" // TODO: include <expected> insteadwhen I really have C++23 impl
%}
%typemap(out) std::expected<void, OperationError> {
if (!$1.has_value()) {
SWIG_exception(SWIG_RuntimeError, "Unexpected error!");
}
Py_RETURN_NONE;
}
%inline %{
class OperationError {};
std::expected<void, OperationError> void_expected() {
return std::expected<void, OperationError>{};
}
%}
(I had to make my own expected.h since I don't have a C++23 compiler to hand!)
// Minimal parts needed to prove the point with this answer
namespace std {
template <typename T, typename E>
struct expected {
bool has_value() const { return true; }
};
}
And some Python to prove it:
import test
test.void_expected()
Which we can then build and run like this:
swig3.0 -c++ -python -py3 test.i
g++ -shared -Wall -Wextra test_wrap.cxx -I/usr/include/python3.7 -m32 -o _test.so
python3 run.py
This runs with no error. Swap the order of the typemap and the %inline
with void_expected()
declaration/definition around however as in your question and you'll see the exact same error since the typemap isn't getting applied.
Upvotes: 1