昆布大好き!

主にプログラミングの技メモ

CとPythonを合わせて使う(その1)

目的

パラメータ設定ファイルをPythonスクリプトで書きたくなったのでやってみま した。

出来るようになること

次のように、数式でパラメータを指定したり、タプル型をパラメータにすることが出来るようになります。

x = 1
tpl1 = (x, 20*x+5, 300*x)
tpl2 = (x+2, 30*x+1, 400*x)

方法

Cコードです。

#include    <iostream>
#include    <Python.h>

void main(int argc, char *argv[])
{
    // Pythonランタイムの初期化
    Py_Initialize();
    // mainモジュールを取得
    PyObject *module = PyImport_AddModule("__main__");
    // スクリプトを実行
    MyPyRunFile("setting.py");

    // Pythonランタイム中の"tpl1"変数を参照
    PyObject *tmp = PyObject_GetAttrString(module, "tpl1");
    int n1, n2, n3;
    // 書式を指定して変数の値を取り出す
    PyArg_Parse(tmp, "(iii)", &n1, &n2, &n3);
    printf("tpl1 = (%d, %d, %d)\n", n1, n2, n3);

    // Pythonランタイムの始末
    Py_Finalize();
}

実行結果

tpl1 = (1, 25, 300)

Cのコードの中でPythonランタイムを起動、 任意のタイミングでスクリプトを実行してパラメータをランタイムに読み込ませます。 読み込ませた値をCコード側から読み取ります。

スクリプトで設定した値は、mainモジュールへの変数に設定されています。 Cコードから参照するには書式指定文字列で型を指定して変数を参照します。

ビルドの際には、インストールしたPythonに合わせてx86かx64かを合わせる必要があります。

少し楽をする

書式指定を省略して楽ができるようにGetTupleクラスを作ります。

#include    <iostream>
#include    <Python.h>

// Pythonタプル型読み取り
class GetTuple {
public :
    static void Initialize();

public :
    // 読み取り関数
    template <class ... Types>
    static void Get(const char *var, Types ... args)
    {
        char fmt[128];
        fmt[0] = '(';
        getPyParseFormat(fmt+1, args ...);
        PyObject *tmp = PyObject_GetAttrString(_mainModule, var);
        PyArg_Parse(tmp, fmt, args ...);
    }

private :
    // Pythonモジュールハンドラ
    static PyObject *_mainModule;

    // パース指定書式を型判定とオーバーロードとテンプレートを駆使して作成
    static void getPyParseFormat(char *sz) {
        *sz = ')';
        *(sz+1) = '\0';
    }

    // 同上
    template <typename T, class ... Types>
    static void getPyParseFormat(char *sz, T type, Types ... args) {
        if ( typeid(*type) == typeid(int) ) {
            *sz = 'i';
        } else if ( typeid(*type) == typeid(float) ){
            *sz = 'f';
        } else if ( typeid(*type) == typeid(wchar_t) ){
            *sz = 'u';
        } else {
            *sz = 's';
        }

        getPyParseFormat(sz+1, args ...);
    }

};

PyObject *GetTuple::_mainModule;

void GetTuple::Initialize()
{
    _mainModule = PyImport_AddModule("__main__");
}

// Pythonスクリプト実行
bool MyPyRunFile(const char *filename)
{
    PyObject *obj = Py_BuildValue("s", filename);
    FILE *file = _Py_fopen_obj(obj, "r+");
    if (file != nullptr) {
        PyRun_SimpleFileEx(file, filename, 1);
        return true;
    }
    return false;
}

void main(int argc, char *argv[])
{
    // Pythonランタイムの初期化
    Py_Initialize();
    // mainモジュールを取得
    PyObject *module = PyImport_AddModule("__main__");
    // スクリプトを実行
    MyPyRunFile("setting.py");

    GetTuple::Initialize();
    // 細工を使って"tpl2"変数の値を取り出す
    GetTuple::Get("tpl2", &n1, &n2, &n3);
    printf("tpl2 = (%d, %d, %d)\n", n1, n2, n3);
    // Pythonランタイムの始末
    Py_Finalize();
}

実行結果

tpl2 = (3, 31, 400)

C++の拡張されたテンプレート機能やオーバーロードなどを多用しています。 args …の部分は、引数で渡ってきた変数がごそっと入ってくる、と思って下さい。