2
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【C/C++】 C/C++で計算した値をpythonの関数に渡して処理を実行させ,さらにその値をC/C++で使う

Last updated at Posted at 2020-04-23

この記事の要約

  • C/C++のソース内で計算した値をpythonの関数に受け渡し,その実行結果を更にC/C++のソース内で受け取る

この記事でやること

  • C/C++で適当な値を作る
  • その値をpythonの関数でプリントする
  • pythonの関数もまた適当な値を返す
  • C/C++側でもpythonの関数が返した値をプリントする

(プログラム内で使われている変数の値に意味はありません)

ソースコード

C/C++

main.cpp
#include <iostream>
#include <vector>
#include <Python.h>

bool isCallable(const char *functionName, PyObject *pythonFunc) {
    if (pythonFunc && PyCallable_Check(pythonFunc)) {
        return true;
    } else {
        std::cerr << "Failed to load the function [" << functionName << "]." << std::endl;
        return false;
    }
}

int main(int argc, char *argv[]) {
    const char *pythonFileName = "script"; // The extension (.py) must be removed.
    const char *functionName = "test_func"; // This function must be included in the script.

    Py_Initialize();
    PyObject *sys = PyImport_ImportModule("sys");
    PyObject *path = PyObject_GetAttrString(sys, "path");
    PyList_Append(path, PyString_FromString(".")); // Add the current directory to the python path list since the script file is in there.
    PyRun_SimpleString("import os, sys\n");

    // load the function from the script
    PyObject *pythonName = PyString_FromString(pythonFileName);
    PyObject *pythonModule = PyImport_Import(pythonName);
    Py_DECREF(pythonName);
    PyObject *pythonFunc = PyObject_GetAttrString(pythonModule, functionName);
    if (!isCallable(functionName, pythonFunc))
        exit(-1);


    // Create data which will be passed to the python's function.
    // Because the numbers of the argument is 2, the python's function must receive 2 arguments (see the script file).
    int pythonFuncArgNum = 2;
    PyObject *pythonArgs = PyTuple_New(pythonFuncArgNum);


    // Create the first argument.
    int dataNum = 3;
    int channelNum = 2;
    int measurementNum = 4;
    PyObject *params = PyList_New(3);
    PyList_SET_ITEM(params, 0, PyInt_FromLong(dataNum));
    PyList_SET_ITEM(params, 1, PyInt_FromLong(channelNum));
    PyList_SET_ITEM(params, 2, PyInt_FromLong(measurementNum));
    PyTuple_SetItem(pythonArgs, 0, params); // Set the first argument.


    // Create the second argument.
    PyObject *datas = PyTuple_New(dataNum);
    for (int i = 0; i < dataNum; i++) {
        PyObject *data = PyTuple_New(channelNum);
        PyObject *channel0 = PyList_New(measurementNum);
        PyObject *channel1 = PyList_New(measurementNum);
        for (int j = 0; j < measurementNum; j++) {
            PyList_SET_ITEM(channel0, j, PyFloat_FromDouble(i + j * 2.0));
            PyList_SET_ITEM(channel1, j, PyFloat_FromDouble(i * 2.0 + j * 2.0));
        }
        PyTuple_SetItem(data, 0, channel0);
        PyTuple_SetItem(data, 1, channel1);
        PyTuple_SetItem(datas, i, data);
    }
    PyTuple_SetItem(pythonArgs, 1, datas); // Set the second argument.


    // Call the python's function and print the returned values.
    PyObject *pythonValue = PyObject_CallObject(pythonFunc, pythonArgs);
    Py_DECREF(pythonArgs);
    std::cout << "Print the results returned from the python function in the C++ source code." << std::endl;
    for (int i = 0; i < (int)PyList_Size(pythonValue); i++)
        std::cout << i << " , " << PyFloat_AsDouble(PyList_GetItem(pythonValue, i)) << std::endl;


    return 0;
}

python

script.py
#! /usr/bin/env python
# -*- coding: utf-8 -*-

import sys
import numpy as np

print 'Scripts written in outside of python functions will be called when this script is loaded by the C++ source code.\n'


# The function must have 2 arguments because the C++ source code gives the function 2 arguments.
def test_func(params, datas):
    print 'Start the python function.'

    # Copy the parameters.
    data_num = params[0]
    channel_num = params[1]
    measurement_num = params[2]
    print 'data_num =', data_num, ', channel_num =', channel_num, ', measurement_num =', measurement_num

    # Print the second arguments
    for i in range(data_num):
        for j in range(measurement_num):
            print 'i =', i, ', channel0 =', datas[i][0][j], ', channel1 =', datas[i][1][j]

    # Create a dummy result and return it.
    values = []
    for i in range(data_num):
        values.append(i * 3.0)

    print 'The python function will return an array (vector) data.\n'

    return values

コンパイル

g++ main.cpp -o call_py_func_from_c -L/usr/lib/python2.7 -lpython2.7 -I/usr/include/python2.7

pythonのバージョンやパスは適宜変えてください.

追記 (Python3系用)

Python3系で実行する場合は,cpp内ではPyString_FromString(const char *v)ではなく,PyUnicode_FromString(const char *u)を使うようです.またコンパイルは,以下の様に実行します.

g++ main.cpp -o call_py_func_from_c -L/usr/lib/python3.6 -lpython3.6m -I/usr/include/python3.6

-lpython3.6mというように,mが付かないとダメなようです(ハマったのでメモ).

実行結果

C/C++とpythonのファイルを同じディレクトリに置き,そこで実行してください(pythonのパスが設定されてないとscript.pyが見つけられないため).C/C++のソースコード内に,カレントディレクトリをpythonのパスに設定する処理が入っています.

Scripts written in outside of python functions will be called when this script is loaded by the C++ source code.

Start the python function.
data_num = 3 , channel_num = 2 , measurement_num = 4
i = 0 , channel0 = 0.0 , channel1 = 0.0
i = 0 , channel0 = 2.0 , channel1 = 2.0
i = 0 , channel0 = 4.0 , channel1 = 4.0
i = 0 , channel0 = 6.0 , channel1 = 6.0
i = 1 , channel0 = 1.0 , channel1 = 2.0
i = 1 , channel0 = 3.0 , channel1 = 4.0
i = 1 , channel0 = 5.0 , channel1 = 6.0
i = 1 , channel0 = 7.0 , channel1 = 8.0
i = 2 , channel0 = 2.0 , channel1 = 4.0
i = 2 , channel0 = 4.0 , channel1 = 6.0
i = 2 , channel0 = 6.0 , channel1 = 8.0
i = 2 , channel0 = 8.0 , channel1 = 10.0
The python function will return an array (vector) data.

Print the results returned from the python function in the C++ source code.
0 , 0
1 , 3
2 , 6

まとめ

C/C++で計算した値をpythonの関数に渡し,pythonの関数内で行った処理をさらにC/C++に返すためのプログラムを作ってみました.

「pythonで良いライブラリ(深層学習とか)が出ているのはわかるけど,プログラムすべてをpythonで書くのは嫌だ」という人間です(新しいものに追いつけない...).なので,基本的にC/C++で書いて,必要なときだけpythonのライブラリを呼び出せると嬉しいなと思い試してみました.

すでに似たようなページもあると思いますが,自分が忘れないためにも...

2
7
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?