diff --git a/Lib/python/pyopers.swg b/Lib/python/pyopers.swg index 3911ff517..017d09b48 100644 --- a/Lib/python/pyopers.swg +++ b/Lib/python/pyopers.swg @@ -18,16 +18,6 @@ %rename(__or__) *::operator|; %rename(__xor__) *::operator^; %rename(__invert__) *::operator~; -%rename(__iadd__) *::operator+=; -%rename(__isub__) *::operator-=; -%rename(__imul__) *::operator*=; -%rename(__idiv__) *::operator/=; -%rename(__imod__) *::operator%=; -%rename(__ilshift__) *::operator<<=; -%rename(__irshift__) *::operator>>=; -%rename(__iand__) *::operator&=; -%rename(__ior__) *::operator|=; -%rename(__ixor__) *::operator^=; %rename(__lt__) *::operator<; %rename(__le__) *::operator<=; %rename(__gt__) *::operator>; @@ -48,4 +38,60 @@ %ignorewarn("386:operator->* ignored") operator->*; %ignorewarn("389:operator[] ignored (consider using %extend)") operator[]; +/* + Inplace operator declarations. + + They translate the inplace C++ operators (+=, -=, ...) into the + corresponding python equivalents(__iadd__,__isub__), etc, + disabling the ownership of the input 'self' pointer, and assigning + it to the returning object: + + %feature("self:disown") *::Operator; + %feature("new") *::Operator; + + This makes the most common case safe, ie: + + A& A::operator+=(int i) { ...; return *this; } + ^^^^ ^^^^^^ + + will work fine, even when the resulting python object shares the + 'this' pointer with the input one. The input object is usually + deleted after the operation, including the shared 'this' pointer, + producing 'strange' seg faults, as reported by Lucriz + (lucriz@sitilandia.it). + + If you have an interface that already takes care of that, ie, you + already are using inplace operators and you are not getting + seg. faults, with the new scheme you could end with 'free' elements + that never get deleted (maybe, not sure, it depends). But if that is + the case, you could recover the old behaviour using + + %feature("self:disown","") A::operator+=; + %feature("new","") A::operator+=; + + which recovers the old behaviour for the class 'A', or if you are + 100% sure your entire system works fine in the old way, use: + + %feature("self:disown","") *::operator+=; + %feature("new","") *::operator+=; + +*/ + +%define %swig_inplace_oper(Oper, PyOper) +%feature("self:disown") *::Oper; +%feature("new") *::Oper; +%rename(__##PyOper##__) *::Oper; +%enddef + +%swig_inplace_oper(operator +=, iadd); +%swig_inplace_oper(operator -=, isub); +%swig_inplace_oper(operator *=, imul); +%swig_inplace_oper(operator /=, idiv); +%swig_inplace_oper(operator %=, imod); +%swig_inplace_oper(operator &=, iand); +%swig_inplace_oper(operator |=, ior); +%swig_inplace_oper(operator ^=, ixor); +%swig_inplace_oper(operator <<=, ilshift); +%swig_inplace_oper(operator >>=, irshift); + #endif diff --git a/Source/Swig/cwrap.c b/Source/Swig/cwrap.c index ed7edca57..f9a4cfd0d 100644 --- a/Source/Swig/cwrap.c +++ b/Source/Swig/cwrap.c @@ -624,6 +624,32 @@ Swig_MethodToFunction(Node *n, String *classname, int flags) { SwigType_add_pointer(type); p = NewParm(type,"self"); Setattr(p,"hidden","1"); + /* + Disable the 'this' ownership in 'self' to manage inplace + operations like: + + A& A::operator+=(int i) { ...; return *this;} + + Here the 'self' parameter ownership needs to be disabled since + there could be two objects sharing the same 'this' pointer: the + input and the result one. And worse, the pointer could be deleted + in one of the objects (input), leaving the other (output) with + just a seg. fault to happen. + + To avoid the previous problem, use + + %feature("self:disown") *::operator+=; + %feature("new") *::operator+=; + + These two lines just transfer the ownership of the 'this' pointer + from the input to the output wrapping object. + + This happens in python, but may also happens in other target + languages. + */ + if (Getattr(n,"feature:self:disown")) { + Setattr(p,"wrap:disown",Getattr(n,"feature:self:disown")); + } set_nextSibling(p,parms); Delete(type);