C Functions and Scalars

Build a scalar C function

Create native_math.c, build it, and call the generated extension:

double add(double left, double right) {
    return left + right;
}
python3 -m prik --language c native_math.c \
  --compiler cc \
  --out native_math \
  --out-dir build

The source build writes this editable native_math.pyi contract beside the extension:

from prik.contracts import Float64

def add(left: Float64, right: Float64) -> Float64: ...

Generate the contract without compiling when you only want to inspect it:

python3 -m prik generate --pyi --language c native_math.c --out native_math.pyi
import sys

import numpy as np

sys.path.insert(0, "build")
import native_math

print(native_math.add(np.float64(3.0), np.float64(2.5)))
5.5

Pass the NumPy scalar matching the generated contract type—for example, np.float64 for a C double. C contract extraction writes one file rather than the Fortran package layout; the .pyi format reference shows both forms.

Rename and reorder arguments

An authored contract can present an existing C ABI under a better Python name and argument order. It names the real C symbol, then states each native argument explicitly.

When the Python declaration and C symbol have the same name, omit @bind: that name is the default native target. Use @bind("native_name") only for a different C symbol.

Create projected.c:

int combine_native(int right, int *left, int bias) {
    return 100 * right + 10 * *left + bias;
}

void read_status(int value, int *output) {
    *output = value + 1;
}

Create projected.pyi:

from prik.contracts import Addr, Arg, Int32, Return, bind, native_call

@bind("combine_native")
@native_call([Arg(1), Addr(Arg(0)), Int32(5)])
def combine(left: Int32, right: Int32) -> Int32: ...

@bind("read_status")
@native_call([Arg(0), Return("output", 0)])
def status(value: Int32) -> Int32: ...

combine is the Python name, combine_native is the linked C symbol, Addr(Arg(0)) passes the address of left, and Int32(5) supplies the literal third native argument. Return(...) turns the output pointer into the Python result.

python3 -m prik --language c projected.pyi \
  --native-c-sources projected.c \
  --compiler cc \
  --out projected \
  --out-dir build
import sys

import numpy as np

sys.path.insert(0, "build")
import projected

print(projected.combine(np.int32(2), np.int32(3)))
print(projected.status(np.int32(7)))
325
8

Exact native scalar identities

Generated C contracts are target-specific. Distinct C types such as long and long long may use the same public NumPy contract type while retaining their exact native identity inside @native_call(...):

from prik.contracts import Arg, CLongLong, Float64, Int64, Return, native_call

@native_call([Arg(0)], result=CLongLong(Return(0)))
def llround(value: Float64) -> Int64: ...

The public signature continues to use ordinary NumPy contract types. Scalars and scalar addresses accept that public dtype and convert at the native boundary. Ranked arguments require the exact NumPy element storage so their pointer path remains zero-copy. See Preserve an Exact C Scalar at the Native Call for arguments, addresses, results, arrays, and exact-storage rules.

Next

Continue with Pointers, Arrays, and Strings when a C parameter uses pointer syntax.