Project layout & generated API¶
Project layout (full)¶
After scaffolding with one object and running just-makeit perf. The tag on
each file is who owns it, which decides what jm apply does to it; the five
kinds are defined in the edit lifecycle.
my_dsp/
├── just-makeit.toml [yours] the manifest
├── objects/
│ └── gain.toml [yours] gain's manifest table
├── pyproject.toml [yours]
├── README.md [yours]
├── CMakeLists.txt [shared] jm splices its marked blocks
├── CMakePresets.json [versioned] cmake --preset release|debug|…
├── Makefile [versioned] make · make test · make bench
├── bootstrap.toml [versioned] toolchain declaration for CI
├── .clang-tidy .gitignore .gitattributes [versioned]
├── Doxyfile zensical.toml [versioned]
├── cmake/
│ ├── my_dsp.pc.in [jm] pkg-config template
│ └── my_dsp-config.cmake.in [jm] find_package template
├── docs/ [yours] index.md, api.md
├── benchmarks/history/ [yours] saved `jm bench` results
├── native/
│ ├── inc/
│ │ └── my_dsp/
│ │ ├── my_dsp.h [jm] umbrella header
│ │ ├── clib_common.h [versioned] common C99 types
│ │ ├── pyex_common.h [versioned] Python extension includes
│ │ ├── jm_perf.h [versioned] JM_FORCEINLINE / JM_HOT / …
│ │ ├── jm_simd.h [versioned] width-portable SIMD macros
│ │ └── gain/
│ │ └── gain_core.h [yours] state struct + step() ← implement
│ ├── src/
│ │ ├── my_dsp_lib.c [yours] libmy_dsp root: my_dsp_version()
│ │ └── gain/
│ │ ├── CMakeLists.txt [jm]
│ │ ├── gain_core.c [yours] create/destroy/steps/methods
│ │ └── gain_ext.c [jm] Python binding ← do not edit
│ ├── tests/
│ │ ├── jm_test.h [versioned] test macros
│ │ ├── test_gain_core.c [yours] CTest lifecycle test
│ │ └── test_gain_symbols.c [derived] links every function the binding calls
│ └── benchmarks/
│ ├── jm_bench.h [versioned] portable timer
│ └── bench_gain_core.c [yours] C benchmark
└── src/
└── my_dsp/
├── __init__.py [shared] jm keeps the re-exports current
├── gain.pyi [jm] type stub
├── benchmarks/bench_gain.py [jm] yours once you delete its `# jm:generated` line
└── tests/test_gain.py [jm] unittest; yours once you delete its `# jm:generated` line
[yours] files are created once and then only gain what is missing — a
declaration or a stub for a new method. [jm] files are rewritten on every
apply. [versioned] files are jm's but written once; a newer jm reports
them as OUTDATED in jm status.
Generated C API¶
Every object follows this lifecycle. Names are derived from the component
name you pass to just-makeit object:
/* Constructor — one parameter per --state declaration */
<pkg>_engine_state_t *<pkg>_engine_create(double gain);
/* Destructor */
void <pkg>_engine_destroy(<pkg>_engine_state_t *state);
/* Reset — restores every field to its declared default */
void <pkg>_engine_reset(<pkg>_engine_state_t *state);
/* Single sample — inline stub in _core.h; implement here */
static inline float _Complex
<pkg>_engine_step(const <pkg>_engine_state_t *state, float _Complex x);
/* Block processor — in _core.c, loops over step() */
void <pkg>_engine_steps(
<pkg>_engine_state_t *state,
const float _Complex *input,
float _Complex *output,
size_t n);
/* Generator (--arg-type void) — no input parameter */
static inline float
<pkg>_nco_step(const <pkg>_nco_state_t *state);
void <pkg>_nco_steps(<pkg>_nco_state_t *state, float *output, size_t n);
/* Getter and setter for each --state variable */
double <pkg>_engine_get_gain(const <pkg>_engine_state_t *state);
void <pkg>_engine_set_gain(<pkg>_engine_state_t *state, double val);
Generated Python API¶
from my_project import Engine # standalone object
import numpy as np
obj = Engine(gain=1.0) # keyword arg per --state variable
obj = Engine() # uses declared defaults
y: complex = obj.step(1.0 + 0.5j) # single sample
x = np.ones(1024, dtype=np.complex64)
y = obj.steps(x) # returns new array
obj.steps(x, out=y) # zero-copy: fills y, returns y
obj.get_gain() # getter
obj.set_gain(2.0) # setter
obj.reset() # restores declared defaults
with Engine() as e: # context manager
y = e.steps(x)
# Module subpackage — one .so, one subpackage import
from my_filters.filter import Fir, Biquad
fir = Fir(gain=1.0)
bq = Biquad(b0=1.0)
Types within a module are fully independent — separate lifecycles, each
with its own step, steps, reset, getters/setters, and context
manager.