Project template
Directory structure
<component>/
native/
inc/
clib_common.h # common C99 types
pyex_common.h # common Python extension includes
<component>/
<component>_core.h # component API (edit this)
src/
<component>/
<component>_core.c # core C implementation
<component>_ext.c # thin Python binding
tests/
test_<component>_core.c # CTest
src/
<component>/
__init__.py
<component>.pyi # type stub
tests/
test_<component>.py # pytest
CMakeLists.txt
Makefile
pyproject.toml
.gitignore
README.md
C conventions
State struct
One field is generated for each --state name:type flag.
typedef struct {
float gain;
float bandwidth;
int32_t order;
} <component>_state_t;
Constructor
One parameter per --state var, in declaration order.
/**
* @return Heap-allocated state, or NULL on allocation failure.
* @note Caller must call <component>_destroy() when done.
*/
<component>_state_t *<component>_create(float gain, float bandwidth, int32_t order);
Destructor
/**
* @param state May be NULL.
*/
void <component>_destroy(<component>_state_t *state);
Reset
Restores all state variables to their declared defaults.
void <component>_reset(<component>_state_t *state);
Single-sample processor
Generated as a pass-through stub — implement your DSP here.
/** Inlined for maximum performance. */
static inline float complex
<component>_step(const <component>_state_t *state, float complex x)
{
(void)state; /* TODO: implement DSP using state variables */
return x;
}
Block processor
/**
* @param output Pre-allocated by caller; may alias input for in-place.
*/
void <component>_steps(
<component>_state_t *state,
const float complex *input,
float complex *output,
size_t n);
Getter / setter (one pair per --state var)
float <component>_get_gain(const <component>_state_t *state);
void <component>_set_gain(<component>_state_t *state, float gain);
float <component>_get_bandwidth(const <component>_state_t *state);
void <component>_set_bandwidth(<component>_state_t *state, float bandwidth);
int32_t <component>_get_order(const <component>_state_t *state);
void <component>_set_order(<component>_state_t *state, int32_t order);
Python binding conventions
The generated <component>_ext.c exposes the C API as a Python class:
from <package> import <Component>
# lifecycle
obj = <Component>(gain=1.0, bandwidth=200.0, order=4)
# single sample
y: complex = obj.step(1.0 + 0.0j)
# block (numpy)
import numpy as np
x = np.ones(256, dtype=np.complex64)
y = obj.steps(x) # returns complex64 ndarray
# getters / setters
obj.get_gain()
obj.set_gain(2.0)
# reset (restores to declared defaults)
obj.reset()
# context manager (auto-destroys)
with <Component>(1.0) as obj:
y = obj.step(1.0 + 0.0j)
# explicit release
obj.destroy()
Build system
CMakeLists.txt uses Python3_add_library (CMake ≥ 3.16) to build the
extension. The .so is written directly into src/<package>/ so the
package is importable from the source tree after a single make.
pyproject.toml declares just-buildit as the PEP 517 backend with
command = "make just-build". The just-build Makefile target copies
src/<package>/ (Python files + .so) to $JUST_BUILDIT_OUTPUT_DIR for
packaging.
make → cmake configure + build
make test → ctest + pytest
make just-build → build + copy to $JUST_BUILDIT_OUTPUT_DIR
pip install . → just-buildit → make just-build → wheel
pip install -e . → just-buildit editable (.pth pointing at src/)