{"action":"created","issue":{"url":"https://api.github.com/repos/swig/swig/issues/764","repository_url":"https://api.github.com/repos/swig/swig","labels_url":"https://api.github.com/repos/swig/swig/issues/764/labels{/name}","comments_url":"https://api.github.com/repos/swig/swig/issues/764/comments","events_url":"https://api.github.com/repos/swig/swig/issues/764/events","html_url":"https://github.com/swig/swig/issues/764","id":168874572,"number":764,"title":"Ref counting trouble with methods returning references to the object itself ","user":{"login":"vadz","id":146917,"avatar_url":"https://avatars.githubusercontent.com/u/146917?v=3","gravatar_id":"","url":"https://api.github.com/users/vadz","html_url":"https://github.com/vadz","followers_url":"https://api.github.com/users/vadz/followers","following_url":"https://api.github.com/users/vadz/following{/other_user}","gists_url":"https://api.github.com/users/vadz/gists{/gist_id}","starred_url":"https://api.github.com/users/vadz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vadz/subscriptions","organizations_url":"https://api.github.com/users/vadz/orgs","repos_url":"https://api.github.com/users/vadz/repos","events_url":"https://api.github.com/users/vadz/events{/privacy}","received_events_url":"https://api.github.com/users/vadz/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/swig/swig/labels/Python","name":"Python","color":"d4c5f9"}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":1,"created_at":"2016-08-02T12:37:21Z","updated_at":"2016-08-02T15:12:22Z","closed_at":null,"body":"It's relatively common to have C++ methods modifying the object and returning a reference to the object itself to allow chaining the calls to them, but this doesn't work in Python if you assign the result of the method call to the same object because Python somehow frees the original object before performing the assignment. I don't really understand if it's a SWIG problem or not, but it's definitely nasty (albeit rare), so I wanted to open a ticket about it to at least have an URL to link to when explaining it to people.\r\n\r\nAn example to make things clearer:\r\n```cpp\r\n%module rs\r\n\r\n%{\r\n#include <stdio.h>\r\n%}\r\n\r\n%inline %{\r\nstruct Foo {\r\n    explicit Foo(int n) : n(n) { printf(\"Creating Foo(%p)\\n\", this); }\r\n    ~Foo() { printf(\"Destroying Foo(%p)\\n\", this); }\r\n\r\n    Foo& Inc() { ++n; return *this; }\r\n    int n;\r\n};\r\n%}\r\n```\r\n\r\nNow build and test it:\r\n```\r\n% swig -python  -c++ -o wrap_rs_python.cpp rs.i\r\n% g++ -g -shared -fpic -o _rs.so -I/usr/include/python2.7  wrap_rs_python.cpp\r\n% python -c 'from rs import Foo; f=Foo(1); print \"before:\", f.n; f=f.Inc(); print \"after:\", f.n'\r\nCreating Foo(0x25159f0)\r\nbefore: 1\r\nDestroying Foo(0x25159f0)\r\nafter: 38885968\r\n```\r\n\r\nAs you can see, the object is destroyed before being used and, unsurprisingly, its fields have junk in them, resulting in a lot of fun.\r\n\r\nUnfortunately I really have no idea how to fix this, we can't `Py_INCREF()` the returned value as it would result in a memory leak, but how else can we prevent Python from freeing it?"},"comment":{"url":"https://api.github.com/repos/swig/swig/issues/comments/236935828","html_url":"https://github.com/swig/swig/issues/764#issuecomment-236935828","issue_url":"https://api.github.com/repos/swig/swig/issues/764","id":236935828,"user":{"login":"vadz","id":146917,"avatar_url":"https://avatars.githubusercontent.com/u/146917?v=3","gravatar_id":"","url":"https://api.github.com/users/vadz","html_url":"https://github.com/vadz","followers_url":"https://api.github.com/users/vadz/followers","following_url":"https://api.github.com/users/vadz/following{/other_user}","gists_url":"https://api.github.com/users/vadz/gists{/gist_id}","starred_url":"https://api.github.com/users/vadz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vadz/subscriptions","organizations_url":"https://api.github.com/users/vadz/orgs","repos_url":"https://api.github.com/users/vadz/repos","events_url":"https://api.github.com/users/vadz/events{/privacy}","received_events_url":"https://api.github.com/users/vadz/received_events","type":"User","site_admin":false},"created_at":"2016-08-02T15:12:22Z","updated_at":"2016-08-02T15:12:22Z","body":"After debugging this further, I understand better what's going on here: in case of assignment to `f`, as above, Python decrements ref count of the existing `f` object first (i.e. does the equivalent of `del f`) before rebinding it as the new value. The Python object representing the RHS (i.e. `f.Inc()`) is valid, but corresponds to the same C++ object as the (old) `f` and deleting the latter deletes the C++ object too, leaving the new `f` with a dangling pointer.\r\n\r\nThe only possibility to prevent this from happening would seem to be to use our own ref counting for C++ pointers stored in `SwigPyObject`, but this will be (a) complicated, especially because this stuff is not obvious and doesn't seem to be documented anywhere (e.g. contrary to what the Python chapter of the manual says, `ptr` pointer is sometimes, but not always, a pointer to the C++ object even without `-builtin`, but can also be a pointer to SWIG dict which contains the real pointer under its `\"this\"` key...) and (b) add additional ref counting overhead for something occurring very rarely.\r\n\r\nSo it's probably not worth doing it... But OTOH at least detecting such C++ pointer aliasing from two different Python objects would be very nice and this could be done at typemaps level. Do you think it would be useful to do this?"}}
{"actor":{"display_login":"vadz"}}