# Writing C Modules Cell makes it easy to extend functionality with C code. C files in a package are compiled into a dynamic library and can be imported like any other module. ## Basic Structure A C module exports a single function that returns a JavaScript value: ```c // mymodule.c #include "cell.h" #define CELL_USE_NAME js_mypackage_mymodule_use static JSValue js_add(JSContext *js, JSValue self, int argc, JSValue *argv) { double a = js2number(js, argv[0]); double b = js2number(js, argv[1]); return number2js(js, a + b); } static JSValue js_multiply(JSContext *js, JSValue self, int argc, JSValue *argv) { double a = js2number(js, argv[0]); double b = js2number(js, argv[1]); return number2js(js, a * b); } static const JSCFunctionListEntry js_funcs[] = { MIST_FUNC_DEF(mymodule, add, 2), MIST_FUNC_DEF(mymodule, multiply, 2), }; CELL_USE_FUNCS(js_funcs) ``` ## Symbol Naming The exported function must follow this naming convention: ``` js___use ``` Where: - `` is the package name with `/` and `.` replaced by `_` - `` is the C file name without extension Examples: - `mypackage/math.c` → `js_mypackage_math_use` - `gitea.pockle.world/john/lib/render.c` → `js_gitea_pockle_world_john_lib_render_use` ## Required Headers Include `cell.h` for all Cell integration: ```c #include "cell.h" ``` This provides: - QuickJS types and functions - Conversion helpers - Module definition macros ## Conversion Functions ### JavaScript ↔ C ```c // Numbers double js2number(JSContext *js, JSValue v); JSValue number2js(JSContext *js, double g); // Booleans int js2bool(JSContext *js, JSValue v); JSValue bool2js(JSContext *js, int b); // Strings (must free with JS_FreeCString) const char *JS_ToCString(JSContext *js, JSValue v); void JS_FreeCString(JSContext *js, const char *str); JSValue JS_NewString(JSContext *js, const char *str); ``` ### Blobs ```c // Get blob data (returns pointer, sets size in bytes) void *js_get_blob_data(JSContext *js, size_t *size, JSValue v); // Get blob data in bits void *js_get_blob_data_bits(JSContext *js, size_t *bits, JSValue v); // Create new stone blob from data JSValue js_new_blob_stoned_copy(JSContext *js, void *data, size_t bytes); // Check if value is a blob int js_is_blob(JSContext *js, JSValue v); ``` ## Function Definition Macros ### JSC_CCALL Define a function with automatic return value: ```c JSC_CCALL(mymodule_greet, const char *name = JS_ToCString(js, argv[0]); char buf[256]; snprintf(buf, sizeof(buf), "Hello, %s!", name); ret = JS_NewString(js, buf); JS_FreeCString(js, name); ) ``` ### JSC_SCALL Shorthand for functions taking a string first argument: ```c JSC_SCALL(mymodule_strlen, ret = number2js(js, strlen(str)); ) ``` ### MIST_FUNC_DEF Register a function in the function list: ```c MIST_FUNC_DEF(prefix, function_name, arg_count) ``` ## Module Export Macros ### CELL_USE_FUNCS Export an object with functions: ```c static const JSCFunctionListEntry js_funcs[] = { MIST_FUNC_DEF(mymod, func1, 1), MIST_FUNC_DEF(mymod, func2, 2), }; CELL_USE_FUNCS(js_funcs) ``` ### CELL_USE_INIT For custom initialization: ```c CELL_USE_INIT( JSValue obj = JS_NewObject(js); // Custom setup... return obj; ) ``` ## Complete Example ```c // vector.c - Simple 2D vector operations #include "cell.h" #include #define CELL_USE_NAME js_mypackage_vector_use JSC_CCALL(vector_length, double x = js2number(js, argv[0]); double y = js2number(js, argv[1]); ret = number2js(js, sqrt(x*x + y*y)); ) JSC_CCALL(vector_normalize, double x = js2number(js, argv[0]); double y = js2number(js, argv[1]); double len = sqrt(x*x + y*y); if (len > 0) { JSValue result = JS_NewObject(js); JS_SetPropertyStr(js, result, "x", number2js(js, x/len)); JS_SetPropertyStr(js, result, "y", number2js(js, y/len)); ret = result; } ) JSC_CCALL(vector_dot, double x1 = js2number(js, argv[0]); double y1 = js2number(js, argv[1]); double x2 = js2number(js, argv[2]); double y2 = js2number(js, argv[3]); ret = number2js(js, x1*x2 + y1*y2); ) static const JSCFunctionListEntry js_funcs[] = { MIST_FUNC_DEF(vector, length, 2), MIST_FUNC_DEF(vector, normalize, 2), MIST_FUNC_DEF(vector, dot, 4), }; CELL_USE_FUNCS(js_funcs) ``` Usage in Cell: ```javascript var vector = use('vector') var len = vector.length(3, 4) // 5 var n = vector.normalize(3, 4) // {x: 0.6, y: 0.8} var d = vector.dot(1, 0, 0, 1) // 0 ``` ## Combining C and Cell A common pattern is to have a C file provide low-level functions and a `.cm` file provide a higher-level API: ```c // _vector_native.c // ... raw C functions ... ``` ```javascript // vector.cm var native = this // C module passed as 'this' function Vector(x, y) { return {x: x, y: y} } Vector.length = function(v) { return native.length(v.x, v.y) } Vector.normalize = function(v) { return native.normalize(v.x, v.y) } return Vector ``` ## Build Process C files are automatically compiled when you run: ```bash cell build cell update ``` The resulting dynamic library is placed in `~/.cell/lib/`. ## Platform-Specific Code Use filename suffixes for platform variants: ``` audio.c # default audio_playdate.c # Playdate audio_emscripten.c # Web/Emscripten ``` Cell selects the appropriate file based on the target platform. ## Static Declarations Keep internal functions and variables `static`: ```c static int helper_function(int x) { return x * 2; } static int module_state = 0; ``` This prevents symbol conflicts between packages.