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() することを忘れないように...。
プログラミングなど
2009年8月2日日曜日
2009年8月1日土曜日
C++アプリケーションからPythonの関数を呼び出す
ここのソースをちょこっと改造しただけ。
---- コード(Python側) testmodule.py ----
# -*- coding: utf-8 -*-
def testplus(i, j):
return i + j + 3
---- コード(C++側) ----
#include <Python.h>
#include <iostream>
#include <string>
#include <sstream>
#define VARNAME(var) #var
// ---- exception ----
class PythonException
{
protected:
const char* message_;
const char* varName_;
public:
static const char* errorMessge_ImportModule;
static const char* errorMessage_GetObject;
static const char* errorMessage_CallFunction;
static const char* errorMessage_CreateTuple;
PythonException(const char* message, const char* varName)
: message_(message), varName_(varName) {}
virtual ~PythonException() throw() {}
virtual std::string what() const throw() {
std::stringstream ss;
ss << message_ << " (" << varName_ << ")";
return ss.str();
}
};
const char* PythonException::errorMessge_ImportModule
= "Module import error.";
const char* PythonException::errorMessage_GetObject
= "Get object error.";
const char* PythonException::errorMessage_CallFunction
= "Call function error.";
const char* PythonException::errorMessage_CreateTuple
= "Create tuple error.";
// ---- main ----
int main(int argc, char *argv[])
{
const char* moduleName = "testmodule";
const char* funcName = "testplus";
Py_Initialize();
PyObject* pyModule = NULL;
PyObject* pyFunc = NULL;
PyObject* pyArgs = NULL;
PyObject* pyArg1 = NULL;
PyObject* pyArg2 = NULL;
PyObject* pyResult = NULL;
try {
PyObject* pyModuleName = PyString_FromString(moduleName);
pyModule = PyImport_Import(pyModuleName);
Py_DECREF(pyModuleName);
// module呼び出し失敗時に NULL が返る
if (NULL == pyModule) {
throw PythonException(
PythonException::errorMessge_ImportModule,
VARNAME(pyModule));
}
// 以下、二つは、"借りた参照"
PyObject* pyDict = PyModule_GetDict(pyModule);
pyFunc = PyDict_GetItemString(pyDict, funcName);
// PyObject_GetAttrString()の場合、
// "新たな参照"のため、DECREFしないといけない
//PyObject* pyFunc = PyObject_GetAttrString(pyModule, funcName);
// 取得失敗時に NULL が返る(PyModule_GetDict()は失敗しない)
if (NULL == pyFunc) {
throw PythonException(
PythonException::errorMessage_GetObject,
VARNAME(pyFunc));
}
// 呼び出し不可オブジェクトの場合、0 が返る
if (0 == PyCallable_Check(pyFunc)) {
throw PythonException(
PythonException::errorMessage_CallFunction,
VARNAME(pyFunc));
}
// 以下、失敗時には NULL が返る
pyArgs = PyTuple_New(2);
if (NULL == pyArgs) {
throw PythonException(
PythonException::errorMessage_CreateTuple,
VARNAME(pyArgs));
}
PyObject* pyArg1 = PyInt_FromLong(2);
if (NULL == pyArg1) {
throw PythonException(
PythonException::errorMessage_GetObject,
VARNAME(pyArg1));
}
PyTuple_SetItem(pyArgs, 0, pyArg1);
PyObject* pyArg2 = PyInt_FromLong(5);
if (NULL == pyArg2) {
throw PythonException(
PythonException::errorMessage_GetObject,
VARNAME(pyArg2));
}
PyTuple_SetItem(pyArgs, 1, pyArg2);
pyResult = PyObject_CallObject(pyFunc, pyArgs);
if (NULL == pyResult) {
throw PythonException(
PythonException::errorMessage_GetObject,
VARNAME(pyResult));
}
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(pyArg2);
Py_XDECREF(pyArg1);
Py_XDECREF(pyArgs);
//Py_XDECREF(pyFunc); // PyObject_GetAttrString()の場合
Py_XDECREF(pyModule);
Py_Finalize();
return 0;
}
// End of file
コードは、ほぼ真似なので...(^^; 気をつけなければいけないことは、参照カウンタの操作です。 Python/C API関数を使用した際、戻り値の PyObject* が"新たな参照" の場合、 Py_DECREF() し、"借りた参照" の場合は、Py_DECREF()してはいけません。 これらについては、Python/C API リファレンスマニュアルで、それぞれの関数の戻り値を調べれば、わかるかと思います。
上記の例では、モジュールから、関数を取得する場合に、PyModule_GetDict()と、PyDict_GetItemString()を使用しています。
この二つは、"借りた参照"を返すため、戻り値の PyObject* を Py_DECREF() していません。
しかし、PyObject_GetAttrString() でも関数を取得できますが、こちらは、"新たな参照"を返すため、Py_DECREF() をします。そうしないと、メモリにごみが残ります。
参照カウンタの詳しい説明は、以下などを見ればよいかと思います。
GCアルゴリズム詳細解説
Py_DECREF() には、以下の2種類があります。
Py_DECREF() は、参照カウンタを1デクリメントします。
Py_XDECREF() は、NULLオブジェクトの場合、Py_DECREF()しません。NULLかどうかわからない場合は、こちらを使用したほうがよいでしょう。
---- コード(Python側) testmodule.py ----
# -*- coding: utf-8 -*-
def testplus(i, j):
return i + j + 3
---- コード(C++側) ----
#include <Python.h>
#include <iostream>
#include <string>
#include <sstream>
#define VARNAME(var) #var
// ---- exception ----
class PythonException
{
protected:
const char* message_;
const char* varName_;
public:
static const char* errorMessge_ImportModule;
static const char* errorMessage_GetObject;
static const char* errorMessage_CallFunction;
static const char* errorMessage_CreateTuple;
PythonException(const char* message, const char* varName)
: message_(message), varName_(varName) {}
virtual ~PythonException() throw() {}
virtual std::string what() const throw() {
std::stringstream ss;
ss << message_ << " (" << varName_ << ")";
return ss.str();
}
};
const char* PythonException::errorMessge_ImportModule
= "Module import error.";
const char* PythonException::errorMessage_GetObject
= "Get object error.";
const char* PythonException::errorMessage_CallFunction
= "Call function error.";
const char* PythonException::errorMessage_CreateTuple
= "Create tuple error.";
// ---- main ----
int main(int argc, char *argv[])
{
const char* moduleName = "testmodule";
const char* funcName = "testplus";
Py_Initialize();
PyObject* pyModule = NULL;
PyObject* pyFunc = NULL;
PyObject* pyArgs = NULL;
PyObject* pyArg1 = NULL;
PyObject* pyArg2 = NULL;
PyObject* pyResult = NULL;
try {
PyObject* pyModuleName = PyString_FromString(moduleName);
pyModule = PyImport_Import(pyModuleName);
Py_DECREF(pyModuleName);
// module呼び出し失敗時に NULL が返る
if (NULL == pyModule) {
throw PythonException(
PythonException::errorMessge_ImportModule,
VARNAME(pyModule));
}
// 以下、二つは、"借りた参照"
PyObject* pyDict = PyModule_GetDict(pyModule);
pyFunc = PyDict_GetItemString(pyDict, funcName);
// PyObject_GetAttrString()の場合、
// "新たな参照"のため、DECREFしないといけない
//PyObject* pyFunc = PyObject_GetAttrString(pyModule, funcName);
// 取得失敗時に NULL が返る(PyModule_GetDict()は失敗しない)
if (NULL == pyFunc) {
throw PythonException(
PythonException::errorMessage_GetObject,
VARNAME(pyFunc));
}
// 呼び出し不可オブジェクトの場合、0 が返る
if (0 == PyCallable_Check(pyFunc)) {
throw PythonException(
PythonException::errorMessage_CallFunction,
VARNAME(pyFunc));
}
// 以下、失敗時には NULL が返る
pyArgs = PyTuple_New(2);
if (NULL == pyArgs) {
throw PythonException(
PythonException::errorMessage_CreateTuple,
VARNAME(pyArgs));
}
PyObject* pyArg1 = PyInt_FromLong(2);
if (NULL == pyArg1) {
throw PythonException(
PythonException::errorMessage_GetObject,
VARNAME(pyArg1));
}
PyTuple_SetItem(pyArgs, 0, pyArg1);
PyObject* pyArg2 = PyInt_FromLong(5);
if (NULL == pyArg2) {
throw PythonException(
PythonException::errorMessage_GetObject,
VARNAME(pyArg2));
}
PyTuple_SetItem(pyArgs, 1, pyArg2);
pyResult = PyObject_CallObject(pyFunc, pyArgs);
if (NULL == pyResult) {
throw PythonException(
PythonException::errorMessage_GetObject,
VARNAME(pyResult));
}
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(pyArg2);
Py_XDECREF(pyArg1);
Py_XDECREF(pyArgs);
//Py_XDECREF(pyFunc); // PyObject_GetAttrString()の場合
Py_XDECREF(pyModule);
Py_Finalize();
return 0;
}
// End of file
上記の例では、モジュールから、関数を取得する場合に、PyModule_GetDict()と、PyDict_GetItemString()を使用しています。
この二つは、"借りた参照"を返すため、戻り値の PyObject* を Py_DECREF() していません。
しかし、PyObject_GetAttrString() でも関数を取得できますが、こちらは、"新たな参照"を返すため、Py_DECREF() をします。そうしないと、メモリにごみが残ります。
参照カウンタの詳しい説明は、以下などを見ればよいかと思います。
GCアルゴリズム詳細解説
Py_DECREF() には、以下の2種類があります。
Py_DECREF() は、参照カウンタを1デクリメントします。
Py_XDECREF() は、NULLオブジェクトの場合、Py_DECREF()しません。NULLかどうかわからない場合は、こちらを使用したほうがよいでしょう。
2009年5月22日金曜日
C# + Python embedding C#のクラスをPython側で使用する
C#のクラスをIronPython側で使うのは、非常に簡単だ。
(CPythonだと、結構、面倒だったような...)
---- コード(C#側) ----
using System;
using IronPython;
using IronPython.Hosting;
using Microsoft.Scripting;
using Microsoft.Scripting.Hosting;
namespace python_test001
{
class Program
{
public static void Main(string[] args)
{
try
{
ScriptEngine engine = Python.CreateEngine();
ScriptScope scope = engine.CreateScope();
// C#と変数のやり取りをするPythonのファイルを設定
ScriptSource source =
engine.CreateScriptSourceFromFile("test.py");
TestNumber test = new TestNumber();
// Python側の変数をTestDataとする
scope.SetVariable("num", test);
// 実行
source.Execute(scope);
int i = test.Number;
// 実行後のPython側の変数値を取得
// 結果を出力 ==> 15
System.Console.WriteLine(test.Number);
}
catch (Exception ex)
{
System.Console.WriteLine("Error:"
+ ex.Message.ToString());
}
}
}
}
// Python側に渡すクラス
class TestNumber {
int number = 0;
public void Plus(int i) {
number += i;
}
public int GetNumber() {
return this.number;
}
public void SetNumber(int i) {
this.number = i;
}
public int Number {
get {
return this.number;
}
set {
this.number = value;
}
}
}
---- コード(Python側) ----
num.Number = 10
print 'py_number:', num.GetNumber()
#プロパティのgetがprotectedメンバだとかでエラー
#print 'py_number:', num.Number
num.Plus(5)
================================
■参考
貧脚レーサーのサボり日記 - C#でIronPython2.0をホスティングする
Embedding the Dynamic Language Runtime
The IronPython Calculator and the Evaluator
(CPythonだと、結構、面倒だったような...)
---- コード(C#側) ----
using System;
using IronPython;
using IronPython.Hosting;
using Microsoft.Scripting;
using Microsoft.Scripting.Hosting;
namespace python_test001
{
class Program
{
public static void Main(string[] args)
{
try
{
ScriptEngine engine = Python.CreateEngine();
ScriptScope scope = engine.CreateScope();
// C#と変数のやり取りをするPythonのファイルを設定
ScriptSource source =
engine.CreateScriptSourceFromFile("test.py");
TestNumber test = new TestNumber();
// Python側の変数をTestDataとする
scope.SetVariable("num", test);
// 実行
source.Execute(scope);
int i = test.Number;
// 実行後のPython側の変数値を取得
// 結果を出力 ==> 15
System.Console.WriteLine(test.Number);
}
catch (Exception ex)
{
System.Console.WriteLine("Error:"
+ ex.Message.ToString());
}
}
}
}
// Python側に渡すクラス
class TestNumber {
int number = 0;
public void Plus(int i) {
number += i;
}
public int GetNumber() {
return this.number;
}
public void SetNumber(int i) {
this.number = i;
}
public int Number {
get {
return this.number;
}
set {
this.number = value;
}
}
}
---- コード(Python側) ----
num.Number = 10
print 'py_number:', num.GetNumber()
#プロパティのgetがprotectedメンバだとかでエラー
#print 'py_number:', num.Number
num.Plus(5)
================================
■参考
貧脚レーサーのサボり日記 - C#でIronPython2.0をホスティングする
Embedding the Dynamic Language Runtime
The IronPython Calculator and the Evaluator
2009年5月17日日曜日
C# + Python embedding ファイルの変数のやりとり
Pythonファイルとのやりとりの例
■.NET Framework2.0 sp1
■IronPython 2.0
---- コード(Python側) ----
result = param1 + param2
---- コード(C#側) ----
using System;
using IronPython;
using IronPython.Hosting;
using Microsoft.Scripting;
using Microsoft.Scripting.Hosting;
...
public static void Main(string[] args)
{
try
{
ScriptEngine engine = Python.CreateEngine();
ScriptScope scope = engine.CreateScope();
// C#と変数のやり取りをするPythonのファイルを設定
ScriptSource source =
engine.CreateScriptSourceFromFile("test.py");
// Python側の変数に値を設定
scope.SetVariable("param1", 1);
scope.SetVariable("param2", 10);
// 実行
source.Execute(scope);
// 実行後のPython側の変数値を取得
// 結果を出力 ==> 1
object arg1 = scope.GetVariable("param1");
System.Console.WriteLine(arg1);
// 結果を出力 ==> 10
object arg2 = scope.GetVariable("param2");
System.Console.WriteLine(arg2);
// 結果を出力 ==> 11
object res = scope.GetVariable("result");
System.Console.WriteLine(res);
}
catch (Exception ex)
{
System.Console.WriteLine("Error:"
+ ex.Message.ToString());
}
}
================================
C#で Pythonファイル(test.py)内の変数の値の設定、取得を行います。
Python側では、あらかじめ変数に値が設定されている必要はありません。
C#で Python側の変数param1, param2 に、それぞれ値を設定し、Python側が実行された後、ython側の計算結果のresult変数をC#で取得しています。
■.NET Framework2.0 sp1
■IronPython 2.0
---- コード(Python側) ----
result = param1 + param2
---- コード(C#側) ----
using System;
using IronPython;
using IronPython.Hosting;
using Microsoft.Scripting;
using Microsoft.Scripting.Hosting;
...
public static void Main(string[] args)
{
try
{
ScriptEngine engine = Python.CreateEngine();
ScriptScope scope = engine.CreateScope();
// C#と変数のやり取りをするPythonのファイルを設定
ScriptSource source =
engine.CreateScriptSourceFromFile("test.py");
// Python側の変数に値を設定
scope.SetVariable("param1", 1);
scope.SetVariable("param2", 10);
// 実行
source.Execute(scope);
// 実行後のPython側の変数値を取得
// 結果を出力 ==> 1
object arg1 = scope.GetVariable("param1");
System.Console.WriteLine(arg1);
// 結果を出力 ==> 10
object arg2 = scope.GetVariable("param2");
System.Console.WriteLine(arg2);
// 結果を出力 ==> 11
object res = scope.GetVariable("result");
System.Console.WriteLine(res);
}
catch (Exception ex)
{
System.Console.WriteLine("Error:"
+ ex.Message.ToString());
}
}
================================
C#で Pythonファイル(test.py)内の変数の値の設定、取得を行います。
Python側では、あらかじめ変数に値が設定されている必要はありません。
C#で Python側の変数param1, param2 に、それぞれ値を設定し、Python側が実行された後、ython側の計算結果のresult変数をC#で取得しています。
C# + Python embedding 簡単な計算
CC#からIronPythonに計算させる簡単な例。
■.NET Framework2.0 sp1で実行
sp1でないと動かない点に注意
■IronPython 2.0
以下からIronPython-2.0.1-Bin.zipをダウンロード
http://www.codeplex.com/IronPython → Downloads
以下を参照設定で追加
IronPython.dll
IronPython.Modules.dll
Microsoft.Scripting.Core.dll
Microsoft.Scripting.dll
Microsoft.Scripting.ExtensionAttribute.dll
---- コード ----
using System;
using IronPython;
using IronPython.Hosting;
using Microsoft.Scripting;
using Microsoft.Scripting.Hosting;
...
public static void Main(string[] args)
{
try
{
ScriptEngine engine = Python.CreateEngine();
ScriptScope scope = engine.CreateScope();
// 1 + 2 を Python に計算させる
ScriptSource source =
engine.CreateScriptSourceFromString(
"1 + 2", SourceCodeKind.Expression);
object res = source.Execute();
// 結果を出力 ==> 3
System.Console.WriteLine(res);
}
catch (Exception ex)
{
System.Console.WriteLine("Error:" +
ex.Message.ToString());
}
}
■.NET Framework2.0 sp1で実行
sp1でないと動かない点に注意
■IronPython 2.0
以下からIronPython-2.0.1-Bin.zipをダウンロード
http://www.codeplex.com/IronPython → Downloads
以下を参照設定で追加
IronPython.dll
IronPython.Modules.dll
Microsoft.Scripting.Core.dll
Microsoft.Scripting.dll
Microsoft.Scripting.ExtensionAttribute.dll
---- コード ----
using System;
using IronPython;
using IronPython.Hosting;
using Microsoft.Scripting;
using Microsoft.Scripting.Hosting;
...
public static void Main(string[] args)
{
try
{
ScriptEngine engine = Python.CreateEngine();
ScriptScope scope = engine.CreateScope();
// 1 + 2 を Python に計算させる
ScriptSource source =
engine.CreateScriptSourceFromString(
"1 + 2", SourceCodeKind.Expression);
object res = source.Execute();
// 結果を出力 ==> 3
System.Console.WriteLine(res);
}
catch (Exception ex)
{
System.Console.WriteLine("Error:" +
ex.Message.ToString());
}
}
登録:
投稿 (Atom)