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.