Objective-CでPythonモジュールを作ってみる

こんにちは!
ブレインズコンサルティングのゆるふわ担当の馬目です!

今回はゆるふわな記事として、Objective-CPythonのモジュールを作成してみます。
PythonのモジュールをC/C++で記述できることは、公式ドキュメントでも説明されているのですが、
実はObjective-Cで記述したコードも、モジュールとしてビルドできます。

1. C や C++ による Python の拡張 — Python 3.7.2 ドキュメント

そこで、MacOSAPIPythonから叩いてみる手始めとして、
Metal APIを叩いて、デバイス名を取得するモジュールを作成してみようと思います。

TL;DR

github.com

環境

開発は以下の環境で行いました。

実装

主となる3つのファイルを作成していきます。
詳細はリポジトリを見ていただければ良いかなと思います。

GitHub - brains-consulting/tech_blog_objectivec_sample_module: ブログ記事「Objective-CでPythonモジュールを作ってみる」用のリポジトリ

Metal APIを叩くObjective-Cのコードを書く

device.mとして、以下の内容を記述。

#import <Metal/MTLDevice.h>

NSString* getDeviceName()
{
    NSArray<id<MTLDevice>> *availableDevices = MTLCopyAllDevices();

    for(int i = 0; i < [availableDevices count]; i++)
    {
        NSLog(@"Device Index[%d]: %@", i, [availableDevices objectAtIndex: i].name);
    }
    return [availableDevices objectAtIndex: 0].name;
}

上記のObjective-Cのコードを、Python側から利用できるようにラップする

deviceWrapper.mとして、以下の内容を記述。

#import <Foundation/NSString.h>
#import "Python.h"

extern NSString* getDeviceName();

PyObject* device_getDeviceName(PyObject* self)
{
    NSString* deviceName = getDeviceName();
    return Py_BuildValue("s", [deviceName UTF8String]);
}

static PyMethodDef deviceMethods[] = {
    {"getDeviceName", device_getDeviceName, METH_VARARGS},
    {NULL},
};

static struct PyModuleDef devicemodule =
{
    PyModuleDef_HEAD_INIT,
    "device",      /* name of module */
    "",            /* module documentation, may be NULL */
    -1,            /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */
    deviceMethods
};

PyMODINIT_FUNC PyInit_device()
{
    return PyModule_Create(&devicemodule);
}

setup.pyを書く

setup.pyとして、以下の内容を記述。

"""Setup file."""
import os
from setuptools import setup, Extension


os.environ['LDFLAGS'] = '-framework Foundation'

DEVICE_MODULE = Extension(
    'pymetal.device',
    sources=[
        'pymetal/src/device.m',
        'pymetal/deviceWrapper.m'
    ]
)

setup(
    name='pymetal',
    author='Yukinori Manome',
    package_dir={
        'pymetal': 'pymetal',
    },
    packages=[
        'pymetal',
    ],
    ext_modules=[DEVICE_MODULE],
    tests_require=['nose'],
    test_suite='nose.collector',
    platforms=['Mac OSX']
)

実行

できたらpython setup.py buildでビルドしてみましょう。
エラーが出なければ成功です!
そのまま使いたければpython setup.py installでインストールできます。
使い方はリポジトリ内にexampleとしてipynbを用意しているので、そちらを御覧ください。

tech_blog_objectivec_sample_module/sample_usage.ipynb at master · brains-consulting/tech_blog_objectivec_sample_module · GitHub

まとめ

  • Objective-Cでコードを書く
  • Python.hをインポートして、上記のコードをラップしたコードを書く
  • C/C++の場合と同じ様にsetup.pyを書く

終わりに

楽しんでいただけたでしょうか。
これでMacOSAPIPythonから利用できるようになりましたので、
MacOS用のGPUを用いた数値計算ライブラリなんかを作っていけたら良いなぁと思っています。

以上、ゆるふわ担当の馬目でした!