2012-07-17 3 views
6

SWIGラッパーとのpythonレベル定義のコールバックを呼び出します。C++バックエンドは、私はPythonのAPIに++ <a href="https://github.com/gtkqq/libwebqq.git" rel="noreferrer">libwebqq</a></p> <p>をC言語で書かれたライブラリをラップしていますブースト機能で定義されているタイプがあり

typedef boost::function<void (std::string)> EventListener; 

"EventListener"変数コールバックを定義できます。

C++レベルでは、アダプタクラスのevent_mapというマップ構造もあります。 event_mapのキータイプはQQEvent enumタイプで、event_mapの値タイプはEvenListenerをラップするクラス "Action"です。

class Action 
{ 

    EventListener _callback; 

    public: 
    Action(){ 
     n_actions++; 
    } 

    Action(const EventListener & cb){ 
    setCallback(cb); 
    } 

    virtual void operator()(std::string data) { 
    _callback(data); 
    } 
    void setCallback(const EventListener & cb){ 
     _callback = cb; 
    } 

    virtual ~Action(){ std::cout<<"Destruct Action"<<std::endl; n_actions --; } 
    static int n_actions; 
}; 


class Adapter{ 

    std::map<QQEvent, Action > event_map; 

public: 
    Adapter(); 
    ~Adapter(); 
    void trigger(const QQEvent &event, const std::string data); 
    void register_event_handler(QQEvent event, EventListener callback); 
    bool is_event_registered(const QQEvent & event); 
    void delete_event_handler(QQEvent event); 
}; 

「register_event_handler」は、関連するイベントにコールバック関数を登録するためのAPIです。 イベントが発生した場合、C++のバックエンドがそれを呼び出します。しかし、私たちはPythonレベルでコールバックを実装する必要があります。ルートを把握するために助けてください

Traceback (most recent call last): 
File "testwrapper.py", line 44, in <module> 
worker = Worker() 
File "testwrapper.py", line 28, in __init__ 
a.setCallback(self.on_message) 
File "/home/devil/linux/libwebqq/wrappers/python/libwebqqpython.py", line 95, in setCallback 
def setCallback(self, *args): return _libwebqqpython.Action_setCallback(self, *args) 
TypeError: in method 'Action_setCallback', argument 2 of type 'EventListener const &' 
Destruct Action 

:そして、私はこの問題は、私はテストのpython scriptにregister_eventを呼び出すとき、型エラーが常に発生し、ある「callback.i」に

をコールバックタイプを包んこのタイプのエラーの原因とこの問題の解決策です。

+0

http://stackoverflow.com/questions/12392703/what-is-the-cleanest-way-to-call-a-python-function-from-c-with-a-swig-wraped – xmduhan

答えて

9

問題は、Pythonの呼び出し可能コードからEventListenerクラスにマップするコードが含まれていないようです。それは無料で提供されるものではありませんが、かなり定期的に出てくるものです。 hereこの回答の一部として参考になった

ご質問は問題には本当に関連して、非常に完了していないいずれかの私はとの問題と解決策を示すために、最小限のヘッダファイルを作成してきたではありませんコードのかなり多くがありますことを考えると

#include <boost/function.hpp> 
#include <string> 
#include <map> 

typedef boost::function<void (std::string)> EventListener; 

enum QQEvent { THING }; 

inline std::map<QQEvent, EventListener>& table() { 
    static std::map<QQEvent, EventListener> map; 
    return map; 
} 

inline const EventListener& register_handler(const QQEvent& e, const EventListener& l) { 
    return table()[e] = l; 
} 

inline void test(const QQEvent& e) { 
    table()[e]("Testing"); 
} 

ヘッダファイルは、単純なラッパーは次のようになります。

import test 

def f(x): 
    print(x) 

test.register_handler(test.THING, f) 
test.test(test.THING) 
:私も一緒に入れ

%module test 

%{ 
#include "test.hh" 
%} 

%include "test.hh" 

のPythonのビットでこれを実行するために、これにより

私はあなたが見るエラーを再現することができます

 
LD_LIBRARY_PATH=. python3.1 run.py 
Traceback (most recent call last): 
    File "run.py", line 6, in 
    test.register_handler(test.THING, f) 
