2013-05-17 31 views
13

私はPythonクラスのメソッドをC++から呼び出そうとしています。これが呼び出されるC++メソッドは、C++コールバックです。C++(またはC)コールバックからpythonメソッドを呼び出す

私がpythonメソッドを呼び出そうとしているとき、このメソッド内では、segmentation faultを与えていました。

私はPlxMsgWrapperがコールバックで使用されるPythonの方法は、ある

// (pFunc is global variable of type PyObject*) 
pFunc = PyDict_GetItemString(pDict, "PlxMsgWrapper"); 

ようなグローバル変数にPython関数のインスタンスが保存されています。その与えセグメンテーションフォールトこのラインで

PyObject * pInstance = PyObject_CallObject(pFunc, args); 

を作成する場合は、コールバックで

は、引数は

PyObject* args = PyTuple_Pack(2, PyString_FromString(header.c_str()), 
           PyString_FromString(payload.c_str())); 

として作成されます。この後、実際のP​​ythonの方法は、あなたは問題がPythonは あなたのファイルを見つけていないことと思われる場合、Pythonは、しかし、それはから実行 されているディレクトリにモジュールを探してください

PyObject* recv_msg_func = PyObject_GetAttrString(module, (char *)"recvCallback"); 
args = PyTuple_Pack(1, pInstance); 
PyObject_CallObject(recv_msg_func, args); 
+0

私は正しく解明するために最善を尽くしました。何か問題があればコメントしてください。 – Chaitanya

+0

PyObject_GetAttrStringが実際に何かを返すかチェックしましたか?何らかの理由で検索が失敗した可能性があります。 「モジュール」が正しく初期化されていない可能性がありますか? – djf

+0

@djf実際には、コントロールそのものがその時点まで来ていません。 'PyObject_CallObject(pFunc、args)'を実行します。このメソッド自体を呼び出す際にクラッシュします。モジュールの問題を取り除くために、私はコールバックにその問題をロードしていません。 – Chaitanya

答えて

27

。あなたがPythonの関数オブジェクトをオフに保存するときにまず、あなたが参照カウントをインクリメントする必要があります。

Py_INCREF(pFunc) 

それ以外のPythonはオブジェクト参照上に保持されないアイデアを持っていない、それはゴミセグメンテーションが得られ、それを収集することがありますあなたがコールバックからそれを使用しようとすると、エラーが発生します。

次に、C/C++コールバックが呼び出されたときに、どのスレッドが実行されているかを心配する必要があります。他のPythonで作成された別のスレッド(つまり、ソケット上のデータを受け取っているC/C++スレッド)から呼び戻される場合、は、PythonのAPI(Global Interpreter Lock)を取得する前にに取得してください。それ以外の場合、プログラムの動作は未定義です。

// Make sure the GIL has been created since we need to acquire it in our 
// callback to safely call into the python application. 
if (! PyEval_ThreadsInitialized()) { 
    PyEval_InitThreads(); 
} 

そうでない場合は、クラッシュや奇妙な行動が可能性があります

void callback() { 
    PyGILState_STATE gstate; 
    gstate = PyGILState_Ensure(); 

    // Get args, etc. 

    // Call your Python function object 
    PyObject * pInstance = PyObject_CallObject(pFunc, args); 

    // Do any other needed Python API operations 

    // Release the thread. No Python API allowed beyond this point. 
    PyGILState_Release(gstate); 
} 

また、あなたの拡張モジュールのinit関数の中で、あなたは正しく初期化されたスレッドを確保するために、次の操作を行う必要があります:GILを取得するには、あなたがありません非PythonスレッドからGILを取得しようとするときに起こります。

詳細はNon-Python Created Threadsを参照してください。

+1

ありがとうございました。 :)それは正確な問題でした。 – Chaitanya

+0

ありがとうございます - 私は間違ったスレッドであるかもしれないとは考えていませんでした。 – eddiewould

2

と呼ばれています、あなたはあなたのプログラム内 モジュール検索パスにコンピュータ上で任意のディレクトリを追加することができます:あなたはC/C++コールバックからPython関数を呼び出している場合あなたがする必要があるいくつかのものがあります

// Initialize the Python Interpreter 
Py_Initialize(); 

// The following two lines to the trick: 
// add path to your module to python's search paths 
PyRun_SimpleString("import sys"); 
PyRun_SimpleString("sys.path.append(\"/path/to/python/module/here\")"); 

// Build the name object 
pName = PyString_FromString("your_module"); 

// Load the module object 
pModule = PyImport_Import(pName); 

// pDict is a borrowed reference 
pDict = PyModule_GetDict(pModule); 

// pFunc is also a borrowed reference 
pFunc = PyDict_GetItemString(pDict, "PlxMsgWrapper"); 

pArgs = ... 

if (PyCallable_Check(pFunc)) 
{ 
    PyObject_CallObject(pFunc, pArgs); 
} else { 
    PyErr_Print(); 
} 
2

これはあなたの質問に正確には答えませんが、コードを大幅に簡略化して参照カウントの問題を避けることができます。Boost::Python

#include "boost/python.hpp" 

using namespace boost::python; 

int main() 
{ 
    Py_Initialize(); 

    object pyFunPlxMsgWrapper = import("your_module").attr("PlxMsgWrapper"); 
    pyFunPlxMsgWrapper(2, "string", "data"); 
    return 0; 
} 
関連する問題