Python Properties & Swig

后端 未结 6 889
日久生厌
日久生厌 2021-01-30 09:24

I am attempting to create python bindings for some C++ code using swig. I seem have run into a problem trying to create python properties from some accessor functions I have for

6条回答
  •  伪装坚强ぢ
    2021-01-30 10:24

    There is an easy way to make python properties from methods with swig.
    Suppose C++ code Example.h:

    C++ header

    class Example{
        public:
          void SetX(int x);
          int  GetX() const;
        };
    

    Lets convert this setter and getter to python propery 'x'. The trick is in .i file. We add some "swiggy" inline python code (with %pythoncode) that is inserted in a body of a resulting python class (in the auto-generated python code).

    Swig wrapping Example.i

    %module example
    %{
         #include "example.h"
    %}
    
    class Example{
        public:
          void SetX(int x);
          int  GetX() const;
    
          %pythoncode %{
             __swig_getmethods__["x"] = GetX
             __swig_setmethods__["x"] = SetX
             if _newclass: x = property(GetX, SetX)
          %}
        };
    

    Check the python code:

    python test code

    import example
    
    test = example.Example()
    test.x = 5
    print "Ha ha ha! It works! X = ", repr(test.x)
    

    That is it!



    Make it simplier!

    There is no need to rewrite a class definition. Thanks to Joshua advice, one could use SWIG directive %extend ClassName { }.

    Swig wrapping Example.i

    %module example
    %{
         #include "example.h"
    %}
    
    %extend Example{
          %pythoncode %{
             __swig_getmethods__["x"] = GetX
             __swig_setmethods__["x"] = SetX
             if _newclass: x = property(GetX, SetX)
          %}
        };
    

    Hiding setter and getter functions

    As one may see, test.GetX() and test.SetX() are still in place after conversion. One can hide them by:

    a) Rename functions by using %rename, add '_' in the beginning thus making methods "private" for python. In the a SWIG interface .i Example.i

    ...
    class Example{
       %rename(_SetX) SetX(int);
       %rename(_GetX) GetX();
    ...
    

    (%rename may be placed in some separated place to save the possibility to convert this class to other languages, which don't need these '_')

    b) or one can play with %feature("shadow")

    Why is it so?

    Why do we have to use such things to convert methods to a property by using SWIG? As it was said, SWIG selfishly overrides _setattr_, so one have to use _swig_getmethods_ and _swig_setmethods_ to register functions and stay in the swig way.

    Why may one prefer this way?

    The methods listed above, especially with PropertyVoodoo are... It is like burning the house to fry an egg. Also it breaks the classes layout, as one have to create inherited classes to make python properties from C++ methods. I mean if class Cow returns class Milk and the inherited class is MilkWithProperties(Milk), how to make Cow to produce MilkWithProperties?

    This approach allows one to:

    1. explicitly control what C++ methods to convert to python properties
    2. conversion rules are located in swig interface(*.i) files, the place where they are supposed to be
    3. one resulting autogenerated .py file
    4. stay in the swig syntax of insertions in swig generated .py file
    5. %pythoncode is ignored if one wraps library to other languages

    Update In a newer version SWIG abandoned _swig_property so just use property. It works with old version of swig the same. I've changed the post.

提交回复
热议问题