{"id":16692902,"url":"https://github.com/seleznevae/ceraii","last_synced_at":"2025-04-10T01:15:03.588Z","repository":{"id":93769011,"uuid":"112828959","full_name":"seleznevae/ceraii","owner":"seleznevae","description":"C MACROS for go-like defer in C which can be used for explicit RAII","archived":false,"fork":false,"pushed_at":"2020-02-08T07:11:33.000Z","size":55,"stargazers_count":33,"open_issues_count":1,"forks_count":3,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-10T01:14:55.959Z","etag":null,"topics":["c","defer","macros","raii"],"latest_commit_sha":null,"homepage":"","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/seleznevae.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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":"2017-12-02T09:18:08.000Z","updated_at":"2025-03-26T21:31:09.000Z","dependencies_parsed_at":"2023-03-05T11:15:24.708Z","dependency_job_id":null,"html_url":"https://github.com/seleznevae/ceraii","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/seleznevae%2Fceraii","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seleznevae%2Fceraii/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seleznevae%2Fceraii/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seleznevae%2Fceraii/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/seleznevae","download_url":"https://codeload.github.com/seleznevae/ceraii/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248137891,"owners_count":21053775,"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","defer","macros","raii"],"created_at":"2024-10-12T16:28:46.727Z","updated_at":"2025-04-10T01:15:03.580Z","avatar_url":"https://github.com/seleznevae.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ceraii (C Explicit RAII)\n[![Build Status](https://travis-ci.org/seleznevae/ceraii.svg?branch=master)](https://travis-ci.org/seleznevae/ceraii)\n[![Build status](https://ci.appveyor.com/api/projects/status/9lm1crskqmui1uvw?svg=true)](https://ci.appveyor.com/project/seleznevae/ceraii)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![Coverage Status](https://coveralls.io/repos/github/seleznevae/ceraii/badge.svg?branch=master)](https://coveralls.io/github/seleznevae/ceraii?branch=master)\n\nIn software projects written in C there's always a lot of boiler plate code for releasing allocated resources. \nThere are two main ways to deal with these problems in different programming languages:\n1. _Finally_ blocks \n2. RAII\n3. Deferred functions\n\nThe goal of **CERAII** is primarily to implement deferred functions as they seems to be closer to C style programming. With deferred functions it should be obvious for anybody who reads the code how resources are released. \n\nExample of code with CERAII:\n\n```C\n#include \u003cmalloc.h\u003e\n#include \"ceraii.h\"\n\nint func()\n{\n    int *pi = (int*)malloc(sizeof(int));\n    DO_AT_EXIT(free(pi));  /* Explicit declaration of what should be done before exit from the function */\n    \n    if (pi == NULL) \n        RETURN(1);\n\n    /* some code with multiple returns */\n    \n    if (some_condition) {\n        /* --- */\n        RETURN(2);\n    } else if (another_condition) {\n        /* --- */\n        RETURN(3);\n    }\n\n    RETURN(0);\n}\n```\nNo matter how many returns you have in your code, statements in  **DO_AT_EXIT** macro will be automatically done before returning from the function. All you need is 2 macros: \n- **DO_AT_EXIT** to declare actions before return from the function\n- **RETURN** macros instead of **return** key word \n\n# Differences with **\"defer\"** in golang\n\nIn **go** a deferred function's arguments are evaluated when the defer statement is evaluated. Therefore in **go** the deferred call will print \"0\" after the function returns:\n```go\nfunc a() {\n    i := 0\n    defer fmt.Println(i)\n    i++\n    return\n}\n```\nExpressions in **DO_AT_EXIT** are evaluated at the moment of **RETURN**. Therefore this function will print \"1\" after the function returns:\n```C\nvoid not_like_go()\n{\n    volatile int i = 0;\n    DO_AT_EXIT(  printf(\"%d\\n\", i);   );\n    \n    i++;\n    RETURN();\n}\n```\n\n# Macros usage\n\n**DO_AT_EXIT** and **RETURN** can be used freely in conditional clauses, switch statements, recursive functions:\n\n```C\n#include \u003cstdio.h\u003e\n#include \"ceraii.h\"\n\nint factorial(int value)\n{\n    printf(\" Factorial(%d) begin\\n\", value);\n    DO_AT_EXIT(printf(\" Factorial(%d) end\\n\", value););\n\n    printf(\"   Factorial body\\n\");\n    if (value == 0 || value == 1)\n        RETURN(1);\n\n    RETURN(value * factorial(value - 1));\n}\n\nint conditional(int value)\n{\n    if (value) {\n        DO_AT_EXIT(printf(\" Condition was true\\n\"););\n    } else {\n        DO_AT_EXIT(printf(\" Condition was false\\n\"););\n    }\n\n    printf(\"   Conditional function body\\n\");\n    RETURN(1);\n}\n\nint switch_case(int value)\n{\n    switch (value) {\n        case 1: DO_AT_EXIT(printf(\" Value = 1\\n\");); break;\n        case 2: DO_AT_EXIT(printf(\" Value = 2\\n\");); break;\n        default: DO_AT_EXIT(printf(\" Value is default\\n\");); break;\n    }\n\n    printf(\"   Switch function body\\n\");\n    RETURN(1);\n}\n\nint main()\n{\n    printf(\"===== Example of recursive function with CERAII =====\\n\");\n    factorial(3);\n\n    printf(\"\\n===== Example of CERAII macros in conditional statements =====\\n\");\n    conditional(1);\n\n    printf(\"\\n===== Example of CERAII macros in switch statements =====\\n\");\n    switch_case(3);\n    switch_case(1);\n\n    RETURN(0);\n}\n```\nOutput is:\n```\n===== Example of recursive function with CERAII =====\n Factorial(3) begin\n   Factorial body\n Factorial(2) begin\n   Factorial body\n Factorial(1) begin\n   Factorial body\n Factorial(1) end\n Factorial(2) end\n Factorial(3) end\n\n===== Example of CERAII macros in conditional statements =====\n   Conditional function body\n Condition was true\n\n===== Example of CERAII macros in switch statements =====\n   Switch function body\n Value is default\n   Switch function body\n Value = 1\n```\n# Installation\n\nTo use **DO_AT_SCOPE_EXIT** and **RETURN** macro include _ceraii.h_ in your source code. Also it necessary to compile _ceraii.c_ and link with other object files during linkage process.\n\n# Pitfalls\n**CERAII** implementation is based on C long jumps. Therefore you should keep in mind:\n1. Variables used inside **DO_AT_SCOPE_EXIT** macro shouldn't be changed from this macro usage till **RETURN** macro. If they are changed they should be declared volatile, otherwise their values will be indeterminate at the moment of execution before returning from the function. Also if when **DO_AT_SCOPE_EXIT** was called, a VLA (Variable-length array) or another variably-modified type variable was in scope and control left that scope, then it will be undefined behavior. See http://en.cppreference.com/w/c/program/setjmp for details. Examples of correct usage:\n```C\n/* No need to declare pi volatile. Because it is not modified after DO_AT_EXIT */\nint *pi = (int*)malloc(sizeof(int));\nDO_AT_EXIT(free(pi));\n\n/* Some code that doesn't modify pi. */\n*pi = 3;\n*pi += *pi;\n...\n\n/* ps should be defined as volatile because lately it would be changed */\nvolatile short *ps = (short*)malloc(sizeof(short)); \nDO_AT_EXIT(free(ps));\n\nfree(ps);\n*ps = (short*)malloc(sizeof(short));\n\nRETURN();\n```\n\n# Defining custom macros\n\n**DO_AT_EXIT** is so verbose on purpose so that it clear what it does. However if your prefer more fashionable names (like \"defer\") you can wrap **CERAII** and redefined them. Moreover for the most frequent cases of resource releasing it is preferred to define your own macros based on DO_AT_EXIT. \nHere are some examples:\n```C\n#include \u003cmalloc.h\u003e\n#include \"ceraii.h\"\n\n/* Automatically free pointers allocated with malloc, calloc, realloc at exit*/\n#define FREE_AT_EXIT(pointer) \\\n    DO_AT_EXIT(free(pointer);)\n    \n/* Automatically unlock pthread mutex at exit*/\n#define UNLOCK_AT_EXIT(mutex) \\\n    DO_AT_EXIT(pthread_mutex_unlock(mutex);)\n    \n/* Automatically close file at exit*/\n#define CLOSE_AT_EXIT(file_p) \\\n    DO_AT_EXIT(fclose(file_p);)\n    \nint func()\n{\n    int *pi = (int*)malloc(sizeof(int));\n    FREE_AT_EXIT(pi);  \n    \n    if (pi == NULL) \n        RETURN(1);\n   \n    if (some_condition) {\n        /* --- */\n        RETURN(2);\n    }\n\n    RETURN(0);\n}\n```\nAfter that it will be much easier to maintain code. For example if it is needed to log all invocations of free at the end of the functions, all you need is to add _printf_ to the _FREE_AT_EXIT_ definition:\n```C\n#define FREE_AT_EXIT(pointer) \\\n    DO_AT_EXIT(\\\n        printf(\"Pointer %p is being freed\\n\", pointer);\n        free(pointer);\\\n    )\\\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseleznevae%2Fceraii","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fseleznevae%2Fceraii","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseleznevae%2Fceraii/lists"}