FFI (Foreign Function Interface) の一つである ctypes を利用すると、C 言語のライブラリを Python から利用できます。サンプルコードを記載します。
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 経由で利用できます。例えば、こちらのページに記載した 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'