#!/usr/bin/env python3 """Generate a performance-oriented C module skeleton. Creates: - include/.h - src/.c - tests/test_.c - bench/bench_.c """ from __future__ import annotations import argparse import re from pathlib import Path def normalize_name(raw: str) -> str: name = raw.strip().lower().replace("-", "_") name = re.sub(r"[^a-z0-9_]", "_", name) name = re.sub(r"_+", "_", name).strip("_") if not name: raise ValueError("module name is empty after normalization") if name[0].isdigit(): name = f"m_{name}" return name def write_file(path: Path, content: str, force: bool) -> None: if path.exists() and not force: raise FileExistsError(f"file exists: {path}") path.parent.mkdir(parents=True, exist_ok=True) path.write_text(content, encoding="utf-8") def render_header(module: str) -> str: guard = f"{module.upper()}_H" return f"""#ifndef {guard} #define {guard} #include #include #ifdef __cplusplus extern "C" {{ #endif typedef enum {{ {module}_ok = 0, {module}_err_null = 1, {module}_err_size = 2 }} {module}_status_t; {module}_status_t {module}_process_f32( const float *restrict in, float *restrict out, size_t n); #ifdef __cplusplus }} #endif #endif """ def render_source(module: str) -> str: return f"""#include "{module}.h" {module}_status_t {module}_process_f32( const float *restrict in, float *restrict out, size_t n) {{ if (in == NULL || out == NULL) {{ return {module}_err_null; }} if (n == 0) {{ return {module}_err_size; }} for (size_t i = 0; i < n; ++i) {{ out[i] = in[i] * 1.0f; }} return {module}_ok; }} """ def render_test(module: str) -> str: return f"""#include #include #include "{module}.h" int main(void) {{ float in[4] = {{1.0f, 2.0f, 3.0f, 4.0f}}; float out[4] = {{0.0f, 0.0f, 0.0f, 0.0f}}; {module}_status_t st = {module}_process_f32(in, out, 4); assert(st == {module}_ok); for (size_t i = 0; i < 4; ++i) {{ assert(out[i] == in[i]); }} printf("test_{module}: ok\\n"); return 0; }} """ def render_bench(module: str) -> str: return f"""#define _POSIX_C_SOURCE 200809L #include #include #include #include #include "{module}.h" static uint64_t ns_now(void) {{ struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (uint64_t)ts.tv_sec * 1000000000ull + (uint64_t)ts.tv_nsec; }} int main(void) {{ const size_t n = 1u << 20; float *in = (float *)malloc(n * sizeof(float)); float *out = (float *)malloc(n * sizeof(float)); if (!in || !out) {{ fprintf(stderr, "alloc failed\\n"); free(in); free(out); return 1; }} for (size_t i = 0; i < n; ++i) {{ in[i] = (float)(i & 1023u); }} uint64_t t0 = ns_now(); {module}_status_t st = {module}_process_f32(in, out, n); uint64_t t1 = ns_now(); if (st != {module}_ok) {{ fprintf(stderr, "kernel error: %d\\n", (int)st); free(in); free(out); return 1; }} double ns_per_elem = (double)(t1 - t0) / (double)n; printf("{module} ns/elem: %.3f\\n", ns_per_elem); free(in); free(out); return 0; }} """ def main() -> int: parser = argparse.ArgumentParser(description="Scaffold a C module with tests and bench") parser.add_argument("--name", required=True, help="Module name (snake_case preferred)") parser.add_argument( "--out-dir", default=".", help="Project root where include/src/tests/bench live", ) parser.add_argument("--force", action="store_true", help="Overwrite existing files") args = parser.parse_args() module = normalize_name(args.name) root = Path(args.out_dir).resolve() write_file(root / "include" / f"{module}.h", render_header(module), args.force) write_file(root / "src" / f"{module}.c", render_source(module), args.force) write_file(root / "tests" / f"test_{module}.c", render_test(module), args.force) write_file(root / "bench" / f"bench_{module}.c", render_bench(module), args.force) print(f"created module skeleton: {module}") print(f"root: {root}") print("next:") print( f" gcc -O0 -g3 -fsanitize=address,undefined " f"-Iinclude src/{module}.c tests/test_{module}.c -o test_{module}" ) print( f" gcc -O3 -march=native " f"-Iinclude src/{module}.c bench/bench_{module}.c -o bench_{module}" ) return 0 if __name__ == "__main__": raise SystemExit(main())