0Pricing
Python Academy · レッスン

cffi: C 外部関数インターフェース

インライン C 宣言を使い、より Python らしい C 相互運用を cffi で実現します。

「cffi: C 外部関数インターフェース」はCoddyKit上の無料Python Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはPython Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Python Academyコースには全4レッスンが含まれています。

cffiとは

cffi(C Foreign Function Interface)は、ctypesのクラスではなくCの宣言を使用して、PythonからCコードを呼び出せるようにします。よりPythonらしく、多くの場合より安全です。

# pip install cffi
from cffi import FFI
ffi = FFI()

ffi.cdef("double sqrt(double x);")
libm = ffi.dlopen(None)   # current process
print(libm.sqrt(2.0))     # 1.4142...

ABIモードとAPIモード

ffi.dlopen() はABIモードで、実行時に既存の共有ライブラリを呼び出します。APIモードはCのラッパーをコンパイルするため、より高速で堅牢です。

from cffi import FFI
ffi = FFI()

# ABI mode (no compile step):
ffi.cdef("int abs(int x);")
libc = ffi.dlopen("libc.so.6")  # Linux
print(libc.abs(-42))   # 42

cdef宣言

Pythonから使用するC関数のシグネチャ、構造体、typedefを宣言するには、ffi.cdef() を使用します。

from cffi import FFI
ffi = FFI()

ffi.cdef("""
    typedef struct {
        double x;
        double y;
    } Point;

    double distance(Point a, Point b);
""")

# lib = ffi.dlopen("mylib.so")

C構造体の作成

ffi.new() を使用してC構造体を割り当て、Pythonからそのフィールドを設定します。

from cffi import FFI
ffi = FFI()

ffi.cdef("typedef struct { int x; int y; } Vec2;")
lib = ffi.dlopen(None)

v = ffi.new("Vec2 *")
v.x = 3
v.y = 4
print(v.x, v.y)   # 3 4

文字列とバッファー

変更可能なバッファーには ffi.new("char[]", n) を使用し、Cの文字列ポインターをPythonのbytesに変換するには ffi.string(ptr) を使用します。

from cffi import FFI
ffi = FFI()

buf = ffi.new("char[]", b"hello world")
print(ffi.string(buf))   # b"hello world"

# Mutable buffer for output:
out = ffi.new("char[128]")
# c_func(out, 128)  # fill the buffer
# result = ffi.string(out).decode()

verifyを使ったAPIモード

APIモードでは、型安全性とパフォーマンスを向上させる小さなCラッパーモジュールをコンパイルします。ffi.set_source() を使用してオフラインでビルドします。

from cffi import FFI
ffi = FFI()

ffi.cdef("int add(int a, int b);")
ffi.set_source("_mylib",
    """
    int add(int a, int b) { return a + b; }
    """
)

# Build: python build_mylib.py
# from _mylib import ffi, lib
# print(lib.add(3, 4))   # 7

CからPythonへのコールバック

ffi.callback() を使用して、Cライブラリに渡せるC関数ポインターとしてPython関数をラップします。

from cffi import FFI
ffi = FFI()
ffi.cdef("typedef int (*compare_fn)(int, int);")

@ffi.callback("int(int, int)")
def compare(a, b):
    return a - b

# pass compare to a C sort function

メモリ管理

ffi.new() で割り当てたオブジェクトはPythonが所有し、自動的に解放されます。カスタムデストラクターを関連付けるには ffi.gc(ptr, destructor) を使用します。

from cffi import FFI
ffi = FFI()

# ptr freed when Python GC collects it:
buf = ffi.new("int[10]")
buf[0] = 42
print(buf[0])   # 42

# Custom destructor:
# ptr = ffi.gc(lib.alloc(), lib.free)

cffiとctypesの比較

cffiはCの宣言文字列を使用するため、C開発者にとってより自然です。一方、ctypesはPythonのクラスを使用します。cffiのAPIモードはより高速で、ctypesはコンパイルを必要としません。

# ctypes approach:
import ctypes
libc = ctypes.CDLL("libc.so.6")
libc.abs.restype = ctypes.c_int
libc.abs.argtypes = [ctypes.c_int]
print(libc.abs(-5))   # 5

# cffi approach:
from cffi import FFI; ffi = FFI()
ffi.cdef("int abs(int);")
lib = ffi.dlopen(None)
print(lib.abs(-5))    # 5

cffiとNumPyの使用

ffi.cast() と配列の ctypes.data ポインターを使用して、cffi経由でNumPy配列のデータを直接Cに渡します。

import numpy as np
from cffi import FFI
ffi = FFI()

arr = np.array([1.0, 2.0, 3.0])
ptr = ffi.cast("double *", arr.ctypes.data)
# Now ptr can be passed to a C function expecting double*

cffiでのエラー処理

C関数は通常、戻り値によってエラーを通知します。戻り値を確認し、POSIXのエラーコードには ffi.errno を使用します。

import errno as errno_mod
from cffi import FFI
ffi = FFI()
ffi.cdef("int open(const char *path, int flags);")
lib = ffi.dlopen(None)

fd = lib.open(b"/nonexistent", 0)
if fd == -1:
    err = ffi.errno
    raise OSError(err, "open failed")

確認問題

cffiでは、呼び出すC関数のシグネチャをどのような方法で宣言しますか?

まとめ

cffiでは、ffi.cdef() でCのシグネチャを宣言し、ffi.dlopen() でABIモードのライブラリを呼び出します。APIモードでは、パフォーマンスを向上させるラッパーをコンパイルします。Cのメモリには ffi.new()、PythonからCへのコールバックには ffi.callback() を使用します。

よくある質問

「cffi: C 外部関数インターフェース」レッスンは無料ですか?

はい。「cffi: C 外部関数インターフェース」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Python Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Python Academyコースには全4レッスンが含まれています。

「cffi: C 外部関数インターフェース」で何を学びますか?

インライン C 宣言を使い、より Python らしい C 相互運用を cffi で実現します。 ブラウザで直接実行するハンズオンコードでPython Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Python Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのPython Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「cffi: C 外部関数インターフェース」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このPython Academyレッスンでコードを書いて実行できますか?

はい。すべてのPython Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. C 拡張の理由: 用途とトレードオフ
  2. ctypes: Python から C ライブラリを呼び出す
  3. cffi: C 外部関数インターフェース
  4. Python C 拡張モジュールの作成
← Python Academyに戻る