12
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ctypesとは

ctypesは、PythonからC言語のライブラリ(DLL/so)を直接呼び出すための標準ライブラリ。外部ライブラリのインストールなしで使える。

低レベルなことやりたいときにモのすごく便利。

import ctypes

基本的な型

ctypes型 C型 サイズ
c_int int 4 bytes
c_long long 4/8 bytes
c_float float 4 bytes
c_double double 8 bytes
c_char char 1 byte
c_char_p char* ポインタ
c_void_p void* ポインタ
c_bool bool 1 byte

ライブラリの読み込み

import ctypes
import ctypes.util

# Windows
libc = ctypes.CDLL('msvcrt')

# Linux/macOS
libc = ctypes.CDLL(ctypes.util.find_library('c'))

# 直接パス指定
lib = ctypes.CDLL('/path/to/library.so')
lib = ctypes.CDLL('C:/path/to/library.dll')

関数の呼び出し

基本的な呼び出し

# 引数と戻り値の型を設定
libc.strlen.argtypes = [ctypes.c_char_p]
libc.strlen.restype = ctypes.c_size_t

# 呼び出し(バイト文字列で渡す)
length = libc.strlen(b"Hello World")
print(length)  # 11

数学関数

# sqrt
libm.sqrt.argtypes = [ctypes.c_double]
libm.sqrt.restype = ctypes.c_double
print(libm.sqrt(2.0))  # 1.4142135623730951

# pow
libm.pow.argtypes = [ctypes.c_double, ctypes.c_double]
libm.pow.restype = ctypes.c_double
print(libm.pow(2.0, 10.0))  # 1024.0

構造体

class Point(ctypes.Structure):
    _fields_ = [
        ("x", ctypes.c_int),
        ("y", ctypes.c_int),
    ]

# 使用
p = Point(10, 20)
print(f"({p.x}, {p.y})")  # (10, 20)
print(ctypes.sizeof(Point))  # 8

ネストした構造体

class Rectangle(ctypes.Structure):
    _fields_ = [
        ("top_left", Point),
        ("bottom_right", Point),
    ]

rect = Rectangle(Point(0, 0), Point(100, 50))

配列

# 固定サイズ配列の型を作成
IntArray5 = ctypes.c_int * 5

# 配列を作成
arr = IntArray5(1, 2, 3, 4, 5)
print(list(arr))  # [1, 2, 3, 4, 5]
print(sum(arr))   # 15

配列を含む構造体

class Vector3D(ctypes.Structure):
    _fields_ = [
        ("coords", ctypes.c_double * 3),
    ]

vec = Vector3D((1.0, 2.0, 3.0))

ポインタ

# 値を作成
x = ctypes.c_int(42)

# ポインタを取得
ptr = ctypes.pointer(x)

# ポインタ経由でアクセス
print(ptr.contents.value)  # 42

# 値を変更
ptr.contents.value = 100
print(x.value)  # 100

byref(参照渡し)

# 関数に参照を渡す(高速)
y = ctypes.c_int(0)
some_c_func(ctypes.byref(y))

コールバック関数

Pythonの関数をCに渡す:

# 関数型を定義
COMPARE_FUNC = ctypes.CFUNCTYPE(
    ctypes.c_int,      # 戻り値
    ctypes.c_void_p,   # 引数1
    ctypes.c_void_p    # 引数2
)

# Python関数
def py_compare(a, b):
    a_val = ctypes.cast(a, ctypes.POINTER(ctypes.c_int)).contents.value
    b_val = ctypes.cast(b, ctypes.POINTER(ctypes.c_int)).contents.value
    return a_val - b_val

# コールバックを作成
callback = COMPARE_FUNC(py_compare)

# qsortで使用
arr = (ctypes.c_int * 5)(5, 2, 8, 1, 9)
libc.qsort(arr, 5, ctypes.sizeof(ctypes.c_int), callback)
print(list(arr))  # [1, 2, 5, 8, 9]

Union(共用体)

同じメモリを別の型として解釈:

class IntOrFloat(ctypes.Union):
    _fields_ = [
        ("i", ctypes.c_int),
        ("f", ctypes.c_float),
    ]

u = IntOrFloat()
u.i = 1234567890
print(u.f)  # 同じビットをfloatとして解釈

実践例:自作Cライブラリ

1. Cコード

// mylib.c
#include <math.h>

double circle_area(double radius) {
    return M_PI * radius * radius;
}

typedef struct {
    double x, y;
} Point;

double distance(Point* p1, Point* p2) {
    double dx = p2->x - p1->x;
    double dy = p2->y - p1->y;
    return sqrt(dx*dx + dy*dy);
}

2. コンパイル

# Linux
gcc -shared -fPIC -o mylib.so mylib.c -lm

# Windows
cl /LD mylib.c

# macOS
gcc -shared -o mylib.dylib mylib.c

3. Python から呼び出し

import ctypes

# ライブラリ読み込み
lib = ctypes.CDLL('./mylib.so')

# circle_area
lib.circle_area.argtypes = [ctypes.c_double]
lib.circle_area.restype = ctypes.c_double
print(lib.circle_area(5.0))  # 78.53981633974483

# 構造体定義
class Point(ctypes.Structure):
    _fields_ = [("x", ctypes.c_double), ("y", ctypes.c_double)]

# distance
lib.distance.argtypes = [ctypes.POINTER(Point), ctypes.POINTER(Point)]
lib.distance.restype = ctypes.c_double

p1 = Point(0.0, 0.0)
p2 = Point(3.0, 4.0)
print(lib.distance(ctypes.byref(p1), ctypes.byref(p2)))  # 5.0

エラーハンドリング

# Cのerrnoを取得
libc = ctypes.CDLL(ctypes.util.find_library('c'), use_errno=True)

# Windowsの場合
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
error = ctypes.get_last_error()

注意点

  1. 型の一致 - 引数と戻り値の型を正確に設定
  2. メモリ管理 - Pythonのオブジェクトがガベージコレクションされないよう注意
  3. 文字列 - Cにはバイト文字列(b"...")で渡す
  4. 32bit/64bit - ポインタサイズが異なる

まとめ

用途 方法
ライブラリ読み込み ctypes.CDLL()
型設定 argtypes, restype
構造体 ctypes.Structure
配列 c_type * n
ポインタ pointer(), byref()
コールバック CFUNCTYPE()

ctypesを使えば、高速なCライブラリをPythonから簡単に呼び出せます!

12
0
0

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
12
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?