{"id":16164727,"url":"https://github.com/aperezdc/autocleanup","last_synced_at":"2025-03-18T23:30:29.598Z","repository":{"id":142776910,"uuid":"127759177","full_name":"aperezdc/autocleanup","owner":"aperezdc","description":"Macros and helper inlines to automatically cleanup scoped resources","archived":false,"fork":false,"pushed_at":"2021-11-09T21:01:44.000Z","size":11,"stargazers_count":8,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-28T12:46:40.913Z","etag":null,"topics":["c","clang","cleanup","gcc","scoped"],"latest_commit_sha":null,"homepage":null,"language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aperezdc.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"COPYING","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":"2018-04-02T13:21:51.000Z","updated_at":"2024-12-04T10:39:42.000Z","dependencies_parsed_at":"2023-04-27T00:04:38.639Z","dependency_job_id":null,"html_url":"https://github.com/aperezdc/autocleanup","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aperezdc%2Fautocleanup","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aperezdc%2Fautocleanup/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aperezdc%2Fautocleanup/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aperezdc%2Fautocleanup/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aperezdc","download_url":"https://codeload.github.com/aperezdc/autocleanup/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243955324,"owners_count":20374369,"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","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":["c","clang","cleanup","gcc","scoped"],"created_at":"2024-10-10T02:47:32.856Z","updated_at":"2025-03-18T23:30:29.593Z","avatar_url":"https://github.com/aperezdc.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"autocleanup\n===========\n\nUtility macros for typesafe RAII-style scoped cleanups for variables.\n\n**Note:** This requires using GCC or Clang.\n\n\nInstallation\n------------\n\nInstall with [clib](https://github.com/clibs/clib):\n\n```sh\nclib install aperezdc/autocleanup --save\n```\n\nOr just copy [autocleanup.h](autocleanup.h) inside your project.\n\n\nUsage\n-----\n\n1. [Defining a new type](#defining-a-new-type)\n2. [Transferring ownership](#transferring-ownership)\n3. [Clearing pointers](#clearing-pointers)\n4. [More idioms](#more-idioms)\n5. [Other pointers](#other-pointers)\n6. [Working with handles](#working-with-handles)\n\n\n### Defining a new type\n\nLet's see how to add automatic cleanup support for an hypothetical `Person`\nopaque type, with its API defined as follows:\n\n```c\ntypedef struct _Person Person;\n\nPerson*     person_new  (const char *name);\nvoid        person_free (Person *person);\nconst char* person_name (Person *person);\n\nPTR_AUTO_DEFINE (Person, person_free)  /* Magic */\n```\n\nNote how the `PTR_AUTO_DEFINE` macro is used to specify that the\n`person_free()` function is to be invoked on instances of `Person*` values.\nTypically the above would be used in the header which defines the type. Code\nwhich uses this type can now rely on values being freed when they go out of\nscope:\n\n```c\nvoid handle_person_named (const char *name) {\n    ptr_auto(Person) person = person_new (name);\n\n    _Bool valid = person_validate (person);\n    if (!valid) return;\n\n    person_handle (person);\n}\n```\n\nIn the function above, the instance pointed by the `person` variable will\nbe automatically freed when the function returns, regardless of whether it\nexecutes till the end or an early return is done.\n\n\n### Transferring ownership\n\nContinuing with the example above, whenever a function wants to return an\ninstance of `Person` to an outer scope, it needs to set the variable which\nholds the pointer to `NULL`, and then return the pointer:\n\n```c\nPerson* person_create_myself (void) {\n    ptr_auto(Person) person = person_new (\"Adrian Perez\");\n\n    person_customize_myself (person);\n\n    Person *return_value = person;  /* Keep around the pointer value, */\n    person = NULL;                  /* avoid the automatic cleanup,   */\n    return return_value;            /* ...and return the pointer      */\n}\n```\n\nBecause the pattern above is so common, the `ptr_steal()` type-safe function\nis provided, which “steals” the pointer from a variable and returns it, and\nis essentially equivalent to the last three lines from the function above.\nThus, it can rewritten as:\n\n```c\nPerson* person_create_myself (void) {\n    ptr_auto(Person) person = person_new (\"Adrian Perez\");\n\n    person_customize_myself (person);\n\n    return ptr_steal (\u0026person);  /* Clears “person”, returns pointer. */\n}\n```\n\nAs a bonus, `ptr_steal()` can be used in any pointer value, regardless of\nwhether it has been declared with `ptr_auto(T)` or not, and it is encouraged\nto use the function to explicitly mark transfers of pointer ownerships.\n\n\n### Clearing pointers\n\nAnother common pattern is to check whether a pointer is `NULL` to determine\nwhether some cleanup needs to be done:\n\n```c\nstatic Person *person = NULL;  /* May be created during execution. */\n\nint main (int argc, char *argv[]) {\n    do_things ();  /* Might assign a non-NULL value to “person”... */\n\n    if (person) {\n        person_free (person);\n        person = NULL;  /* Catch use-after-free situations better. */\n    }\n\n    do_more_things ();\n    return 0;\n}\n```\n\nThe above idiom can be replace with `ptr_clear()`, which behaves exactly\nas the `if` block above:\n\n```c\nint main (int argc, char *argv[]) {\n    do_things ();  /* Might assign a non-NULL value to “person”... */\n\n    ptr_clear (\u0026person, person_free);  /* How convenient is this?  */\n\n    do_more_things ();\n    return 0;\n}\n```\n\n### More idioms\n\nOf course, it is also possible to use `ptr_auto()` and friends in the\nimplementation of `Person` as well. One good pattern to follow is have\nthe functions that create instances use a scoped pointer, and then\n[transfer its ownership](#transferring-ownership) to the caller. This ensures\nthat early returns, e.g. from failures during initialization, will not\nleak the instance being constructed. For example, `person_new()` could\nbe written as:\n\n```c\nPerson* person_new (const char *name) {\n    ptr_auto(Person) p = calloc (1, sizeof Person);\n    p-\u003ename = strdup (name);\n\n    /* More code here that might “return NULL” early on errors.  */\n\n    return ptr_steal (\u0026p);  /* Transfer ownership on completion. */\n}\n```\n\nOf course, following the pattern in the example above needs that calling\n`person_free()` (the cleanup function) on half-initialized instances behaves\nsafely. It is a good practice to first initialize the allocated memory in a\nway that checks can be added in the cleanup function. For example using\n`calloc()` above ensures that the allocated memory is cleared before the\ninitialization continues.\n\nA convenient way of making cleanup functions safe to use on half-initialized\ninstances is to [clear pointers](#clearing-pointers) instead of manually\nwriting all the needed code to do e.g. `NULL`-checks:\n\n```c\nvoid person_free (Person *p) {\n    ptr_clear (\u0026p-\u003ename, free);  /* No-op if “p-\u003ename” is NULL. */\n    free (p);\n}\n```\n\n\n### Other pointers\n\nFor pointers returned by functions which allocate memory using `malloc()`\nby libraries which have not been modified to support automatic cleanups,\nthe `ptr_autofree` macro is provided, which can be assign a generic cleanup\nfunction to the pointer that just calls `free()`.\n\nThis can go a long way to ease usage of string functions provided by the C\nlibrary:\n\n```c\nchar* build_path (const char *progname) {\n    ptr_autofree char *prefix = config_get (\"prefix-path\");\n    ptr_autofree char *cache = xdg_get_cache_path ();\n\n    char *s;\n    return (asprintf (\u0026s, \"%s/%s/%s\", prefix, cache, progname) == -1) ? NULL : s;\n}\n```\n\nBy default the function called for pointers marked with `ptr_autofree` is the\n`free()` function from the C library. This can be overriden by setting the\n`AUTOCLEANUP_FREE_FUNC` macro to the name of a different function at build\ntime before including the header.\n\n\n### Working with handles\n\nAutomatic cleanups for non-pointer variables can be very useful as well, and\nfacilities for working with such types [are provided as well](#handles). The\nprime example is file descriptors, which on Unix-like systems are of type\n`int`.\n\nIn the same way as for pointers, a cleanup function can be associated with\neach handle type, but as many handle types may have the same base type, it may\nbe needed to define a type alias for each type of handle that uses a different\ncleanup function. Continuing wit the file descriptor example, the following\nenables automatically invoking `close()` when they go out of scope, as long\nas they are valid different from `-1` (which we call the “nil value” for the\nhandle type):\n\n```c\ntypedef int FdHandle;\n\n/* Whenever a FdHandle goes out of scope, call close(), unless it's -1. */\nHANDLE_AUTO_DEFINE (FdHandle, close, -1)\n```\n\nNext, use `handle_auto(T)` to declare an scoped handle. With definition\nabove file descriptors are automatically closed at the end of the code\nblock where they are declared, which means file descriptors are never\nleaked in the function below regardless of which branch of the code is\ntaken to return from it:\n\n```c\n_Bool copy_file (const char *path_in, const char *path_out) {\n    handle_auto(FdHandle) fd_in = open (path_in, O_RDONLY, 0);\n    if (fd == -1) return false;\n\n    struct stat sb;\n    if (fstat (fd_in, \u0026sb) == -1) return false;\n\n    handle_auto(FdHandle) fd_out = open (path_out, O_RDWR, 0666);\n    if (fd == -1) return false;\n\n    return splice (fd_in, NULL, fd_out, NULL, sb.st_size, 0) != -1;\n}\n```\n\nConversely, there are also `handle_clear()` and `handle_steal()` functions\nwhich work on handles. Note that it is needed to specify the “nil value”\nfor the handle type when using them. As an example, the following function\nwill automatically close the file descriptor being opened in case of errors,\nand transfer its ownership to the caller on success:\n\n```c\nint open_read_async (const char *path) {\n    handle_auto(FdHandle) fd = open (path, O_RDONLY, 0);\n    if (fd != -1) return -1;\n\n    int flags = fcntl (fd, F_GETFL);\n    if (flags == -1) return  -1;\n\n    flags |= (FD_CLOEXEC | O_ASYNC);\n    if (fcntl (fd, F_SETFL, flags) == -1) return -1;\n\n    return handle_steal (\u0026fd, -1);\n}\n```\n\n\nAPI\n---\n\nNote that `T` below refers to any type valid in the context of the caller.\nThe C language is not polymorphic, yet the implementation is type safe due to\njudicious usage of compiler extensions, preprocessor macros, and inline\nfunctions. Arguments to function-like macros are guaranteed to be evaluated\nonly once and have no undesirable side-effects other than the documented\nbehavior.\n\n\n### Pointers\n\n```c\n#define PTR_AUTO_DEFINE(T, t_ptr_free) /* ... */\n\n#define ptr_auto(T)    /* ... */\n#define ptr_autofree   /* ... */\n\nvoid ptr_clear(T** pp, void (*f)(T*));\nT*   ptr_steal(T** pp);\n```\n\n\n```c\n#ifndef AUTOCLEANUP_FREE_FUNC\n#define AUTOCLEANUP_FREE_FUNC free\n#endif\n\n#define ptr_autofree  /* ... */\n```\n\n\n### Handles\n\n```c\n#define HANDLE_AUTO_DEFINE(T, t_handle_free)  /* ... */\n\n#define handle_auto(T)  /* ... */\n\nvoid handle_clear(T* hp, void (*f)(T*), T nil_value);\nT    handle_steal(T* hp, T nil_value);\n```\n\n\nLicense\n-------\n\nDistributed under the terms of the [MIT/X11\nlicense](https://opensource.org/licenses/MIT)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faperezdc%2Fautocleanup","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faperezdc%2Fautocleanup","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faperezdc%2Fautocleanup/lists"}