{"id":19967301,"url":"https://github.com/foxbud/libcclosure","last_synced_at":"2025-07-23T03:04:41.693Z","repository":{"id":39783038,"uuid":"350350487","full_name":"Foxbud/libcclosure","owner":"Foxbud","description":"Thread-safe closures as first-class functions for C","archived":false,"fork":false,"pushed_at":"2023-10-07T18:51:44.000Z","size":110,"stargazers_count":15,"open_issues_count":1,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-05-04T00:32:13.766Z","etag":null,"topics":["anonymous","anonymous-functions","c","closure","closure-library","closures","lambda","lambda-functions","library","pthreads","thread-safe","threadsafe"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Foxbud.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-03-22T13:18:05.000Z","updated_at":"2025-04-25T10:03:03.000Z","dependencies_parsed_at":"2024-11-13T02:41:13.249Z","dependency_job_id":"6597aa55-c871-4f04-abae-5b6f7c54ffbb","html_url":"https://github.com/Foxbud/libcclosure","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/Foxbud/libcclosure","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Foxbud%2Flibcclosure","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Foxbud%2Flibcclosure/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Foxbud%2Flibcclosure/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Foxbud%2Flibcclosure/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Foxbud","download_url":"https://codeload.github.com/Foxbud/libcclosure/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Foxbud%2Flibcclosure/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266608982,"owners_count":23955551,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-23T02:00:09.312Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["anonymous","anonymous-functions","c","closure","closure-library","closures","lambda","lambda-functions","library","pthreads","thread-safe","threadsafe"],"created_at":"2024-11-13T02:40:57.574Z","updated_at":"2025-07-23T03:04:41.671Z","avatar_url":"https://github.com/Foxbud.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# libcclosure\n\n**libcclosure** is a library which adds thread-safe [closures](https://en.wikipedia.org/wiki/Closure_(computer_programming)) as first-class functions to the C language.\n\nThis library is heavily inspired by and intended as a more permissively-licensed alternative to libffcall's [callback](https://www.gnu.org/software/libffcall/callback.html) module. If your project's license permits the use of [GPL 3.0-licensed](https://www.gnu.org/licenses/gpl-3.0.html) software, you should probably use libffcall instead; it has had more rigorous bug testing and supports a wider range of systems and architectures.\n\n## Compatibility\n\n### Supported Operating Systems\n- Linux\n\n### Supported ISAs\n- x86 ([cdecl](https://en.wikipedia.org/wiki/X86_calling_conventions#cdecl))\n- x86_64 ([sys v](https://en.wikipedia.org/wiki/X86_calling_conventions#System_V_AMD64_ABI))\n\n### Supported Multi-Threading Libraries\n- [POSIX Threads](https://en.wikipedia.org/wiki/POSIX_Threads)\n\n## Build and Installation\n\nThis library uses [CMake](https://cmake.org/) to generate its build system.\n\n### Configuration\n\nThe first step is to configure the build system by running the following command:\n\n```\n$ CC=gcc cmake -S . -B build \\\n    -D CMAKE_BUILD_TYPE=Release \\\n    -D BUILD_TESTING=OFF \\\n    -D BUILD_THREADING=ON \\\n    -D BUILD_ARCH=x86_64\n```\n\nSetting the `CC` environment variable is optional and likely unnecessary unless you want to use a compiler other than your user default. The supported compilers are [GCC](https://gcc.gnu.org/), [Clang](https://clang.llvm.org/), and [TCC](https://bellard.org/tcc/).\n\nUnless you you plan to modify libcclosure, itself, you'll likely want to use `Release` for `CMAKE_BUILD_TYPE` and `OFF` for `BUILD_TESTING`.\n\nWhile thread-safety is one of the primary goals of this library, it also involves non-negligible overhead. If you'll be using libcclosure in a single-threaded environment, you can gain a little extra performance by using `OFF` for `BUILD_THREADING` to prevent the inclusion of thread-safety-related system calls.\n\nFinally, choose a target architecture to build the library for by passing it as `BUILD_ARCH`. The supported architectures are `x86` and `x86_64`.\n\n### Build\n\nTo build the library, run:\n\n```\n$ cmake --build build/\n```\n\nThis creates both a static (`libcclosure.a`) and shared (`libcclosure.so`) library.\n\n### Installation\n\n```\n# cmake --build build/ --target install\n```\n\nBy default, this will install the library to `/usr/local`. You can change the installation directory by instead running:\n\n```\n$ cmake -S . -B build -D CMAKE_INSTALL_PREFIX=$HOME/.local\n$ cmake --build build/ --target install\n```\n\nThe header file `cclosure.h` will be installed to `${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}`.\n\nThe library files `libcclosure.a` and `libcclosure.so` will be installed to `${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}`.\n\nImportable cmake scripts which define the targets `CClosure::cclosure_static` and `CClosure::cclosure_shared` will be installed to the directory `${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_CMAKEDIR}/CClosure`.\n\n## Quick Start\n\nThese closures are first-class C functions in the sense that they can accept arbitrary arguments (including variadic) and have an arbitrary return type. To create one, first define a callback function that accepts a special closure \"context\" as its first argument followed by the other desired arguments:\n\n```c\nint Callback(CClosureCtx ctx, double filter, size_t numVArgs, ...) {\n    /* \"ctx.env\" is a pointer to the closure's environment. */\n\n    /* ... */\n}\n```\n\nTo create a closure, you must bind an environment to the callback function (pass `true` as the third argument to `CClosureNew` if the callback returns an [aggregate type](https://gcc.gnu.org/onlinedocs/gcc-3.4.2/gccint/Aggregate-Return.html) rather than a scalar):\n\n```c\nint (*closure)(double, size_t, ...) = CClosureNew(Callback, \u0026someEnv, false);\n```\n\n`CClosureNew` is completely thread-safe assuming that libcclosure was compiled with multi-threading support.\n\n`closure` can now be called like any other C function, and its bound environment will be implicitly passed to it before the arguments it was called with:\n\n```c\nint val0 = closure(15.0, 2, \"some\", \"string\");\nint val1 = closure(8.0, 0);\n```\n\nThe bound closure is thread-safe in the sense that multiple threads may safely call it in parallel and read from its environment. If the closure's callback modifies its environment, however, you must ensure that it does so in a thread-safe manner (like by using a mutex).\n\nUse `CClosureCheck` to determine whether or not a given reference is to a bound closure:\n\n```c\nbool isClosure = CClosureCheck(closure);\n```\n\nRetrieve the environment bound to a closure using `CClosureGetEnv`:\n\n```c\nvoid *env = CClosureGetEnv(closure);\n```\n\nand retrieve the callback function bound to it using `CClosureGetFcn`:\n\n```c\nvoid *fcn = CClosureGetFcn(closure);\n```\n\nUse `CClosureFree` to de-allocate a bound closure:\n\n```c\nvoid *env = CClosureFree(closure);\n```\n\nNote that `CClosureFree` returns the previously-bound environment.\n\n`CClosureFree` is thread-safe in the sense that multiple threads may safely call it (along with `CClosureNew`) in parallel. It is also safe for a closure to free itself and still return as normal. However, there are\nsituations in which calling this function along with others (such as `CClosureGetEnv` and `CClosureGetFcn`)\nin parallel may result in undefined behavior.\n\nTest whether or not libcclosure was compiled with multi-threading support using the `CCLOSURE_THREAD_TYPE` global:\n\n```c\nswitch (CCLOSURE_THREAD_TYPE) {\n    /* Compiled with multi-threading support using POSIX Threads. */\n    case CCLOSURE_THREAD_PTHREADS:\n        break;\n\n    /* Not compiled with any multi-threading support. */\n    case CCLOSURE_THREAD_NONE:\n        break;\n}\n```\n\n## Example\n\nSuppose an external API provides some function that accepts a callback function:\n\n```c\n/* list.h */\n#include \u003cstdbool.h\u003e\n#include \u003cstddef.h\u003e\n\ntypedef void List;\n\nList *ListCreate(size_t num, ...);\nvoid ListForEach(List *list, bool (*callback)(int *element));\n```\n\nFunctions like this typically accept a \"data\" parameter to pass to callback in addition to `element`, but imagine that isn't the case here. That functionality can be recreated using a closure:\n\n```c\n/* main.c */\n#include \u003cstdio.h\u003e\n#include \u003cstdlib.h\u003e\n\n#include \"cclosure.h\"\n#include \"lists.h\"\n\n/* Type of closure environment. */\nstruct SumGreaterThanEnv {\n    int sum;\n    int threshold;\n};\n\n/* Function that accepts closure context as first parameter. */\nstatic bool SumGreaterThanCallback(CClosureCtx ctx, int *element) {\n    /* Closure context contains the bound environment. */\n    struct SumGreaterThanEnv *env = ctx.env;\n\n    if (*element \u003e env-\u003ethreshold)\n        env-\u003esum += *element;\n\n    return true;\n}\n\nint main(void) {\n    List *list = ListCreate(5, 3, -10, 77, 42, 15);\n\n    /* Instantiate an environment for the closure. */\n    struct SumGreaterThanEnv *env = malloc(sizeof(struct SumGreaterThanEnv));\n    *env = (SumGreaterThanEnv){\n        .sum = 0,\n        .threshold = 10\n    };\n\n    /* Create a closure by binding environment to callback. */\n    bool (*callback)(int *) = CClosureNew(SumGreaterThanCallback, env, false);\n\n    /* Callback is now a first-class function that will be passed environment\n       implicitly as its first parameter. */\n    ListForEach(list, callback);\n\n    /* Would print \"134\". */\n    printf(\"%i\", env-\u003esum);\n\n    /* \"CClosureFree\" returns the closure's environment. */\n    free(CClosureFree(callback));\n\n    return 0;\n}\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffoxbud%2Flibcclosure","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffoxbud%2Flibcclosure","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffoxbud%2Flibcclosure/lists"}