--- icon: cpu title: C Bindings order: 17 --- # C bindings A binding converts Python arguments to native values, calls your C code, and writes a Python result. All native functions use the `py_CFunction` signature: ```c typedef bool (*py_CFunction)(int argc, py_StackRef argv); ``` `argc` is the argument count and `py_arg(i)` accesses argument `i`. The parameter names `argc` and `argv` are required by the convenience macros. ## A complete binding This program exports `native.add(a, b=1)`: ```c #include "pocketpy.h" #include static bool native_add(int argc, py_Ref argv) { PY_CHECK_ARGC(2); PY_CHECK_ARG_TYPE(0, tp_int); PY_CHECK_ARG_TYPE(1, tp_int); py_i64 a = py_toint(py_arg(0)); py_i64 b = py_toint(py_arg(1)); if((b > 0 && a > INT64_MAX - b) || (b < 0 && a < INT64_MIN - b)) { return ValueError("sum is outside the signed 64-bit range"); } py_newint(py_retval(), a + b); return true; } int main(void) { py_initialize(); py_GlobalRef mod = py_newmodule("native"); py_bind(mod, "add(a, b=1)", native_add); bool ok = py_exec("from native import add\n" "assert add(3, 7) == 10\n" "assert add(4) == 5\n" "assert add(4, b=2) == 6\n", "app.py", EXEC_MODE, NULL); if(!ok) py_printexc(); py_finalize(); return ok ? 0 : 1; } ``` The signature passed to `py_bind()` resolves defaults before the callback runs, so the callback receives two arguments even for `add(4)`. Required parameters such as `a` are positional in pocketpy; see [argument compatibility](features/differences.md#function-arguments). Use `py_i64` for Python integers and validate the native library's accepted range before narrowing to `int` or another smaller type. The example checks addition overflow to avoid undefined C arithmetic. ## Choose a binding helper | Helper | Use | | --- | --- | | `py_bind(obj, "name(args...)", callback)` | Signature-based binding with names and defaults. | | `py_bindfunc(module, "name", callback)` | Simple positional-argument function; the callback validates the count. | | `py_bindmethod(type, "name", callback)` | Instance method; `self` is argument 0 and included in `argc`. | | `py_bindstaticmethod(type, "name", callback)` | Method without an implicit `self`. | | `py_bindproperty(type, "name", getter, setter)` | Property; a `NULL` setter makes it read-only. | | `py_bindmagic(type, py_name("__len__"), callback)` | Special method on a native type. | For a global function, bind to `py_getmodule("__main__")`. For a reusable API, create a named module. ## Results and errors On success, write a value to `py_retval()` and return `true`. A function with no meaningful result must still call `py_newnone(py_retval())`. On failure, set an exception and return `false`. For example: ```c static bool require_positive(int argc, py_Ref argv) { PY_CHECK_ARGC(1); PY_CHECK_ARG_TYPE(0, tp_int); if(py_toint(py_arg(0)) <= 0) return ValueError("value must be positive"); py_newnone(py_retval()); return true; } ``` Type-check macros return immediately when validation fails. If an API you call fails, propagate its error rather than replacing it with a generic exception. See [execution and exceptions](C-API/execution.md) for the host's recovery path. ## Native object lifetimes `py_newtype()` registers a Python type. `py_newobject()` allocates an instance with native userdata and optional Python slots or an attribute dictionary. Store references to Python objects in Python slots or another GC-visible container. A raw pointer inside userdata is not automatically a GC root. Keep a native resource alive for as long as a bound object can access it, and use the type's destructor callback to release native allocations. For class-heavy C++ libraries, the [C++ binding layer](bindings-cpp.md) handles much of this plumbing. The [function reference](C-API/functions.md) documents the lower-level type, slot, and binding operations.