TypeError: in method 'register_handler', argument 2 of type 'EventListener const &' 

がうまくいけば、我々は今、同じページにしています。タイプEventListener(正確には型のためのSWIGの生成されたプロキシ)を期待するregister_handlerの単一のバージョンがあります。関数を呼び出すときにEventListenerを渡すつもりはありません。代わりにPython Callableで、C++側ではあまり知られていません。型の一致や暗黙の変換ができません。だから私たちは私たちのインターフェースにPython型を実際のC++型にマッシュアップするためにいくつかの接着剤を追加する必要があります。

SWIGラッパーコードの内部にのみ存在する(つまり、%{ }%内にある)完全に新しいタイプを定義することで、これを行います。タイプPyCallbackには1つの目的があります:私たちが作業している実際のPythonのことを参照し、C++の関数のように見えるようにすることです。

我々は(誰もが見ることを得るない)ことPyCallback実装の詳細を追加したら、私たちは、直接PyObject*を取り、私たちのためにPyCallback + EventListenerを構築register_handlerための別のオーバーロードを追加する必要があります。これはラッピングの目的にのみ存在するため、%inlineを使用してSWIGインターフェイスファイル内のすべてを宣言し、定義してラップします。したがって、私たちのインターフェイスファイルは次のようになります:

%module test 

%{ 
#include "test.hh" 

class PyCallback 
{ 
    PyObject *func; 
    PyCallback& operator=(const PyCallback&); // Not allowed 
public: 
    PyCallback(const PyCallback& o) : func(o.func) { 
     Py_XINCREF(func); 
    } 
    PyCallback(PyObject *func) : func(func) { 
     Py_XINCREF(this->func); 
     assert(PyCallable_Check(this->func)); 
    } 
    ~PyCallback() { 
     Py_XDECREF(func); 
    } 
    void operator()(const std::string& s) { 
     if (!func || Py_None == func || !PyCallable_Check(func)) 
     return; 
     PyObject *args = Py_BuildValue("(s)", s.c_str()); 
     PyObject *result = PyObject_Call(func,args,0); 
     Py_DECREF(args); 
     Py_XDECREF(result); 
    } 
}; 
%} 

%include "test.hh" 

%inline %{ 
    void register_handler(const QQEvent& e, PyObject *callback) { 
    register_handler(e, PyCallback(callback)); 
    } 
%} 

この時点で、元のテストPythonが正常に動作するようになりました。

register_handlerの元のオーバーロードを非表示にすることができましたが、この例では、C++定義のコールバックを操作することができるので、表示しないままにするのは間違いではありません。また、例えばPython側からそれらを取得/設定し、いくつかの中核要素をグローバル変数として公開することができます。代わりに、私はフリー機能をオーバーロードするために私の例で使用し%inlineの、ライン%include "test.hh"後、Action::setCallbackをオーバーロードする

%extend Action { 
    void setCallback(PyObject *callback) { 
    $self->setCallback(PyCallback(callback)); 
    } 
} 

:あなたがしたいと思うあなたの実際のコードで

最後に、あなたが%renameを使用してActionクラスのoperator()を公開する場合があります、またはあなたがfunction pointer/member function trickを使用してcallback_メンバーを公開することを選んだ可能性があります。

+0

すべての拳あなたの親切なサポートに感謝します。私は、ファイル "libwebqq.i"にPycallbackクラスと "extends Action - > setCallback"を追加し、 "register_event_handler"がAdapterのメソッドであるため、 "inline register_event_handler"を "extend adapter"に変更しました。プロジェクトは正常に構築できます。しかし、C++レベルでpythonで定義されたcallabckを呼び出すと、セグメントの障害が発生します。エラーメッセージは "swig/pythonが 'EventListener *'型のメモリリークを検出しましたが、デストラクタは見つかりませんでした。 – user1530527

+0

プロジェクトlibwebqq(https://github.com/gtkqq/libwebqq) – user1530527

+0

@ user1530527を参照してください - いくつかの問題のように聞こえます。まず、SWIGが型をラップするときに、合法的に 'delete'を呼び出せるだけの十分なクラスの定義があることを確認する必要があります。第二に、コードがマルチスレッドの場合にロック問題が発生する可能性があります。 – Flexo

関連する問題

 関連する問題