PythonのライブラリをC++で作成する(2)

C++で簡単なPythonのライブラリを作ります。
整数同士を掛け算する関数です。

さっそくコードを示します。

# setup.py
from distutils.core import setup, Extension

setup(name = 'mymath', version = '1.0.0', \
    ext_modules = [Extension('mymath', ['mymath.cpp'])])
// mymath.cpp
#include <Python.h>

static PyObject *mul(PyObject *self, PyObject *args) {
    int a, b;
    if (!PyArg_ParseTuple(args, "ii", &a, &b))
        return NULL;
    
    const int   c = a * b;
    return Py_BuildValue("i", c);
}

static PyMethodDef mymathMethods[] = {
    { "mul", mul, METH_VARARGS },
    { NULL }
};

static struct PyModuleDef mymath = {
    PyModuleDef_HEAD_INIT, "mymath", "Python3 C API Module",
    -1, mymathMethods
};

PyMODINIT_FUNC PyInit_mymath() {
    return PyModule_Create(&mymath);
}

ポイントは型変換です。
Pythonからやってくる値はPyObjectで、返す値もPyObjectなので、PyObjectをintに変換して、計算した結果のintをPyObjectに変換します。

まず、PyObjectをintに変換する部分です。

    int a, b;
    if (!PyArg_ParseTuple(args, "ii", &a, &b))
        return NULL;

タプルなので、PyObjectを一つずつ取り出して、それをintにしてもよいです。

    PyObject    *obj1 = PyTuple_GetItem(args, 0);
    PyObject    *obj2 = PyTuple_GetItem(args, 1);
    if(!PyLong_Check(obj1))
        return Py_None;
    if(!PyLong_Check(obj2))
        return Py_None;
    const int   a = PyLong_AsLong(obj1);
    const int   b = PyLong_AsLong(obj2);

タプルオブジェクト (tuple object) - Python 3.9.1 ドキュメント
整数型オブジェクト (integer object) - Python 3.9.1 ドキュメント

上のように、PyArg_ParseTupleを使うと一度にintを2つ取り出せます。
PyArg_ParseTupleの第2引数がどの型に変換するかを指定しています。"ii"だと2つともintに変換します。

引数の解釈と値の構築 - Python 3.9.1 ドキュメント

実行してみましょう。

$ python
Python 3.7.4 (default, Aug 13 2019, 20:35:49)
[GCC 7.3.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import mymath
>>> mymath.mul(2, 3)
6

次の例だと、

>>> mymath.mul(100000, 100000)
1410065408

32ビット符号付き整数なので、ちゃんとオーバーフローしていますね。

>>> (100000*100000+2**31)%2**32-2**31
1410065408

あってます。