Python から C ライブラリを利用 (ctypes)
[History] [Last Modified] (2019/04/28 09:44:37)
ここは
趣味のプログラミングを楽しむための情報共有サービス。記事の一部は有料設定にして公開できます。 詳しくはこちらをクリック📝
Recent posts
Popular pages

概要

FFI (Foreign Function Interface) の一つである ctypes を利用すると、C 言語のライブラリを Python から利用できます。サンプルコードを記載します。

libm の sqrt を利用する例

main.py

#!/usr/bin/python
# -*- coding: utf-8 -*-

from ctypes import CDLL, c_double
from ctypes.util import find_library

def Main():
    libm = CDLL(find_library('m'), use_errno = True)
    libm.sqrt.argtypes = [c_double]
    libm.sqrt.restype = c_double
    print libm.sqrt(121)

if __name__ == '__main__':
    Main()

実行例

$ python main.py
11.0

libc 経由でシステムコールを利用する例

こちらのページに記載したとおり、システムコールは libc 経由で利用できます。例えば、こちらのページに記載した poll を Python から libc 経由で利用する例は以下のようになります。

main.py

#!/usr/bin/python
# -*- coding: utf-8 -*-

from ctypes import CDLL, Structure, POINTER, c_short, c_int
from ctypes.util import find_library

class Pollfd(Structure):
    # $ man 2 poll
    # struct pollfd {
    #     int   fd;         /* file descriptor */
    #     short events;     /* requested events */
    #     short revents;    /* returned events */
    # };
    _fields_ = [("fd", c_int),
                ("events", c_short),
                ("revents", c_short)]
    def _init_(self, fd, events, revents):
        self.fd = fd
        self.events = events
        self.revents = revents

OnePollfdArrayType = Pollfd * 1
POLLIN = 0x0001 # https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/poll.h
POLLERR = 0x0008
POLLHUP = 0x0010
POLLNVAL = 0x0020

def Main():
    libc = CDLL(find_library('c'), use_errno = True)

    # ファイルディスクリプタ 0 を監視
    fds = OnePollfdArrayType(Pollfd())
    fds[0].fd = 0
    fds[0].events = POLLIN 

    # C 言語の型を引数と返り値に関して設定
    # $ man 2 poll
    # int poll(struct pollfd *fds, nfds_t nfds, int timeout);
    libc.poll.argtypes = [POINTER(Pollfd), c_int, c_int]
    libc.poll.restype = c_int

    while True:
        n = libc.poll(fds, 1, 5000)
        if n < 0:
            raise Exception("poll")
        elif n == 0:
            print "no input"
        else:
            for ifd in range(1):
                if fds[ifd].revents & (POLLERR | POLLHUP | POLLNVAL):
                    raise Exception("poll %d" % ifd)
                elif fds[ifd].revents & POLLIN:
                    print "input from fd%d: %s" % (ifd, raw_input())

if __name__ == '__main__':
    Main()

実行例

$ python main.py
aaa ←エンター
input from fd0: aaa
xxx
input from fd0: xxx
no input  ←5秒経過
no input

その他の連携方法

C/C++ 動的ライブラリを Python から利用する方法としては ctypes 以外にも Python 標準の <Python.h>Boost.Python があります。

例えば ModernGL では libGL.so.1 を C++ 側で読み込むために <Python.h> が利用されていますOpenRAVE では Boost.Python が利用されています

hello.cpp

#include <boost/python.hpp>

char const* greet() {
    return "hello, world";
}

BOOST_PYTHON_MODULE(hello) {
    using namespace boost::python;
    def("greet", greet);
}

ldconfig で動的リンク対象のライブラリがインストールされていることを確認します。

/sbin/ldconfig -p | grep python

dpkg -S /usr/lib/x86_64-linux-gnu/libboost_python.so  #=> libboost-python1.62-dev
dpkg -S /usr/lib/x86_64-linux-gnu/libpython2.7.so  #=> libpython2.7-dev

ビルドコマンド例

g++ -shared -fPIC -lboost_python -lpython2.7 -I/usr/include/python2.7 -o hello.so hello.cpp

実行例

In [1]: import hello

In [2]: hello.greet()
Out[2]: 'hello, world'
Related pages
    概要 コンピュータグラフィックスのレンダリングライブラリの一つ OpenGL はプラットフォームに依存しない仕様となっています。プラットフォームの一つに X11 があります。プラットフォームに依存する仕様は EGL (Embedded-System Graphics Library) にまとめられています。EGL は OpenGL とネイティブプラットフォームの間のインタフェースとして機能します
    概要 ファイル記述子 (File Descriptor) に関連するシステムコールを利用した C 言語のサンプルコードを記載します。 ファイルの読み書き open/close main.c #include <unistd.h> #include <fcntl.h> #include <stdio.h> int main() { int fd_r, fd_w; // 読
    概要 C++ を Python から利用する方法の一つに pybind11 があります。C++11 をサポートするコンパイラが必要です。サンプルコードを記載します。 pybind11 — Seamless operability between C++11 and Python Reference 簡単なサンプル