プログラミングなど

2009年8月2日日曜日

C++アプリケーションからPythonのクラスのメンバ関数を呼び出す

C++アプリケーションからPythonの関数を呼び出すの続きで、Python内のクラスと、メンバ関数を呼び出します。

---- コード(Python側) testmodule.py ----

# -*- coding: utf-8 -*-

class TestClass:
  value = 10
  def testmethodplus(self, i, j):
    return i + j + self.value;

---- コード(C++側) ----
// ---- main ----
int main(int argc, char *argv[])
{
 const char* moduleName = "testmodule";
 const char* className = "TestClass";
 char* funcName = "testmethodplus";
 Py_Initialize();

 PyObject* pyModule = NULL;
 PyObject* pyClass = NULL;
 PyObject* pyInstance = NULL;
 PyObject* pyResult = NULL;
 try {
  PyObject* pyModuleName = PyString_FromString(moduleName);
  pyModule = PyImport_Import(pyModuleName);
  Py_DECREF(pyModuleName);
  // module呼び出し失敗時に NULL が返る
  if (NULL == pyModule) {
   throw PythonException();
  }
  // 以下、二つは、"借りた参照"
  PyObject* pyDict = PyModule_GetDict(pyModule);
  pyClass = PyDict_GetItemString(pyDict, className);

  // --- クラスをcall ---

  // 呼び出し不可オブジェクトの場合、0 が返る
  if (0 == PyCallable_Check(pyClass)) {
   // --- エラー処理 ---
   throw PythonException();
  }

  // クラスの実体を取得
  pyInstance = PyObject_CallObject(pyClass, NULL);

  // --- メンバ関数をcall ---

  if (NULL == pyInstance) {
   // --- エラー処理 ---
   throw PythonException();
  }
  pyResult = PyObject_CallMethod(pyInstance, funcName, "(ii)", 2, 5);

  if (NULL == pyResult) {
   // --- エラー処理 ---
   throw PythonException();
  }
  std::cout << "Result: "
   << PyInt_AsLong(pyResult) << std::endl;
 } catch (const PythonException& pyEx) {
  if (PyErr_Occurred()) {
   PyErr_Print();
  }
  std::cout << pyEx.what() << std::endl;
 }
 Py_XDECREF(pyResult);
 Py_XDECREF(pyInstance);
 Py_XDECREF(pyModule);

 Py_Finalize();
 return 0;
}

// End of file

前回のPythonの関数呼び出しのソースを流用していますが、関数呼び出しの部分が、 クラスcall→クラスの実体を取得→メンバ関数call となっている部分が異なります。
また、メンバ関数callで、 PyObject_CallMethod() 関数を使用しているため、引数の指定の仕方が違います。(PyObject_CallMethod()関数だけではなく、ほかにも方法はあります。)

クラスの実体を取得する際に使用した PyObject_CallObject() 関数は、"新たな参照"を返すため、 Py_DECREF() することを忘れないように...。

0 件のコメント:

コメントを投稿