├── .github ├── FUNDING.yml └── workflows │ ├── release.yml │ ├── sonar.yml │ ├── build.yml │ └── docs.yml ├── docs ├── example.png ├── octicons │ ├── check-circle-fill-16.svg │ ├── info-16.svg │ ├── check-circle-16.svg │ ├── alert-16.svg │ ├── stop-16.svg │ ├── trash-16.svg │ ├── checkbox-16.svg │ ├── report-16.svg │ ├── eye-16.svg │ ├── light-bulb-16.svg │ └── bug-16.svg ├── footer.html ├── icon.svg ├── logo.svg ├── exceptions4c.css ├── doxygen-awesome-sidebar-only.css ├── exceptions4c.svg ├── Doxyfile └── README.md ├── exceptions4c.pc.in ├── .editorconfig ├── clib.json ├── .codecov.yml ├── NOTICE ├── sonar-project.properties ├── .gitattributes ├── tests ├── throw-uncaught-1.c ├── panic-dangling.c ├── throw-uncaught-2.c ├── panic-try.c ├── handler-terminate.c ├── throw-format.c ├── catch-duplicate.c ├── panic-retry.c ├── panic-reacquire.c ├── handler-uncaught.c ├── catch-unordered.c ├── catch-all.c ├── catch-sigint.c ├── catch-sigterm.c ├── panic-context.c ├── handler-finalize.c ├── handler-initialize.c ├── panic-block-try.c ├── panic-block-next.c ├── retry.c ├── panic-block-catch.c ├── catch-sigsegv.c ├── is-uncaught.c ├── catch-specific.c ├── get-exception.c ├── reacquire.c ├── throw-cause.c ├── throw-suppressed.c ├── catch-generic.c ├── finally.c ├── with-use.c └── testing.h ├── examples ├── uncaught-handler.c ├── signals.c ├── pthreads.c ├── customization.c └── pet-store.c ├── configure.ac ├── README.md ├── .gitignore ├── CHANGELOG.md ├── Makefile.am ├── LICENSE └── src └── exceptions4c.c /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | 2 | github: LeakyAbstractions 3 | ko_fi: guillermocalvo 4 | -------------------------------------------------------------------------------- /docs/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guillermocalvo/exceptions4c/HEAD/docs/example.png -------------------------------------------------------------------------------- /exceptions4c.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | libdir=@libdir@ 4 | includedir=${prefix}/include 5 | 6 | Name: exceptions4c 7 | Description: Exceptions for C 8 | Version: @VERSION@ 9 | Libs: -L${libdir} -llibexceptions4c 10 | Cflags: -I${includedir} 11 | -------------------------------------------------------------------------------- /docs/octicons/check-circle-fill-16.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org/ 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | indent_style = space 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | max_line_length = 120 12 | 13 | [*.md] 14 | # Preserve trailing whitespace for Markdown files 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /clib.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "exceptions4c", 3 | "version": "4.0.0", 4 | "description": "Exceptions for C", 5 | "repo": "guillermocalvo/exceptions4c", 6 | "license": "Apache-2.0", 7 | "src": [ 8 | "src/exceptions4c.c", 9 | "src/exceptions4c.h" 10 | ], 11 | "keywords": [ 12 | "exceptions", 13 | "error-handling" 14 | ] 15 | } -------------------------------------------------------------------------------- /docs/octicons/info-16.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/octicons/check-circle-16.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/octicons/alert-16.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/octicons/stop-16.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/octicons/trash-16.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/octicons/checkbox-16.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.codecov.yml: -------------------------------------------------------------------------------- 1 | 2 | codecov: 3 | notify: 4 | require_ci_to_pass: yes 5 | 6 | coverage: 7 | precision: 4 8 | round: down 9 | range: "80...98" 10 | status: 11 | project: yes 12 | patch: yes 13 | changes: no 14 | 15 | parsers: 16 | gcov: 17 | branch_detection: 18 | conditional: yes 19 | loop: yes 20 | method: no 21 | macro: no 22 | 23 | comment: 24 | layout: "header, diff" 25 | behavior: default 26 | require_changes: no 27 | 28 | ignore: 29 | - "tests/*" 30 | - "examples/*" 31 | -------------------------------------------------------------------------------- /docs/octicons/report-16.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2025 Guillermo Calvo 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.projectKey=guillermocalvo_exceptions4c 2 | sonar.organization=guillermocalvo 3 | sonar.projectName=exceptions4c 4 | sonar.projectVersion=4.0.0 5 | sonar.description=Exceptions for C 6 | sonar.links.homepage=https://exceptions4c.guillermo.dev/ 7 | sonar.links.scm=https://github.com/guillermocalvo/exceptions4c 8 | sonar.links.ci=https://github.com/guillermocalvo/exceptions4c/actions 9 | sonar.links.issue=https://github.com/guillermocalvo/exceptions4c/issues 10 | sonar.sourceEncoding=UTF-8 11 | sonar.sources=src,tests,examples 12 | sonar.exclusions=examples/pthreads.c,examples/customization.c 13 | #sonar.inclusions= 14 | #sonar.tests= 15 | #sonar.test.exclusions= 16 | #sonar.test.inclusions= 17 | sonar.verbose=true 18 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set the default behavior that will override individual core.autocrlf setting 2 | * text eol=lf 3 | 4 | # Text files that will always be normalized on checkout 5 | *.config text 6 | *.editorconfig text 7 | *.gradle text 8 | *.groovy text 9 | *.java text 10 | *.js text 11 | *.json text 12 | *.md text 13 | *.properties text 14 | *.py text 15 | *.sh text 16 | *.sql text 17 | *.toml text 18 | *.txt text 19 | *.xml text 20 | *.yml text 21 | *.yaml text 22 | *.Jenkinsfile text 23 | 24 | # Files that will always have CRLF line endings on checkout 25 | *.bat text eol=crlf 26 | 27 | # Files that are truly binary and should not be modified on checkout 28 | *.bin binary 29 | *.class binary 30 | *.jar binary 31 | *.png binary 32 | *.mp3 binary 33 | -------------------------------------------------------------------------------- /docs/octicons/eye-16.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/octicons/light-bulb-16.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | 2 | # Publish a new Release 3 | name: Release 4 | 5 | on: 6 | push: 7 | tags: ['[0-9]+.[0-9]+.[0-9]+*'] 8 | 9 | jobs: 10 | publish: 11 | 12 | name: Publish 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | 17 | # ================================ 18 | # CHECKOUT 19 | # ================================ 20 | - name: Checkout git tag 21 | uses: actions/checkout@v4 22 | 23 | # ================================ 24 | # CREATE RELEASE 25 | # ================================ 26 | - name: Create Release 27 | uses: softprops/action-gh-release@v2 28 | with: 29 | preserve_order: true 30 | files: | 31 | src/exceptions4c.h 32 | src/exceptions4c.c 33 | LICENSE 34 | NOTICE 35 | CHANGELOG.md 36 | -------------------------------------------------------------------------------- /docs/footer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /docs/octicons/bug-16.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/throw-uncaught-1.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Guillermo Calvo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include "testing.h" 19 | 20 | static const struct e4c_exception_type OOPS = {NULL, "Oops"}; 21 | 22 | /** 23 | * Tests macro THROW with no CATCH block. 24 | */ 25 | int main(void) { 26 | THROW(OOPS, NULL); 27 | 28 | TEST_PRINT_ERR("Reached %s:%d\n", __FILE__, __LINE__); 29 | TEST_PASS; 30 | } 31 | -------------------------------------------------------------------------------- /tests/panic-dangling.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Guillermo Calvo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include "testing.h" 20 | 21 | static void failure(int); 22 | 23 | /** 24 | * Force library panic due to dangling context. 25 | */ 26 | int main(void) { 27 | signal(SIGABRT, failure); 28 | 29 | TRY { 30 | goto oops; 31 | } 32 | 33 | oops: 34 | TEST_PASS; 35 | } 36 | 37 | static void failure(int _) { 38 | (void) _; 39 | TEST_FAIL("Handled SIGABORT %s:%d\n", __FILE__, __LINE__); 40 | } 41 | -------------------------------------------------------------------------------- /examples/uncaught-handler.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Guillermo Calvo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | const struct e4c_exception_type MY_ERROR = {NULL, "My error"}; 22 | 23 | //! [uncaught_handler] 24 | static void my_uncaught_handler(const struct e4c_exception * exception) { 25 | fprintf(stderr, "UNCAUGHT: %s\n", exception->message); 26 | } 27 | 28 | int main(int argc, char * argv[]) { 29 | e4c_get_context()->uncaught_handler = my_uncaught_handler; 30 | THROW(MY_ERROR, "Oops"); 31 | } 32 | //! [uncaught_handler] 33 | -------------------------------------------------------------------------------- /tests/throw-uncaught-2.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Guillermo Calvo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifdef NDEBUG 18 | #undef NDEBUG 19 | #else 20 | #define NDEBUG 21 | #endif 22 | 23 | #include 24 | #include "testing.h" 25 | 26 | static const struct e4c_exception_type OOPS = {NULL, "Oops"}; 27 | 28 | /** 29 | * Tests macro THROW inside a CATCH block. 30 | */ 31 | int main(void) { 32 | TRY { 33 | THROW(OOPS, "Root cause"); 34 | } CATCH(OOPS) { 35 | THROW(OOPS, NULL); 36 | } 37 | 38 | TEST_PRINT_ERR("Reached %s:%d\n", __FILE__, __LINE__); 39 | TEST_PASS; 40 | } 41 | -------------------------------------------------------------------------------- /tests/panic-try.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Guillermo Calvo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include "testing.h" 20 | 21 | static void failure(int); 22 | 23 | /** 24 | * Force library panic due to TRY without exception block. 25 | */ 26 | int main(void) { 27 | signal(SIGABRT, failure); 28 | 29 | (void) e4c_try(EXCEPTIONS4C_DEBUG); 30 | 31 | TEST_PRINT_ERR("Reached %s:%d\n", __FILE__, __LINE__); 32 | TEST_PASS; 33 | } 34 | 35 | static void failure(int _) { 36 | (void) _; 37 | TEST_FAIL("Handled SIGABORT %s:%d\n", __FILE__, __LINE__); 38 | } 39 | -------------------------------------------------------------------------------- /tests/handler-terminate.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Guillermo Calvo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include "testing.h" 19 | 20 | static void custom_termination_handler(void); 21 | 22 | static const struct e4c_exception_type OOPS = {NULL, "Oops"}; 23 | 24 | /** 25 | * Tests that termination handlers are called. 26 | */ 27 | int main(void) { 28 | e4c_get_context()->termination_handler = &custom_termination_handler; 29 | THROW(OOPS, NULL); 30 | } 31 | 32 | static void custom_termination_handler(void) { 33 | TEST_PRINT_OUT("Termination handler %s:%d\n", __FILE__, __LINE__); 34 | TEST_PASS; 35 | } 36 | -------------------------------------------------------------------------------- /tests/throw-format.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Guillermo Calvo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include "testing.h" 19 | 20 | static const struct e4c_exception_type OOPS = {NULL, "Oops"}; 21 | 22 | /** 23 | * Tests macro THROW with a formatted message. 24 | */ 25 | int main(void) { 26 | volatile bool caught = false; /* NOSONAR */ 27 | 28 | TRY { 29 | THROW(OOPS, "%s_%s", "FORMATTED", "MESSAGE"); 30 | } CATCH (OOPS) { 31 | TEST_ASSERT_STR_EQUALS(e4c_get_exception()->message, "FORMATTED_MESSAGE"); 32 | caught = true; 33 | } 34 | TEST_ASSERT(caught); 35 | TEST_PASS; 36 | } 37 | -------------------------------------------------------------------------------- /tests/catch-duplicate.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Guillermo Calvo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include "testing.h" 19 | 20 | static const struct e4c_exception_type OOPS = {NULL, "Oops"}; 21 | 22 | /** 23 | * Duplicate CATCH blocks should have no effect. 24 | */ 25 | int main(void) { 26 | volatile bool caught1 = false, caught2 = false; /* NOSONAR */ 27 | 28 | TRY { 29 | THROW(OOPS, NULL); 30 | } CATCH (OOPS) { 31 | caught1 = true; 32 | } CATCH (OOPS) { 33 | caught2 = true; 34 | } 35 | 36 | TEST_ASSERT(caught1); 37 | TEST_ASSERT(!caught2); 38 | TEST_PASS; 39 | } 40 | -------------------------------------------------------------------------------- /tests/panic-retry.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Guillermo Calvo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include "testing.h" 20 | 21 | static void failure(int); 22 | 23 | static const struct e4c_exception_type OOPS = {NULL, "Oops"}; 24 | 25 | /** 26 | * Force library panic due to RETRY without TRY block. 27 | */ 28 | int main(void) { 29 | signal(SIGABRT, failure); 30 | 31 | RETRY(100, OOPS, NULL); 32 | 33 | TEST_PRINT_ERR("Reached %s:%d\n", __FILE__, __LINE__); 34 | TEST_PASS; 35 | } 36 | 37 | static void failure(int _) { 38 | (void) _; 39 | TEST_FAIL("Handled SIGABORT %s:%d\n", __FILE__, __LINE__); 40 | } 41 | -------------------------------------------------------------------------------- /examples/signals.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Guillermo Calvo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | //! [null_pointer] 18 | #include 19 | #include 20 | #include 21 | 22 | const struct e4c_exception_type SEGFAULT = {NULL, "Segmentation fault"}; 23 | 24 | void segfault(int sigsegv) { 25 | signal(sigsegv, segfault); 26 | THROW(SEGFAULT, NULL); 27 | } 28 | 29 | int main(void) { 30 | const int * null_pointer = NULL; 31 | 32 | signal(SIGSEGV, segfault); 33 | 34 | TRY { 35 | printf("Oh no %d", *null_pointer); 36 | } CATCH (SEGFAULT) { 37 | printf("Danger avoided!\n"); 38 | } 39 | 40 | return EXIT_SUCCESS; 41 | } 42 | //! [null_pointer] 43 | -------------------------------------------------------------------------------- /tests/panic-reacquire.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Guillermo Calvo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include "testing.h" 20 | 21 | static void failure(int); 22 | 23 | static const struct e4c_exception_type OOPS = {NULL, "Oops"}; 24 | 25 | /** 26 | * Force library panic due to REACQUIRE without WITH block. 27 | */ 28 | int main(void) { 29 | signal(SIGABRT, failure); 30 | 31 | REACQUIRE(100, OOPS, NULL); 32 | 33 | TEST_PRINT_ERR("Reached %s:%d\n", __FILE__, __LINE__); 34 | TEST_PASS; 35 | } 36 | 37 | static void failure(int _) { 38 | (void) _; 39 | TEST_FAIL("Handled SIGABORT %s:%d\n", __FILE__, __LINE__); 40 | } 41 | -------------------------------------------------------------------------------- /tests/handler-uncaught.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Guillermo Calvo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include "testing.h" 19 | 20 | static void custom_uncaught_handler(const struct e4c_exception *); 21 | 22 | static const struct e4c_exception_type OOPS = {NULL, "Oops"}; 23 | 24 | /** 25 | * Tests that uncaught handlers are called. 26 | */ 27 | int main(void) { 28 | e4c_get_context()->uncaught_handler = &custom_uncaught_handler; 29 | THROW(OOPS, NULL); 30 | } 31 | 32 | static void custom_uncaught_handler(const struct e4c_exception *exception) { 33 | (void) exception; 34 | TEST_PRINT_OUT("Uncaught handler %s:%d\n", __FILE__, __LINE__); 35 | TEST_PASS; 36 | } 37 | -------------------------------------------------------------------------------- /tests/catch-unordered.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Guillermo Calvo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include "testing.h" 19 | 20 | static const struct e4c_exception_type GENERIC = {NULL, "Generic error"}; 21 | static const struct e4c_exception_type SPECIFIC = {&GENERIC, "Specific error"}; 22 | 23 | /** 24 | * Specific CATCH blocks should go before generic ones. 25 | */ 26 | int main(void) { 27 | volatile bool caught1 = false, caught2 = false; /* NOSONAR */ 28 | TRY { 29 | THROW(SPECIFIC, NULL); 30 | } CATCH (GENERIC) { 31 | caught1 = true; 32 | } CATCH (SPECIFIC) { 33 | caught2 = true; 34 | } 35 | TEST_ASSERT(caught1); 36 | TEST_ASSERT(!caught2); 37 | TEST_PASS; 38 | } 39 | -------------------------------------------------------------------------------- /tests/catch-all.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Guillermo Calvo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include "testing.h" 19 | 20 | static const struct e4c_exception_type OOPS = {NULL, "Oops"}; 21 | 22 | /** 23 | * Tests macro CATCH_ALL. 24 | */ 25 | int main(void) { 26 | volatile bool caught1 = false, caught2 = false; /* NOSONAR */ 27 | 28 | TRY { 29 | THROW(OOPS, NULL); 30 | } CATCH_ALL { 31 | caught1 = true; 32 | } 33 | 34 | TEST_ASSERT(caught1); 35 | 36 | TRY { 37 | const struct e4c_exception_type *NULL_EXCEPTION_TYPE = NULL; 38 | THROW(*NULL_EXCEPTION_TYPE, NULL); 39 | } CATCH_ALL { 40 | caught2 = true; 41 | } 42 | 43 | TEST_ASSERT(caught2); 44 | TEST_PASS; 45 | } 46 | -------------------------------------------------------------------------------- /tests/catch-sigint.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Guillermo Calvo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include "testing.h" 20 | 21 | static const struct e4c_exception_type USERINT = {NULL, "User interruption"}; 22 | 23 | static void throw_on_signal(int); 24 | 25 | /** 26 | * Tests that signal SIGINT can be converted into a exception. 27 | */ 28 | int main(void) { 29 | volatile bool caught = false; /* NOSONAR */ 30 | 31 | signal(SIGINT, throw_on_signal); 32 | 33 | TRY { 34 | raise(SIGINT); 35 | TEST_FAIL("Reached %s:%d\n", __FILE__, __LINE__); 36 | } CATCH (USERINT) { 37 | caught = true; 38 | } 39 | 40 | TEST_ASSERT(caught); 41 | TEST_PASS; 42 | } 43 | 44 | static void throw_on_signal(int _) { 45 | (void) _; 46 | THROW(USERINT, NULL); 47 | } 48 | -------------------------------------------------------------------------------- /tests/catch-sigterm.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Guillermo Calvo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include "testing.h" 20 | 21 | static const struct e4c_exception_type TERMINATE = {NULL, "Termination requested"}; 22 | 23 | static void throw_on_signal(int); 24 | 25 | /** 26 | * Tests that signal SIGTERM can be converted into a exception. 27 | */ 28 | int main(void) { 29 | volatile bool caught = false; /* NOSONAR */ 30 | 31 | signal(SIGTERM, throw_on_signal); 32 | 33 | TRY { 34 | raise(SIGTERM); 35 | TEST_FAIL("Reached %s:%d\n", __FILE__, __LINE__); 36 | } CATCH (TERMINATE) { 37 | caught = true; 38 | } 39 | 40 | TEST_ASSERT(caught); 41 | TEST_PASS; 42 | } 43 | 44 | static void throw_on_signal(int _) { 45 | (void) _; 46 | THROW(TERMINATE, NULL); 47 | } 48 | -------------------------------------------------------------------------------- /tests/panic-context.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Guillermo Calvo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include "testing.h" 20 | 21 | static void failure(int); 22 | 23 | static struct e4c_context *my_supplier(void); 24 | 25 | /** 26 | * Force library panic due to null context. 27 | */ 28 | int main(void) { 29 | signal(SIGABRT, failure); 30 | 31 | e4c_set_context_supplier(&my_supplier); 32 | 33 | TRY { 34 | TEST_PRINT_ERR("Reached %s:%d\n", __FILE__, __LINE__); 35 | TEST_PASS; 36 | } 37 | 38 | TEST_PRINT_ERR("Reached %s:%d\n", __FILE__, __LINE__); 39 | TEST_PASS; 40 | } 41 | 42 | static struct e4c_context *my_supplier(void) { 43 | return NULL; 44 | } 45 | 46 | static void failure(int _) { 47 | (void) _; 48 | TEST_FAIL("Handled SIGABORT %s:%d\n", __FILE__, __LINE__); 49 | } 50 | -------------------------------------------------------------------------------- /docs/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /tests/handler-finalize.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Guillermo Calvo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include "testing.h" 19 | 20 | static void custom_finalizer(const struct e4c_exception *); 21 | 22 | static volatile bool custom_handler_was_finalized = false; 23 | static const struct e4c_exception_type OOPS = {NULL, "Oops"}; 24 | 25 | /** 26 | * Tests that exception finalizers are called. 27 | */ 28 | int main(void) { 29 | e4c_get_context()->finalize_exception = &custom_finalizer; 30 | 31 | TRY { 32 | THROW(OOPS, NULL); 33 | } CATCH (OOPS) { 34 | TEST_PRINT_OUT("Exception caught"); 35 | } 36 | 37 | TEST_ASSERT(custom_handler_was_finalized); 38 | TEST_PASS; 39 | } 40 | 41 | static void custom_finalizer(const struct e4c_exception *exception) { 42 | (void) exception; 43 | custom_handler_was_finalized = true; 44 | } 45 | -------------------------------------------------------------------------------- /tests/handler-initialize.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Guillermo Calvo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include "testing.h" 19 | 20 | static void custom_initializer(struct e4c_exception *exception); 21 | 22 | static int my_custom_data = 123; 23 | static const struct e4c_exception_type OOPS = {NULL, "Oops"}; 24 | 25 | /** 26 | * Tests that exception initializers are called. 27 | */ 28 | int main(void) { 29 | int *data = NULL; 30 | e4c_get_context()->initialize_exception = &custom_initializer; 31 | 32 | TRY { 33 | THROW(OOPS, NULL); 34 | } CATCH (OOPS) { 35 | data = e4c_get_exception()->data; 36 | } 37 | 38 | TEST_ASSERT_NOT_NULL(data); 39 | TEST_ASSERT_PTR_EQUALS(data, &my_custom_data); 40 | TEST_PASS; 41 | } 42 | 43 | static void custom_initializer(struct e4c_exception *exception) { 44 | exception->data = &my_custom_data; 45 | } 46 | -------------------------------------------------------------------------------- /tests/panic-block-try.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Guillermo Calvo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include "testing.h" 20 | 21 | static void failure(int); 22 | 23 | static struct e4c_context *my_supplier(void); 24 | 25 | static struct e4c_context my_context = {0}; 26 | 27 | /** 28 | * Force library panic due to null block on TRY block. 29 | */ 30 | int main(void) { 31 | signal(SIGABRT, failure); 32 | 33 | e4c_set_context_supplier(&my_supplier); 34 | 35 | (void) e4c_try(EXCEPTIONS4C_DEBUG); 36 | 37 | TEST_PRINT_ERR("Reached %s:%d\n", __FILE__, __LINE__); 38 | TEST_PASS; 39 | } 40 | 41 | static struct e4c_context *my_supplier(void) { 42 | free(my_context._innermost_block); 43 | my_context._innermost_block = NULL; 44 | return &my_context; 45 | } 46 | 47 | static void failure(int _) { 48 | (void) _; 49 | TEST_FAIL("Handled SIGABORT %s:%d\n", __FILE__, __LINE__); 50 | } 51 | -------------------------------------------------------------------------------- /tests/panic-block-next.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Guillermo Calvo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include "testing.h" 20 | 21 | static void failure(int); 22 | 23 | static struct e4c_context *my_supplier(void); 24 | 25 | static struct e4c_context my_context = {0}; 26 | 27 | /** 28 | * Force library panic due to null block on next stage. 29 | */ 30 | int main(void) { 31 | signal(SIGABRT, failure); 32 | 33 | e4c_set_context_supplier(&my_supplier); 34 | 35 | (void) e4c_next(EXCEPTIONS4C_DEBUG); 36 | 37 | TEST_PRINT_ERR("Reached %s:%d\n", __FILE__, __LINE__); 38 | TEST_PASS; 39 | } 40 | 41 | static struct e4c_context *my_supplier(void) { 42 | free(my_context._innermost_block); 43 | my_context._innermost_block = NULL; 44 | return &my_context; 45 | } 46 | 47 | static void failure(int _) { 48 | (void) _; 49 | TEST_FAIL("Handled SIGABORT %s:%d\n", __FILE__, __LINE__); 50 | } 51 | -------------------------------------------------------------------------------- /tests/retry.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Guillermo Calvo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include "testing.h" 19 | 20 | static const struct e4c_exception_type OOPS = {NULL, "Oops"}; 21 | static const struct e4c_exception_type GIVE_UP = {NULL, "Giving up"}; 22 | 23 | /** 24 | * Tests macro RETRY. 25 | */ 26 | int main(void) { 27 | volatile int total_tries = 0; /* NOSONAR */ 28 | TRY { 29 | TRY { 30 | total_tries++; 31 | if (total_tries == 1) { 32 | TEST_PRINT_OUT("First try\n"); 33 | } else { 34 | TEST_PRINT_OUT("retries: %d\n", total_tries - 1); 35 | } 36 | THROW(OOPS, NULL); 37 | } CATCH (OOPS) { 38 | RETRY(2, GIVE_UP, NULL); 39 | } 40 | } CATCH (GIVE_UP) { 41 | TEST_PRINT_OUT("total_tries: %d\n", total_tries); 42 | } 43 | TEST_ASSERT_INT_EQUALS(total_tries, 3); 44 | TEST_PASS; 45 | } 46 | -------------------------------------------------------------------------------- /tests/panic-block-catch.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Guillermo Calvo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include "testing.h" 20 | 21 | static void failure(int); 22 | 23 | static struct e4c_context *my_supplier(void); 24 | 25 | static struct e4c_context my_context = {0}; 26 | 27 | /** 28 | * Force library panic due to null block on CATCH block. 29 | */ 30 | int main(void) { 31 | signal(SIGABRT, failure); 32 | 33 | e4c_set_context_supplier(&my_supplier); 34 | 35 | (void) e4c_catch(NULL, EXCEPTIONS4C_DEBUG); 36 | 37 | TEST_PRINT_ERR("Reached %s:%d\n", __FILE__, __LINE__); 38 | TEST_PASS; 39 | } 40 | 41 | static struct e4c_context *my_supplier(void) { 42 | free(my_context._innermost_block); 43 | my_context._innermost_block = NULL; 44 | return &my_context; 45 | } 46 | 47 | static void failure(int _) { 48 | (void) _; 49 | TEST_FAIL("Handled SIGABORT %s:%d\n", __FILE__, __LINE__); 50 | } 51 | -------------------------------------------------------------------------------- /tests/catch-sigsegv.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Guillermo Calvo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include "testing.h" 20 | 21 | static void *null(int); 22 | 23 | static void throw_on_signal(int); 24 | 25 | static const struct e4c_exception_type SEGFAULT = {NULL, "Segmentation fault"}; 26 | 27 | static int integer = 123; 28 | 29 | /** 30 | * Tests that signal SIGSEGV can be converted into a exception. 31 | */ 32 | int main(void) { 33 | volatile bool caught = false; /* NOSONAR */ 34 | 35 | signal(SIGSEGV, throw_on_signal); 36 | 37 | TRY { 38 | const int *pointer = null(integer); 39 | integer = *pointer; 40 | TEST_FAIL("Reached %s:%d\n", __FILE__, __LINE__); 41 | } CATCH (SEGFAULT) { 42 | caught = true; 43 | } 44 | 45 | TEST_ASSERT(caught); 46 | TEST_PASS; 47 | } 48 | 49 | static void *null(const int dummy) { 50 | return dummy ? NULL : &integer; 51 | } 52 | 53 | static void throw_on_signal(int _) { 54 | (void) _; 55 | THROW(SEGFAULT, NULL); 56 | } 57 | -------------------------------------------------------------------------------- /tests/is-uncaught.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Guillermo Calvo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include "testing.h" 19 | 20 | static const struct e4c_exception_type OOPS = {NULL, "Oops"}; 21 | 22 | /** 23 | * Tests function e4c_is_uncaught. 24 | */ 25 | int main(void) { 26 | TEST_ASSERT(e4c_is_uncaught() == false); 27 | 28 | TRY { 29 | TEST_ASSERT(e4c_is_uncaught() == false); 30 | } FINALLY { 31 | TEST_ASSERT(e4c_is_uncaught() == false); 32 | } 33 | 34 | TRY { 35 | THROW(OOPS, NULL); 36 | } CATCH (OOPS) { 37 | TEST_ASSERT(e4c_is_uncaught() == false); 38 | } FINALLY { 39 | TEST_ASSERT(e4c_is_uncaught() == false); 40 | } 41 | 42 | TRY { 43 | TRY { 44 | THROW(OOPS, NULL); 45 | } FINALLY { 46 | TEST_ASSERT(e4c_is_uncaught() == true); 47 | } 48 | } CATCH (OOPS) { 49 | TEST_ASSERT(e4c_is_uncaught() == false); 50 | } FINALLY { 51 | TEST_ASSERT(e4c_is_uncaught() == false); 52 | } 53 | 54 | TEST_PASS; 55 | } 56 | -------------------------------------------------------------------------------- /tests/catch-specific.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Guillermo Calvo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include "testing.h" 19 | 20 | static const struct e4c_exception_type OOPS = {NULL, "Oops"}; 21 | 22 | /** 23 | * Tests macro CATCH with a specific exception type. 24 | */ 25 | int main(void) { 26 | volatile bool caught = false; /* NOSONAR */ 27 | 28 | TRY { 29 | THROW(OOPS, NULL); 30 | } CATCH (OOPS) { 31 | caught = true; 32 | const struct e4c_exception * exception = e4c_get_exception(); 33 | TEST_ASSERT_NOT_NULL(exception); 34 | #ifndef NDEBUG 35 | TEST_ASSERT_STR_CONTAINS(exception->debug.file, "catch-specific.c"); 36 | TEST_ASSERT_INT_EQUALS(exception->debug.line, 29); 37 | TEST_ASSERT_STR_EQUALS(exception->debug.function, "main"); 38 | #else 39 | TEST_ASSERT_NULL(exception->debug.file); 40 | TEST_ASSERT_INT_EQUALS(exception->debug.line, 0); 41 | TEST_ASSERT_NULL(exception->debug.function); 42 | #endif 43 | } 44 | 45 | TEST_ASSERT(caught); 46 | TEST_PASS; 47 | } 48 | -------------------------------------------------------------------------------- /tests/get-exception.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Guillermo Calvo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include "testing.h" 19 | 20 | static const struct e4c_exception_type OOPS = {NULL, "Oops"}; 21 | 22 | /** 23 | * Tests function e4c_get_exception. 24 | */ 25 | int main(void) { 26 | TEST_ASSERT(e4c_get_exception() == NULL); 27 | 28 | TRY { 29 | TEST_ASSERT(e4c_get_exception() == NULL); 30 | } FINALLY { 31 | TEST_ASSERT(e4c_get_exception() == NULL); 32 | } 33 | 34 | TRY { 35 | THROW(OOPS, NULL); 36 | } CATCH (OOPS) { 37 | TEST_ASSERT(e4c_get_exception() != NULL); 38 | } FINALLY { 39 | TEST_ASSERT(e4c_get_exception() != NULL); 40 | } 41 | 42 | TRY { 43 | TRY { 44 | THROW(OOPS, NULL); 45 | } CATCH (OOPS) { 46 | TEST_ASSERT(e4c_get_exception() != NULL); 47 | } FINALLY { 48 | TEST_ASSERT(e4c_get_exception() != NULL); 49 | } 50 | } FINALLY { 51 | TEST_ASSERT(e4c_get_exception() == NULL); 52 | } 53 | 54 | TEST_PASS; 55 | } 56 | -------------------------------------------------------------------------------- /.github/workflows/sonar.yml: -------------------------------------------------------------------------------- 1 | # Scan with SonarQube 2 | name: SonarQube 3 | 4 | on: 5 | push: 6 | branches: 7 | - '*' 8 | pull_request: 9 | branches: 10 | - main 11 | - develop 12 | 13 | permissions: 14 | checks: write 15 | 16 | jobs: 17 | build: 18 | 19 | name: Build 20 | runs-on: ubuntu-latest 21 | env: 22 | BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory 23 | 24 | steps: 25 | 26 | # ================================ 27 | # CHECKOUT BRANCH 28 | # ================================ 29 | - name: Checkout branch 30 | uses: actions/checkout@v4 31 | with: 32 | fetch-depth: 0 33 | 34 | # ================================ 35 | # INSTALL BUILD WRAPPER 36 | # ================================ 37 | - name: Install build wrapper 38 | uses: SonarSource/sonarqube-scan-action/install-build-wrapper@v4 39 | 40 | # ================================ 41 | # RUN BUILD WRAPPER 42 | # ================================ 43 | - name: Run build wrapper 44 | run: | 45 | autoreconf --install 46 | ./configure 47 | build-wrapper-linux-x86-64 --out-dir ${{env.BUILD_WRAPPER_OUT_DIR}} make coverage 48 | 49 | # ================================ 50 | # SONARQUBE SCAN 51 | # ================================ 52 | - name: SonarQube scan 53 | uses: SonarSource/sonarqube-scan-action@v4 54 | env: 55 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 56 | with: 57 | args: > 58 | --define sonar.cfamily.compile-commands="${{env.BUILD_WRAPPER_OUT_DIR}}/compile_commands.json" 59 | -------------------------------------------------------------------------------- /tests/reacquire.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Guillermo Calvo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include "testing.h" 19 | 20 | static const struct e4c_exception_type OOPS = {NULL, "Oops"}; 21 | static const struct e4c_exception_type GIVE_UP = {NULL, "Giving up"}; 22 | 23 | /** 24 | * Tests macro REACQUIRE. 25 | */ 26 | int main(void) { 27 | volatile int total_acquisitions = 0; /* NOSONAR */ 28 | 29 | TRY { 30 | WITH (0) { 31 | total_acquisitions++; 32 | if (total_acquisitions == 1) { 33 | TEST_PRINT_OUT("First acquisition\n"); 34 | } else { 35 | TEST_PRINT_OUT("reacquisitions: %d\n", total_acquisitions - 1); 36 | } 37 | THROW(OOPS, NULL); 38 | } USE (true) { 39 | TEST_PRINT_OUT("use block\n"); 40 | } CATCH (OOPS) { 41 | REACQUIRE(2, GIVE_UP, NULL); 42 | } 43 | } CATCH (GIVE_UP) { 44 | TEST_PRINT_OUT("total_acquisitions: %d\n", total_acquisitions); 45 | } 46 | TEST_ASSERT_INT_EQUALS(total_acquisitions, 3); 47 | TEST_PASS; 48 | } 49 | -------------------------------------------------------------------------------- /docs/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /examples/pthreads.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Guillermo Calvo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | static void cancel_current_thread() { 22 | pthread_exit(PTHREAD_CANCELED); 23 | } 24 | 25 | struct e4c_context dummy = { 26 | ._innermost_block = NULL, 27 | .uncaught_handler = NULL, 28 | .termination_handler = cancel_current_thread, 29 | .initialize_exception = NULL, 30 | .finalize_exception = NULL 31 | }; 32 | 33 | struct e4c_context *e4c_pthreads_context_supplier() { 34 | return &dummy; 35 | } 36 | 37 | //! [setup] 38 | const struct e4c_exception_type OOPS = {NULL, "Oops"}; 39 | 40 | /* A Thread that throws an exception */ 41 | static void *my_thread(void *arg) { 42 | THROW(OOPS, "Oh no"); 43 | } 44 | 45 | int main(void) { 46 | /* Set the thread-safe exception context supplier */ 47 | e4c_set_context_supplier(&e4c_pthreads_context_supplier); 48 | 49 | /* Start the thread */ 50 | pthread_t thread; 51 | pthread_create(&thread, NULL, my_thread, NULL); 52 | pthread_join(thread, NULL); 53 | 54 | /* The program was not terminated, only the thread was canceled */ 55 | return EXIT_SUCCESS; 56 | } 57 | //! [setup] 58 | -------------------------------------------------------------------------------- /tests/throw-cause.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Guillermo Calvo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include "testing.h" 19 | 20 | static const struct e4c_exception_type CAUSE = {NULL, "Root cause"}; 21 | static const struct e4c_exception_type ERROR = {NULL, "Generic error"}; 22 | static const struct e4c_exception_type PROBLEM = {NULL, "Specific problem"}; 23 | 24 | /** 25 | * Tests that exceptions keep track of their root causes. 26 | */ 27 | int main(void) { 28 | TRY { 29 | TRY { 30 | THROW(CAUSE, NULL); 31 | } CATCH (CAUSE) { 32 | THROW(ERROR, NULL); 33 | } 34 | } CATCH (ERROR) { 35 | TEST_ASSERT_NOT_NULL(e4c_get_exception()->cause); 36 | TEST_ASSERT_PTR_EQUALS(e4c_get_exception()->cause->type, &CAUSE); 37 | } 38 | 39 | TRY { 40 | TRY { 41 | TRY { 42 | THROW(CAUSE, NULL); 43 | } CATCH (CAUSE) { 44 | THROW(ERROR, NULL); 45 | } 46 | } CATCH (ERROR) { 47 | THROW(PROBLEM, NULL); 48 | } 49 | } CATCH (PROBLEM) { 50 | const struct e4c_exception *exception = e4c_get_exception(); 51 | TEST_ASSERT_NOT_NULL(exception->cause); 52 | TEST_ASSERT_PTR_EQUALS(exception->cause->type, &ERROR); 53 | TEST_ASSERT_NOT_NULL(exception->cause->cause); 54 | TEST_ASSERT_PTR_EQUALS(exception->cause->cause->type, &CAUSE); 55 | } 56 | 57 | TEST_PASS; 58 | } 59 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # Continuous Integration 2 | name: Build 3 | 4 | on: 5 | push: 6 | branches: 7 | - '*' 8 | pull_request: 9 | branches: 10 | - main 11 | - develop 12 | 13 | permissions: 14 | checks: write 15 | 16 | jobs: 17 | build: 18 | 19 | name: Build 20 | runs-on: ubuntu-latest 21 | 22 | steps: 23 | 24 | # ================================ 25 | # SHALLOW CLONE 26 | # ================================ 27 | - name: Shallow clone 28 | uses: actions/checkout@v4 29 | 30 | # ================================ 31 | # UPDATE CONFIGURE SCRIPTS 32 | # ================================ 33 | - name: Update configure scripts 34 | run: | 35 | autoreconf --install 36 | ./configure 37 | 38 | # ================================ 39 | # MAKE ALL 40 | # ================================ 41 | - name: Make all 42 | run: make all 43 | 44 | # ================================ 45 | # TEST 46 | # ================================ 47 | - name: Test 48 | run: make tests 49 | 50 | # ================================ 51 | # COVERAGE 52 | # ================================ 53 | - name: Coverage 54 | run: make coverage 55 | 56 | # ================================ 57 | # UPLOAD COVERAGE REPORTS TO CODECOV 58 | # ================================ 59 | - name: Upload coverage reports to Codecov 60 | uses: codecov/codecov-action@v5 61 | with: 62 | token: ${{ secrets.CODECOV_TOKEN }} 63 | slug: guillermocalvo/exceptions4c 64 | 65 | # ================================ 66 | # TEST REPORT 67 | # ================================ 68 | - name: Generate test report 69 | uses: guillermocalvo/autotools-test-report@v1 70 | if: success() || failure() 71 | 72 | # ================================ 73 | # UPLOAD TEST LOG 74 | # ================================ 75 | - name: Upload test report as artifact 76 | uses: actions/upload-artifact@v4 77 | if: success() || failure() 78 | with: 79 | path: ${{github.workspace}}/test-suite.log 80 | -------------------------------------------------------------------------------- /tests/throw-suppressed.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Guillermo Calvo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include "testing.h" 19 | 20 | static void custom_finalizer(const struct e4c_exception *); 21 | 22 | static const struct e4c_exception_type CAUSE = {NULL, "Cause"}; 23 | static const struct e4c_exception_type SUPPRESSED = {NULL, "Suppressed"}; 24 | static const struct e4c_exception_type OOPS = {NULL, "Oops"}; 25 | 26 | static volatile bool is_finalized = false; 27 | 28 | /** 29 | * Tests that exceptions may be suppressed. 30 | */ 31 | int main(void) { 32 | volatile bool caught1 = false, caught2 = false; /* NOSONAR */ 33 | 34 | e4c_get_context()->finalize_exception = &custom_finalizer; 35 | 36 | TRY { 37 | TRY { 38 | TRY { 39 | THROW(CAUSE, NULL); 40 | } FINALLY { 41 | THROW(SUPPRESSED, NULL); 42 | } 43 | } CATCH (SUPPRESSED) { 44 | caught1 = true; 45 | TEST_ASSERT(e4c_get_exception()->cause != NULL && e4c_get_exception()->cause->type == &CAUSE); 46 | } FINALLY { 47 | THROW(OOPS, NULL); 48 | } 49 | } CATCH (OOPS) { 50 | caught2 = true; 51 | TEST_ASSERT(e4c_get_exception()->cause == NULL); 52 | } 53 | 54 | TEST_ASSERT(caught1); 55 | TEST_ASSERT(caught2); 56 | TEST_ASSERT(is_finalized); 57 | TEST_PASS; 58 | } 59 | 60 | static void custom_finalizer(const struct e4c_exception *exception) { 61 | if (!is_finalized) { 62 | is_finalized = exception->type == &SUPPRESSED; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /tests/catch-generic.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Guillermo Calvo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include "testing.h" 19 | 20 | static const struct e4c_exception_type GENERIC = {NULL, "Generic exception"}; 21 | static const struct e4c_exception_type SPECIFIC = {&GENERIC, "Specific exception"}; 22 | static const struct e4c_exception_type DIFFERENT = {&GENERIC, "Different exception"}; 23 | static const struct e4c_exception_type MORE_SPECIFIC = {&SPECIFIC, "More specific exception"}; 24 | 25 | /** 26 | * Tests macro CATCH with generic exception types. 27 | */ 28 | int main(void) { 29 | volatile bool caught1 = false, caught2 = false, caught3 = false; /* NOSONAR */ 30 | 31 | TRY { 32 | THROW(SPECIFIC, NULL); 33 | } CATCH (GENERIC) { 34 | caught1 = true; 35 | } 36 | 37 | TEST_ASSERT(caught1); 38 | 39 | TRY { 40 | TRY { 41 | THROW(SPECIFIC, NULL); 42 | } CATCH (DIFFERENT) { 43 | TEST_FAIL("Should not have caught the exception here\n"); 44 | } FINALLY { 45 | THROW(MORE_SPECIFIC, NULL); 46 | } 47 | } CATCH (GENERIC) { 48 | caught2 = true; 49 | } 50 | 51 | TEST_ASSERT(caught2); 52 | 53 | TRY { 54 | TRY { 55 | THROW(SPECIFIC, NULL); 56 | } CATCH (MORE_SPECIFIC) { 57 | TEST_FAIL("Should not have caught the exception here\n"); 58 | } FINALLY { 59 | THROW(MORE_SPECIFIC, NULL); 60 | } 61 | } CATCH (SPECIFIC) { 62 | caught3 = true; 63 | } 64 | 65 | TEST_ASSERT(caught3); 66 | TEST_PASS; 67 | } 68 | -------------------------------------------------------------------------------- /tests/finally.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Guillermo Calvo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include "testing.h" 19 | 20 | static void aux1(void); 21 | 22 | static void aux2(void); 23 | 24 | static void aux3(void); 25 | 26 | static void aux4(void); 27 | 28 | static void aux5(void); 29 | 30 | static const struct e4c_exception_type OOPS = {NULL, "Oops"}; 31 | 32 | static volatile bool finalized1 = false; 33 | static volatile bool finalized2 = false; 34 | static volatile bool finalized3 = false; 35 | 36 | /** 37 | * Tests macro FINALLY. 38 | */ 39 | int main(void) { 40 | volatile bool caught = false; /* NOSONAR */ 41 | TRY { 42 | aux1(); 43 | } CATCH (OOPS) { 44 | caught = true; 45 | } 46 | TEST_ASSERT(caught); 47 | TEST_ASSERT(finalized1); 48 | TEST_ASSERT(finalized2); 49 | TEST_ASSERT(finalized3); 50 | TEST_PASS; 51 | } 52 | 53 | static void aux1(void) { 54 | aux2(); 55 | exit(EXIT_FAILURE); 56 | } 57 | 58 | static void aux2(void) { 59 | TRY { 60 | aux3(); 61 | } FINALLY { 62 | finalized1 = true; 63 | } 64 | TEST_FAIL("Reached %s:%d\n", __FILE__, __LINE__); 65 | } 66 | 67 | static void aux3(void) { 68 | aux4(); 69 | TEST_FAIL("Reached %s:%d\n", __FILE__, __LINE__); 70 | } 71 | 72 | static void aux4(void) { 73 | TRY { 74 | aux5(); 75 | } CATCH (OOPS) { 76 | THROW(OOPS, NULL); 77 | } FINALLY { 78 | finalized2 = true; 79 | } 80 | TEST_FAIL("Reached %s:%d\n", __FILE__, __LINE__); 81 | } 82 | 83 | static void aux5(void) { 84 | TRY { 85 | THROW(OOPS, NULL); 86 | } FINALLY { 87 | finalized3 = true; 88 | } 89 | TEST_FAIL("Reached %s:%d\n", __FILE__, __LINE__); 90 | } 91 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # -*- Autoconf -*- 2 | # Process this file with autoconf to produce a configure script. 3 | 4 | 5 | # Autoconf requirements 6 | AC_PREREQ([2.68]) 7 | 8 | 9 | # Initialize package 10 | AC_INIT([exceptions4c], [4.0.0], [exceptions4c@guillermo.dev], [exceptions4c], [https://github.com/guillermocalvo/exceptions4c/]) 11 | 12 | 13 | # Information on the package 14 | AC_COPYRIGHT([Copyright 2025 Guillermo Calvo]) 15 | AC_REVISION([$PACKAGE_VERSION]) 16 | AC_MSG_NOTICE([ 17 | _ _ _ _ 18 | _____ _____ ___ _ __ | |_(_) ___ _ __ ___| || | ___ 19 | / _ \ \/ / __/ _ \ '_ \| __| |/ _ \| '_ \/ __| || |_ / __| 20 | | __/> < (_| __/ |_) | |_| | (_) | | | \__ \__ _| (__ 21 | \___/_/\_\___\___| .__/ \__|_|\___/|_| |_|___/ |_| \___| 22 | |_| 23 | ]) 24 | 25 | 26 | # Check if the source folder is correct 27 | AC_CONFIG_SRCDIR([src/exceptions4c.c]) 28 | 29 | 30 | # Checks for programs. 31 | AC_PROG_CC 32 | AC_PROG_CPP 33 | AC_PROG_RANLIB 34 | AM_PROG_AR 35 | 36 | 37 | # Checks for header files. 38 | AC_HEADER_STDBOOL 39 | AC_CHECK_HEADERS([errno.h]) 40 | AC_CHECK_HEADERS([setjmp.h]) 41 | AC_CHECK_HEADERS([stdarg.h]) 42 | AC_CHECK_HEADERS([stdbool.h]) 43 | AC_CHECK_HEADERS([stdio.h]) 44 | AC_CHECK_HEADERS([stdlib.h]) 45 | AC_CHECK_HEADERS([string.h]) 46 | 47 | 48 | # Checks for types 49 | AC_TYPE_SIZE_T 50 | AC_CHECK_TYPES(jmp_buf) 51 | AC_CHECK_TYPES(sigjmp_buf) 52 | 53 | 54 | # Checks for compiler characteristics 55 | AC_LANG([C]) 56 | 57 | 58 | # Checks for library functions. 59 | AC_FUNC_MALLOC 60 | AC_CHECK_FUNCS([atexit]) 61 | AC_CHECK_FUNCS([calloc]) 62 | AC_CHECK_FUNCS([exit]) 63 | AC_CHECK_FUNCS([fflush]) 64 | AC_CHECK_FUNCS([fprintf]) 65 | AC_CHECK_FUNCS([free]) 66 | AC_CHECK_FUNCS([longjmp]) 67 | AC_CHECK_FUNCS([malloc]) 68 | AC_CHECK_FUNCS([setjmp]) 69 | AC_CHECK_FUNCS([siglongjmp]) 70 | AC_CHECK_FUNCS([sigsetjmp]) 71 | AC_CHECK_FUNCS([snprintf]) 72 | AC_CHECK_FUNCS([sprintf]) 73 | AC_CHECK_FUNCS([vsnprintf]) 74 | 75 | 76 | # The config file is generated but not used by the source code 77 | #AC_CONFIG_HEADERS([config.h]) 78 | AC_CONFIG_FILES([ 79 | exceptions4c.pc 80 | Makefile 81 | ]) 82 | 83 | 84 | # Automake initialisation 85 | AM_INIT_AUTOMAKE([ 86 | -Wall 87 | -Werror 88 | foreign 89 | subdir-objects 90 | no-define 91 | ]) 92 | 93 | 94 | AC_OUTPUT 95 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | # Generate and publish docs to GitHub Pages 2 | name: Docs 3 | 4 | on: 5 | push: 6 | branches: 7 | - 'main' 8 | workflow_dispatch: 9 | 10 | jobs: 11 | docs: 12 | 13 | permissions: 14 | pages: write 15 | id-token: write 16 | name: Generate Documentation 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | 21 | # ================================ 22 | # INSTALL DOXYGEN 23 | # ================================ 24 | - name: Install Doxygen 25 | run: | 26 | mkdir ~/doxygen 27 | cd ~/doxygen 28 | echo "Installing latest Doxygen version" 29 | DOWNLOAD_URL=`curl -s https://api.github.com/repos/doxygen/doxygen/releases/latest | jq -r '.assets[].browser_download_url | select(contains("linux"))'` 30 | wget -nv $DOWNLOAD_URL 31 | tar -xf *.linux.bin.tar.gz 32 | rm *.linux.bin.tar.gz 33 | cd doxygen-* 34 | sudo make install 35 | 36 | # ================================ 37 | # INSTALL LCOV 38 | # ================================ 39 | - name: Install LCOV 40 | run: sudo apt-get install lcov 41 | 42 | # ================================ 43 | # SHALLOW CLONE 44 | # ================================ 45 | - name: Shallow clone 46 | uses: actions/checkout@v4 47 | 48 | # ================================ 49 | # UPDATE CONFIGURE SCRIPTS 50 | # ================================ 51 | - name: Update configure scripts 52 | run: | 53 | autoreconf --install 54 | ./configure 55 | 56 | # ================================ 57 | # MAKE DOCS 58 | # ================================ 59 | - name: Make docs 60 | run: make docs 61 | 62 | # ================================ 63 | # COVERAGE REPORT 64 | # ================================ 65 | - name: Make coverage report 66 | run: make coverage-report 67 | 68 | # ================================ 69 | # UPLOAD DOXYGEN 70 | # ================================ 71 | - name: Upload Doxygen files as artifact 72 | uses: actions/upload-pages-artifact@v3 73 | with: 74 | path: ${{github.workspace}}/docs/html 75 | 76 | # ================================ 77 | # PUBLISH DOCUMENTATION 78 | # ================================ 79 | - name: Publish documentation to GitHub Pages 80 | uses: actions/deploy-pages@v4 81 | -------------------------------------------------------------------------------- /docs/exceptions4c.css: -------------------------------------------------------------------------------- 1 | html { 2 | --attention-color-dark: var(--bug-color-dark); 3 | --attention-color-darker: var(--bug-color-darker); 4 | --attention-color: var(--bug-color); 5 | --important-color-dark: var(--todo-color-dark); 6 | --important-color-darker: var(--todo-color-darker); 7 | --important-color: var(--todo-color); 8 | --remark-color-dark: var(--invariant-color-dark); 9 | --remark-color-darker: var(--invariant-color-darker); 10 | --remark-color: var(--invariant-color); 11 | --see-color-dark: var(--note-color-dark); 12 | --see-color-darker: var(--note-color-darker); 13 | --see-color: var(--note-color); 14 | } 15 | 16 | dl.attention > dt::before, 17 | dl.bug > dt::before, 18 | dl.deprecated > dt::before, 19 | dl.important > dt::before, 20 | dl.invariant > dt::before, 21 | dl.note > dt::before, 22 | dl.post > dt::before, 23 | dl.pre > dt::before, 24 | dl.remark > dt::before, 25 | dl.see > dt::before, 26 | dl.warning > dt::before { 27 | margin-right: 0.5em; 28 | } 29 | 30 | dl.attention > dt::before { 31 | content: url(stop-16.svg); 32 | } 33 | 34 | dl.bug > dt::before { 35 | content: url(bug-16.svg); 36 | } 37 | 38 | dl.deprecated > dt::before { 39 | content: url(trash-16.svg); 40 | } 41 | 42 | dl.important > dt::before { 43 | content: url(report-16.svg); 44 | } 45 | 46 | dl.invariant > dt::before { 47 | content: url(checkbox-16.svg); 48 | } 49 | 50 | dl.note > dt::before { 51 | content: url(info-16.svg); 52 | } 53 | 54 | dl.post > dt::before { 55 | content: url(check-circle-fill-16.svg); 56 | } 57 | 58 | dl.pre > dt::before { 59 | content: url(check-circle-16.svg); 60 | } 61 | 62 | dl.remark > dt::before { 63 | content: url(light-bulb-16.svg); 64 | } 65 | 66 | dl.see > dt::before { 67 | content: url(eye-16.svg); 68 | } 69 | 70 | dl.warning > dt::before { 71 | content: url(alert-16.svg); 72 | } 73 | 74 | dl.attention { 75 | background: var(--attention-color); 76 | border-color: var(--attention-color-dark); 77 | color: var(--attention-color-darker); 78 | dt { 79 | color: var(--attention-color-dark); 80 | } 81 | } 82 | 83 | dl.important { 84 | background: var(--important-color); 85 | border-color: var(--important-color-dark); 86 | color: var(--important-color-darker); 87 | dt { 88 | color: var(--important-color-dark); 89 | } 90 | } 91 | 92 | dl.remark { 93 | background: var(--remark-color); 94 | border-color: var(--remark-color-dark); 95 | color: var(--remark-color-darker); 96 | dt { 97 | color: var(--remark-color-dark); 98 | } 99 | } 100 | 101 | dl.see { 102 | background: var(--see-color); 103 | border-left: 8px solid var(--see-color-dark); 104 | border-radius: var(--border-radius-small); 105 | color: var(--see-color-darker); 106 | padding: var(--spacing-medium); 107 | margin: var(--spacing-medium) 0; 108 | overflow: hidden; 109 | margin-left: 0; 110 | dt { 111 | color: var(--see-color-dark); 112 | } 113 | dd { 114 | color: var(--see-color-darker); 115 | margin-left: 1em; 116 | display: list-item; 117 | list-style-type: square; 118 | } 119 | dd::marker { 120 | color: var(--see-color-darker); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /examples/customization.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Guillermo Calvo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #define main main_initialize_exception 22 | //! [initialize_exception] 23 | const struct e4c_exception_type PET_ERROR = {NULL, "Pet error"}; 24 | 25 | static void set_custom_data(struct e4c_exception * exception) { 26 | exception->data = "My custom data"; 27 | } 28 | 29 | int main(void) { 30 | e4c_get_context()->initialize_exception = set_custom_data; 31 | TRY { 32 | THROW(PET_ERROR, "Bad dog"); 33 | } CATCH_ALL { 34 | const char * data = e4c_get_exception()->data; 35 | printf("Custom data: %s\n", data); 36 | } 37 | return EXIT_SUCCESS; 38 | } 39 | //! [initialize_exception] 40 | #undef main 41 | 42 | #define main main_finalize_exception 43 | //! [finalize_exception] 44 | struct my_custom_data { int id; const char * msg; }; 45 | 46 | static void my_initializer(struct e4c_exception * exception) { 47 | struct my_custom_data data = {123, "Hello world!"}, * tmp; 48 | if ((tmp = exception->data = malloc(sizeof(data)))) *tmp = data; 49 | } 50 | 51 | static void my_finalizer(const struct e4c_exception * exception) { 52 | free(exception->data); 53 | } 54 | 55 | int main(void) { 56 | struct e4c_context * context = e4c_get_context(); 57 | context->initialize_exception = my_initializer; 58 | context->finalize_exception = my_finalizer; 59 | TRY { 60 | THROW(PET_ERROR, "Bad dog"); 61 | } CATCH_ALL { 62 | const struct my_custom_data * data = e4c_get_exception()->data; 63 | printf("ID: %d MSG: %s\n", data->id, data->msg); 64 | } 65 | return EXIT_SUCCESS; 66 | } 67 | //! [finalize_exception] 68 | #undef main 69 | 70 | #define main main_termination_handler 71 | //! [termination_handler] 72 | static void my_termination_handler(void) { 73 | exit(EXIT_SUCCESS); 74 | } 75 | 76 | int main(void) { 77 | e4c_get_context()->termination_handler = &my_termination_handler; 78 | THROW(PET_ERROR, "Bad dog"); 79 | } 80 | //! [termination_handler] 81 | #undef main 82 | 83 | #define main main_set_context_supplier 84 | //! [set_context_supplier] 85 | static struct e4c_context my_custom_context = { 86 | .initialize_exception = my_initializer, 87 | .finalize_exception = my_finalizer 88 | }; 89 | 90 | static struct e4c_context * my_context_supplier(void) { 91 | return &my_custom_context; 92 | } 93 | 94 | int main(void) { 95 | e4c_set_context_supplier(my_context_supplier); 96 | TRY { 97 | THROW(PET_ERROR, "Bad dog"); 98 | } CATCH_ALL { 99 | const struct my_custom_data * data = e4c_get_exception()->data; 100 | printf("MSG: %s\n", data->msg); 101 | } 102 | return EXIT_SUCCESS; 103 | } 104 | //! [set_context_supplier] 105 | #undef main 106 | 107 | int main(int argc, char * argv[]) { 108 | 109 | //! [get_context] 110 | struct e4c_context * context = e4c_get_context(); 111 | //! [get_context] 112 | 113 | printf("current context: %p\n", (void *) context); 114 | 115 | main_initialize_exception(); 116 | main_finalize_exception(); 117 | main_set_context_supplier(); 118 | main_termination_handler(); 119 | 120 | return EXIT_SUCCESS; 121 | } 122 | -------------------------------------------------------------------------------- /docs/doxygen-awesome-sidebar-only.css: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | Doxygen Awesome 4 | https://github.com/jothepro/doxygen-awesome-css 5 | 6 | MIT License 7 | 8 | Copyright (c) 2021 - 2023 jothepro 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | */ 29 | 30 | html { 31 | /* side nav width. MUST be = `TREEVIEW_WIDTH`. 32 | * Make sure it is wide enough to contain the page title (logo + title + version) 33 | */ 34 | --side-nav-fixed-width: 335px; 35 | --menu-display: none; 36 | 37 | --top-height: 120px; 38 | --toc-sticky-top: -25px; 39 | --toc-max-height: calc(100vh - 2 * var(--spacing-medium) - 25px); 40 | } 41 | 42 | #projectname { 43 | white-space: nowrap; 44 | } 45 | 46 | 47 | @media screen and (min-width: 768px) { 48 | html { 49 | --searchbar-background: var(--page-background-color); 50 | } 51 | 52 | #side-nav { 53 | min-width: var(--side-nav-fixed-width); 54 | max-width: var(--side-nav-fixed-width); 55 | top: var(--top-height); 56 | overflow: visible; 57 | } 58 | 59 | #nav-tree, #side-nav { 60 | height: calc(100vh - var(--top-height)) !important; 61 | } 62 | 63 | #nav-tree { 64 | padding: 0; 65 | } 66 | 67 | #top { 68 | display: block; 69 | border-bottom: none; 70 | height: var(--top-height); 71 | margin-bottom: calc(0px - var(--top-height)); 72 | max-width: var(--side-nav-fixed-width); 73 | overflow: hidden; 74 | background: var(--side-nav-background); 75 | } 76 | #main-nav { 77 | float: left; 78 | padding-right: 0; 79 | } 80 | 81 | .ui-resizable-handle { 82 | cursor: default; 83 | width: 1px !important; 84 | background: var(--separator-color); 85 | box-shadow: 0 calc(-2 * var(--top-height)) 0 0 var(--separator-color); 86 | } 87 | 88 | #nav-path { 89 | position: fixed; 90 | right: 0; 91 | left: var(--side-nav-fixed-width); 92 | bottom: 0; 93 | width: auto; 94 | } 95 | 96 | #doc-content { 97 | height: calc(100vh - 31px) !important; 98 | padding-bottom: calc(3 * var(--spacing-large)); 99 | padding-top: calc(var(--top-height) - 80px); 100 | box-sizing: border-box; 101 | margin-left: var(--side-nav-fixed-width) !important; 102 | } 103 | 104 | #MSearchBox { 105 | width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium))); 106 | } 107 | 108 | #MSearchField { 109 | width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - 65px); 110 | } 111 | 112 | #MSearchResultsWindow { 113 | left: var(--spacing-medium) !important; 114 | right: auto; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![Build Status][BADGE_BUILD_STATUS]][BUILD_STATUS] 3 | [![Quality Gate Status][BADGE_QUALITY_GATE]][QUALITY_GATE] 4 | [![Code Coverage][BADGE_COVERAGE]][COVERAGE] 5 | [![Docs Status][BADGE_DOCS_STATUS]][DOCS] 6 | [![Latest Release][BADGE_LATEST_RELEASE]][LATEST_RELEASE] 7 | [![FOSSA Status][BADGE_FOSSA_STATUS]][FOSSA_STATUS] 8 | 9 | ![Exceptions for C][LOGO] 10 | 11 | 12 | ## An Exception Handling Library for C 13 | 14 | Bring the power of exceptions to your C applications! 15 | 16 | ![][EXAMPLE] 17 | 18 | > [!NOTE] 19 | > This library provides you with a set of macros and functions that map the exception handling semantics you are 20 | > probably already used to. 21 | 22 | 23 | ## Author 24 | 25 | Copyright 2025 [Guillermo Calvo][AUTHOR]. 26 | 27 | [![][GUILLERMO_IMAGE]][GUILLERMO] 28 | 29 | 30 | ## License 31 | 32 | This library is licensed under the *Apache License, Version 2.0* (the "License"); 33 | you may not use it except in compliance with the License. 34 | 35 | You may obtain a copy of the License at 36 | 37 | Unless required by applicable law or agreed to in writing, software distributed under the License 38 | is distributed on an "AS IS" BASIS, **WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND**, either express or implied. 39 | 40 | See the License for the specific language governing permissions and limitations under the License. 41 | 42 | 43 | **Permitted:** 44 | 45 | - **Commercial Use**: You may use this library and derivatives for commercial purposes. 46 | - **Modification**: You may modify this library. 47 | - **Distribution**: You may distribute this library. 48 | - **Patent Use**: This license provides an express grant of patent rights from contributors. 49 | - **Private Use**: You may use and modify this library without distributing it. 50 | 51 | **Required:** 52 | 53 | - **License and Copyright Notice**: If you distribute this library you must include a copy of the license and copyright 54 | notice. 55 | - **State Changes**: If you modify and distribute this library you must document changes made to this library. 56 | 57 | **Forbidden:** 58 | 59 | - **Trademark use**: This license does not grant any trademark rights. 60 | - **Liability**: The library author cannot be held liable for damages. 61 | - **Warranty**: This library is provided without any warranty. 62 | 63 | 64 | [AUTHOR]: https://github.com/guillermocalvo/ 65 | [BADGE_BUILD_STATUS]: https://github.com/guillermocalvo/exceptions4c/workflows/Build/badge.svg 66 | [BADGE_COVERAGE]: https://codecov.io/gh/guillermocalvo/exceptions4c/branch/main/graph/badge.svg 67 | [BADGE_DOCS_STATUS]: https://github.com/guillermocalvo/exceptions4c/workflows/Docs/badge.svg 68 | [BADGE_FOSSA_STATUS]: https://app.fossa.com/api/projects/git%2Bgithub.com%2Fguillermocalvo%2Fexceptions4c.svg?type=shield 69 | [BADGE_LATEST_RELEASE]: https://img.shields.io/github/v/release/guillermocalvo/exceptions4c 70 | [BADGE_QUALITY_GATE]: https://sonarcloud.io/api/project_badges/measure?project=guillermocalvo_exceptions4c&metric=alert_status 71 | [BUILD_STATUS]: https://github.com/guillermocalvo/exceptions4c/actions?query=workflow%3ABuild 72 | [COVERAGE]: https://codecov.io/gh/guillermocalvo/exceptions4c 73 | [DOCS]: https://exceptions4c.guillermo.dev/ 74 | [EXAMPLE]: docs/example.png 75 | [FOSSA_STATUS]: https://app.fossa.com/projects/git%2Bgithub.com%2Fguillermocalvo%2Fexceptions4c?ref=badge_shield 76 | [GUILLERMO]: https://guillermo.dev/ 77 | [GUILLERMO_IMAGE]: https://guillermo.dev/assets/images/thumb.png 78 | [LATEST_RELEASE]: https://github.com/guillermocalvo/exceptions4c/releases/latest 79 | [LOGO]: docs/exceptions4c.svg 80 | [QUALITY_GATE]: https://sonarcloud.io/dashboard?id=guillermocalvo_exceptions4c 81 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/c,autotools,gcov,cmake,ninja,clion+all 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=c,autotools,gcov,cmake,ninja,clion+all 3 | 4 | ### Autotools ### 5 | # http://www.gnu.org/software/automake 6 | 7 | Makefile.in 8 | /ar-lib 9 | /mdate-sh 10 | /py-compile 11 | /test-driver 12 | /ylwrap 13 | .deps/ 14 | .dirstamp 15 | 16 | # http://www.gnu.org/software/autoconf 17 | 18 | autom4te.cache 19 | /autoscan.log 20 | /autoscan-*.log 21 | /aclocal.m4 22 | /compile 23 | /config.cache 24 | /config.guess 25 | /config.h.in 26 | /config.log 27 | /config.status 28 | /config.sub 29 | /configure 30 | /configure.scan 31 | /depcomp 32 | /install-sh 33 | /missing 34 | /stamp-h1 35 | 36 | # https://www.gnu.org/software/libtool/ 37 | 38 | /ltmain.sh 39 | 40 | # http://www.gnu.org/software/texinfo 41 | 42 | /texinfo.tex 43 | 44 | # http://www.gnu.org/software/m4/ 45 | 46 | m4/libtool.m4 47 | m4/ltoptions.m4 48 | m4/ltsugar.m4 49 | m4/ltversion.m4 50 | m4/lt~obsolete.m4 51 | 52 | # Generated Makefile 53 | # (meta build system like autotools, 54 | # can automatically generate from config.status script 55 | # (which is called by configure script)) 56 | Makefile 57 | 58 | ### Autotools Patch ### 59 | 60 | ### C ### 61 | # Prerequisites 62 | *.d 63 | 64 | # Object files 65 | *.o 66 | *.ko 67 | *.obj 68 | *.elf 69 | 70 | # Linker output 71 | *.ilk 72 | *.map 73 | *.exp 74 | 75 | # Precompiled Headers 76 | *.gch 77 | *.pch 78 | 79 | # Libraries 80 | *.lib 81 | *.a 82 | *.la 83 | *.lo 84 | 85 | # Shared objects (inc. Windows DLLs) 86 | *.dll 87 | *.so 88 | *.so.* 89 | *.dylib 90 | 91 | # Executables 92 | *.exe 93 | *.out 94 | *.app 95 | *.i*86 96 | *.x86_64 97 | *.hex 98 | 99 | # Debug files 100 | *.dSYM/ 101 | *.su 102 | *.idb 103 | *.pdb 104 | 105 | # Kernel Module Compile Results 106 | *.mod* 107 | *.cmd 108 | .tmp_versions/ 109 | modules.order 110 | Module.symvers 111 | Mkfile.old 112 | dkms.conf 113 | 114 | ### CLion+all ### 115 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 116 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 117 | 118 | # User-specific stuff 119 | .idea/**/workspace.xml 120 | .idea/**/tasks.xml 121 | .idea/**/usage.statistics.xml 122 | .idea/**/dictionaries 123 | .idea/**/shelf 124 | 125 | # AWS User-specific 126 | .idea/**/aws.xml 127 | 128 | # Generated files 129 | .idea/**/contentModel.xml 130 | 131 | # Sensitive or high-churn files 132 | .idea/**/dataSources/ 133 | .idea/**/dataSources.ids 134 | .idea/**/dataSources.local.xml 135 | .idea/**/sqlDataSources.xml 136 | .idea/**/dynamic.xml 137 | .idea/**/uiDesigner.xml 138 | .idea/**/dbnavigator.xml 139 | 140 | # Gradle 141 | .idea/**/gradle.xml 142 | .idea/**/libraries 143 | 144 | # Gradle and Maven with auto-import 145 | # When using Gradle or Maven with auto-import, you should exclude module files, 146 | # since they will be recreated, and may cause churn. Uncomment if using 147 | # auto-import. 148 | # .idea/artifacts 149 | # .idea/compiler.xml 150 | # .idea/jarRepositories.xml 151 | # .idea/modules.xml 152 | # .idea/*.iml 153 | # .idea/modules 154 | # *.iml 155 | # *.ipr 156 | 157 | # CMake 158 | cmake-build-*/ 159 | 160 | # Mongo Explorer plugin 161 | .idea/**/mongoSettings.xml 162 | 163 | # File-based project format 164 | *.iws 165 | 166 | # IntelliJ 167 | out/ 168 | 169 | # mpeltonen/sbt-idea plugin 170 | .idea_modules/ 171 | 172 | # JIRA plugin 173 | atlassian-ide-plugin.xml 174 | 175 | # Cursive Clojure plugin 176 | .idea/replstate.xml 177 | 178 | # SonarLint plugin 179 | .idea/sonarlint/ 180 | 181 | # Crashlytics plugin (for Android Studio and IntelliJ) 182 | com_crashlytics_export_strings.xml 183 | crashlytics.properties 184 | crashlytics-build.properties 185 | fabric.properties 186 | 187 | # Editor-based Rest Client 188 | .idea/httpRequests 189 | 190 | # Android studio 3.1+ serialized cache file 191 | .idea/caches/build_file_checksums.ser 192 | 193 | ### CLion+all Patch ### 194 | # Ignore everything but code style settings and run configurations 195 | # that are supposed to be shared within teams. 196 | 197 | .idea/* 198 | 199 | !.idea/codeStyles 200 | !.idea/runConfigurations 201 | 202 | ### CMake ### 203 | CMakeLists.txt.user 204 | CMakeCache.txt 205 | CMakeFiles 206 | CMakeScripts 207 | Testing 208 | cmake_install.cmake 209 | install_manifest.txt 210 | compile_commands.json 211 | CTestTestfile.cmake 212 | _deps 213 | 214 | ### CMake Patch ### 215 | CMakeUserPresets.json 216 | 217 | # External projects 218 | *-prefix/ 219 | 220 | ### Gcov ### 221 | # gcc coverage testing tool files 222 | 223 | *.gcno 224 | *.gcda 225 | *.gcov 226 | 227 | ### Ninja ### 228 | .ninja_deps 229 | .ninja_log 230 | 231 | # End of https://www.toptal.com/developers/gitignore/api/c,autotools,gcov,cmake,ninja,clion+all 232 | -------------------------------------------------------------------------------- /tests/with-use.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Guillermo Calvo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include "testing.h" 19 | 20 | enum resource_status { UNINITIALIZED, INITIALIZED, USED, FINALIZED }; 21 | 22 | static const struct e4c_exception_type OOPS = {NULL, "Oops"}; 23 | 24 | #define FINALIZE(fail, status, expected_status, caught, thrown) \ 25 | 0); \ 26 | TEST_ASSERT_INT_EQUALS(status, expected_status); \ 27 | TEST_ASSERT_FALSE(caught); \ 28 | if (thrown) { \ 29 | TEST_ASSERT_NOT_NULL(e4c_get_exception()); \ 30 | } else { \ 31 | TEST_ASSERT_NULL(e4c_get_exception()); \ 32 | } \ 33 | if (fail) { \ 34 | THROW(OOPS, NULL); \ 35 | } \ 36 | (void) (status = FINALIZED 37 | 38 | 39 | /** 40 | * Tests macros WITH... USE. 41 | */ 42 | int main(void) { 43 | volatile enum resource_status status; /* NOSONAR */ 44 | volatile bool caught; /* NOSONAR */ 45 | 46 | /* Happy path */ 47 | status = UNINITIALIZED; 48 | caught = false; 49 | WITH (FINALIZE(false, status, USED, caught, false)) { 50 | status = INITIALIZED; 51 | } USE (true) { 52 | TEST_ASSERT_INT_EQUALS(status, INITIALIZED); 53 | status = USED; 54 | } FINALLY { 55 | TEST_ASSERT_INT_EQUALS(status, FINALIZED); 56 | } 57 | TEST_ASSERT_INT_EQUALS(status, FINALIZED); 58 | 59 | /* Not used */ 60 | status = UNINITIALIZED; 61 | caught = false; 62 | WITH (FINALIZE(false, status, INITIALIZED, caught, false)) { 63 | status = INITIALIZED; 64 | } USE (false) { 65 | TEST_FAIL("Reached %s:%d", __FILE__, __LINE__); 66 | } FINALLY { 67 | TEST_ASSERT_INT_EQUALS(status, FINALIZED); 68 | } 69 | TEST_ASSERT_INT_EQUALS(status, FINALIZED); 70 | 71 | /* Exception while acquiring the resource */ 72 | status = UNINITIALIZED; 73 | caught = false; 74 | WITH (FINALIZE(false, status, UNINITIALIZED, caught, true)) { 75 | THROW(OOPS, NULL); 76 | } USE (true) { 77 | TEST_FAIL("Reached %s:%d", __FILE__, __LINE__); 78 | } CATCH_ALL { 79 | TEST_ASSERT_INT_EQUALS(status, UNINITIALIZED); 80 | caught = true; 81 | } FINALLY { 82 | TEST_ASSERT_INT_EQUALS(status, UNINITIALIZED); 83 | TEST_ASSERT_TRUE(caught); 84 | } 85 | TEST_ASSERT_INT_EQUALS(status, UNINITIALIZED); 86 | 87 | /* Exception while using the resource */ 88 | status = UNINITIALIZED; 89 | caught = false; 90 | WITH (FINALIZE(false, status, USED, caught, true)) { 91 | status = INITIALIZED; 92 | TEST_ASSERT_FALSE(caught); 93 | } USE (true) { 94 | TEST_ASSERT_INT_EQUALS(status, INITIALIZED); 95 | status = USED; 96 | THROW(OOPS, NULL); 97 | } CATCH_ALL { 98 | TEST_ASSERT_INT_EQUALS(status, FINALIZED); 99 | caught = true; 100 | } FINALLY { 101 | TEST_ASSERT_INT_EQUALS(status, FINALIZED); 102 | TEST_ASSERT_TRUE(caught); 103 | } 104 | TEST_ASSERT_INT_EQUALS(status, FINALIZED); 105 | 106 | /* Exception while disposing of the resource */ 107 | status = UNINITIALIZED; 108 | caught = false; 109 | WITH (FINALIZE(true, status, USED, caught, false)) { 110 | TEST_ASSERT_FALSE(caught); 111 | status = INITIALIZED; 112 | } USE (true) { 113 | TEST_ASSERT_INT_EQUALS(status, INITIALIZED); 114 | status = USED; 115 | } CATCH_ALL { 116 | TEST_ASSERT_INT_EQUALS(status, USED); 117 | caught = true; 118 | } FINALLY { 119 | TEST_ASSERT_INT_EQUALS(status, USED); 120 | TEST_ASSERT_TRUE(caught); 121 | } 122 | TEST_ASSERT_INT_EQUALS(status, USED); 123 | TEST_ASSERT_TRUE(caught); 124 | 125 | TEST_PASS; 126 | } 127 | -------------------------------------------------------------------------------- /docs/exceptions4c.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | # Changelog 3 | 4 | All notable changes to this project will be documented in this file. 5 | 6 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), 7 | and this project adheres to [Semantic Versioning](https://semver.org/). 8 | 9 | 10 | ## [4.0.0] 11 | 12 | - Changed license from LGPL to Apache 2. 13 | - Renamed public macros and "keywords". 14 | - Extracted multithreading support to a separate library 15 | [exceptions4c-pthreads](https://github.com/guillermocalvo/exceptions4c-pthreads). 16 | - Extracted lightweight version to a separate library 17 | [exceptions4c-lite](https://github.com/guillermocalvo/exceptions4c-lite). 18 | - Removed non-essential public functions and typedefs. 19 | - Removed built-in exceptions and signal handling. 20 | - Removed support for legacy compilers. 21 | 22 | ### Added 23 | 24 | - Type `e4c_exception_type` 25 | - Type `e4c_debug_info` 26 | - Type `e4c_exception` 27 | - Type `e4c_context` 28 | - Macro `EXCEPTIONS4C` 29 | - Macro `TRY` 30 | - Macro `CATCH` 31 | - Macro `CATCH_ALL` 32 | - Macro `FINALLY` 33 | - Macro `THROW` 34 | - Macro `RETRY` 35 | - Macro `USING` 36 | - Macro `WITH` 37 | - Macro `USE` 38 | - Macro `REACQUIRE` 39 | - Function `e4c_set_context_supplier` 40 | - Function `e4c_get_context` 41 | - Function `e4c_get_exception` 42 | - Function `e4c_is_uncaught` 43 | 44 | 45 | ## [3.0.5] 46 | 47 | - Added lightweight version of the library. 48 | - Added support for generating backtraces via execinfo. 49 | - Minor bugfixes, changes and optimizations. 50 | - Migrated repo to GitHub. 51 | 52 | 53 | ## [3.0.0] 54 | 55 | - Changed license from GPL to LGPL. 56 | - Minor internal changes. 57 | 58 | 59 | ## [2.11.0] 60 | 61 | - Added custom uncaught handler mechanism. 62 | - Added extern "C" block for C++ compatibility. 63 | - Improved current thread termination. 64 | - Other internal changes and bugfixes. 65 | 66 | 67 | ## [2.10.0] 68 | 69 | - Added support for generating backtraces via addr2line. 70 | - Fixed minor bug (dangling contexts). 71 | - Other internal changes. 72 | - Renamed some public macros. 73 | 74 | 75 | ## [2.9.0] 76 | 77 | - Added custom data to thrown exception. 78 | - Improved detection of cause exception. 79 | - Renamed some struct members. 80 | - Other internal changes. 81 | 82 | 83 | ## [2.8.0] 84 | 85 | - Changed assertion macro. 86 | - Other internal changes related to multithreading. 87 | 88 | 89 | ## [2.7.0] 90 | 91 | - Renamed some public macros and functions. 92 | - Internal changes and optimizations. 93 | - Fixed signal-related bug. 94 | 95 | 96 | ## [2.6.0] 97 | 98 | - Added `e4c_exception_type`. 99 | - Added `snprintf` support where available. 100 | - Removed `volatile` qualifier. 101 | 102 | 103 | ## [2.5.0] 104 | 105 | - Enhanced specific compiler, OS, C source version, and PTHREADS detection. 106 | - Minor changes to signal handling. 107 | - Fixed minor bugs. 108 | 109 | 110 | ## [2.4.0] 111 | 112 | - Enhanced compiler detection. 113 | - Fixed minor bugs. 114 | - Fixed compilation error on some compilers. 115 | 116 | 117 | ## [2.3.0] 118 | 119 | - Added macros to rethrow the current exception, retry, and reacquire. 120 | 121 | 122 | ## [2.2.0] 123 | 124 | - Added `volatile` qualifier to some struct members. 125 | - Fixed minor bugs. 126 | 127 | 128 | ## [2.1.0] 129 | 130 | - Added macro to throw with formatted message. 131 | - Added function name to thrown exception. 132 | - Added test suites. 133 | 134 | 135 | ## [2.0.0] 136 | 137 | - Added errno, file, and line info to thrown exception. 138 | - Added detailed message to thrown exception. 139 | - Added cause to thrown exception. 140 | - Added assertion mechanism. 141 | 142 | 143 | ## [1.6.0] 144 | 145 | - Added thread-safe mode information to the library version number. 146 | - Improved thread cancelation. 147 | 148 | 149 | ## [1.5.0] 150 | 151 | - Added mechanism to retrieve library version. 152 | - Enhanced dangling context detection. 153 | - Improved PTHREADS detection. 154 | 155 | 156 | ## [1.4.0] 157 | 158 | - Added integration macro. 159 | 160 | 161 | ## [1.3.0] 162 | 163 | - Added dispose pattern (automatic resource disposal). 164 | - Added multithreading support. 165 | 166 | 167 | ## [1.2.0] 168 | 169 | - Minor changes. 170 | 171 | 172 | ## [1.1.0] 173 | 174 | - Added signal handling. 175 | 176 | 177 | ## [1.0.0] 178 | 179 | Initial release. 180 | 181 | 182 | [1.0.0]: https://github.com/guillermocalvo/exceptions4c/releases/tag/v1.0.0 183 | [1.1.0]: https://github.com/guillermocalvo/exceptions4c/releases/tag/v1.1.0 184 | [1.2.0]: https://github.com/guillermocalvo/exceptions4c/releases/tag/v1.2.0 185 | [1.3.0]: https://github.com/guillermocalvo/exceptions4c/releases/tag/v1.3.0 186 | [1.4.0]: https://github.com/guillermocalvo/exceptions4c/releases/tag/v1.4.0 187 | [1.5.0]: https://github.com/guillermocalvo/exceptions4c/releases/tag/v1.5.0 188 | [1.6.0]: https://github.com/guillermocalvo/exceptions4c/releases/tag/v1.6.0 189 | [2.0.0]: https://github.com/guillermocalvo/exceptions4c/releases/tag/v2.0.0 190 | [2.1.0]: https://github.com/guillermocalvo/exceptions4c/releases/tag/v2.1.0 191 | [2.2.0]: https://github.com/guillermocalvo/exceptions4c/releases/tag/v2.2.0 192 | [2.3.0]: https://github.com/guillermocalvo/exceptions4c/releases/tag/v2.3.0 193 | [2.4.0]: https://github.com/guillermocalvo/exceptions4c/releases/tag/v2.4.0 194 | [2.5.0]: https://github.com/guillermocalvo/exceptions4c/releases/tag/v2.5.0 195 | [2.6.0]: https://github.com/guillermocalvo/exceptions4c/releases/tag/v2.6.0 196 | [2.7.0]: https://github.com/guillermocalvo/exceptions4c/releases/tag/v2.7.0 197 | [2.8.0]: https://github.com/guillermocalvo/exceptions4c/releases/tag/v2.8.0 198 | [2.9.0]: https://github.com/guillermocalvo/exceptions4c/releases/tag/v2.9.0 199 | [2.10.0]: https://github.com/guillermocalvo/exceptions4c/releases/tag/v2.10.0 200 | [2.11.0]: https://github.com/guillermocalvo/exceptions4c/releases/tag/v2.11.0 201 | [3.0.0]: https://github.com/guillermocalvo/exceptions4c/releases/tag/v3.0.0 202 | [3.0.5]: https://github.com/guillermocalvo/exceptions4c/releases/tag/v3.0.5 203 | [4.0.0]: https://github.com/guillermocalvo/exceptions4c/releases/tag/4.0.0 204 | -------------------------------------------------------------------------------- /docs/Doxyfile: -------------------------------------------------------------------------------- 1 | 2 | #--------------------------------------------------------------------------- 3 | # Project related configuration options 4 | #--------------------------------------------------------------------------- 5 | 6 | PROJECT_NAME = "exceptions4c" 7 | PROJECT_NUMBER = 4.0 8 | PROJECT_BRIEF = "Exceptions for C" 9 | PROJECT_LOGO = ./docs/logo.svg 10 | PROJECT_ICON = ./docs/icon.svg 11 | OUTPUT_DIRECTORY = ./docs 12 | CREATE_SUBDIRS = NO 13 | ALWAYS_DETAILED_SEC = YES 14 | FULL_PATH_NAMES = NO 15 | JAVADOC_AUTOBRIEF = YES 16 | TYPEDEF_HIDES_STRUCT = YES 17 | OPTIMIZE_OUTPUT_FOR_C = YES 18 | MARKDOWN_ID_STYLE = GITHUB 19 | 20 | 21 | #--------------------------------------------------------------------------- 22 | # Build related configuration options 23 | #--------------------------------------------------------------------------- 24 | 25 | EXTRACT_ALL = NO 26 | HIDE_UNDOC_MEMBERS = YES 27 | HIDE_SCOPE_NAMES = YES 28 | HIDE_UNDOC_CLASSES = YES 29 | CASE_SENSE_NAMES = NO 30 | SORT_MEMBER_DOCS = NO 31 | 32 | 33 | #--------------------------------------------------------------------------- 34 | # Configuration options related to warning and progress messages 35 | #--------------------------------------------------------------------------- 36 | 37 | WARN_NO_PARAMDOC = YES 38 | WARN_AS_ERROR = YES 39 | 40 | 41 | #--------------------------------------------------------------------------- 42 | # Configuration options related to the input files 43 | #--------------------------------------------------------------------------- 44 | 45 | INPUT = ./src/ \ 46 | ./docs/README.md 47 | FILE_PATTERNS = *.c \ 48 | *.h \ 49 | *.md 50 | EXAMPLE_PATH = ./examples/ 51 | IMAGE_PATH = ./docs 52 | USE_MDFILE_AS_MAINPAGE = ./docs/README.md 53 | 54 | 55 | #--------------------------------------------------------------------------- 56 | # Configuration options related to source browsing 57 | #--------------------------------------------------------------------------- 58 | 59 | VERBATIM_HEADERS = NO 60 | 61 | 62 | #--------------------------------------------------------------------------- 63 | # Configuration options related to the alphabetical class index 64 | #--------------------------------------------------------------------------- 65 | 66 | ALPHABETICAL_INDEX = NO 67 | 68 | 69 | #--------------------------------------------------------------------------- 70 | # Configuration options related to the HTML output 71 | #--------------------------------------------------------------------------- 72 | 73 | GENERATE_HTML = YES 74 | HTML_FOOTER = ./docs/footer.html 75 | DISABLE_INDEX = NO 76 | GENERATE_TREEVIEW = YES 77 | HTML_EXTRA_STYLESHEET = ./docs/doxygen-awesome.css \ 78 | ./docs/doxygen-awesome-sidebar-only.css \ 79 | ./docs/exceptions4c.css 80 | HTML_EXTRA_FILES = ./docs/octicons/alert-16.svg \ 81 | ./docs/octicons/bug-16.svg \ 82 | ./docs/octicons/check-circle-16.svg \ 83 | ./docs/octicons/check-circle-fill-16.svg \ 84 | ./docs/octicons/checkbox-16.svg \ 85 | ./docs/octicons/eye-16.svg \ 86 | ./docs/octicons/info-16.svg \ 87 | ./docs/octicons/light-bulb-16.svg \ 88 | ./docs/octicons/report-16.svg \ 89 | ./docs/octicons/stop-16.svg \ 90 | ./docs/octicons/trash-16.svg 91 | 92 | 93 | #--------------------------------------------------------------------------- 94 | # Configuration options related to the LaTeX output 95 | #--------------------------------------------------------------------------- 96 | 97 | GENERATE_LATEX = NO 98 | 99 | 100 | #--------------------------------------------------------------------------- 101 | # Configuration options related to the RTF output 102 | #--------------------------------------------------------------------------- 103 | 104 | GENERATE_RTF = NO 105 | 106 | 107 | #--------------------------------------------------------------------------- 108 | # Configuration options related to the man page output 109 | #--------------------------------------------------------------------------- 110 | 111 | GENERATE_MAN = NO 112 | 113 | 114 | #--------------------------------------------------------------------------- 115 | # Configuration options related to the XML output 116 | #--------------------------------------------------------------------------- 117 | 118 | GENERATE_XML = NO 119 | 120 | 121 | #--------------------------------------------------------------------------- 122 | # Configuration options related to the DOCBOOK output 123 | #--------------------------------------------------------------------------- 124 | 125 | GENERATE_DOCBOOK = NO 126 | 127 | 128 | #--------------------------------------------------------------------------- 129 | # Configuration options for the AutoGen Definitions output 130 | #--------------------------------------------------------------------------- 131 | 132 | GENERATE_AUTOGEN_DEF = NO 133 | 134 | 135 | #--------------------------------------------------------------------------- 136 | # Configuration options related to the Perl module output 137 | #--------------------------------------------------------------------------- 138 | 139 | GENERATE_PERLMOD = NO 140 | 141 | 142 | #--------------------------------------------------------------------------- 143 | # Configuration options related to the preprocessor 144 | #--------------------------------------------------------------------------- 145 | 146 | PREDEFINED = __bool_true_false_are_defined 147 | SKIP_FUNCTION_MACROS = NO 148 | 149 | 150 | #--------------------------------------------------------------------------- 151 | # Configuration options related to diagram generator tools 152 | #--------------------------------------------------------------------------- 153 | 154 | HAVE_DOT = NO 155 | -------------------------------------------------------------------------------- /tests/testing.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Guillermo Calvo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #define TEST_RESULT_PASS 0 22 | #define TEST_RESULT_FAIL 1 23 | #define TEST_RESULT_SKIP 77 24 | #define TEST_RESULT_HARD 99 25 | 26 | #define TEST_PRINT(stream, ...) \ 27 | do { \ 28 | (void) fprintf(stream, __VA_ARGS__); \ 29 | (void) fflush(stream); \ 30 | } while(0) 31 | #define TEST_PRINT_OUT(...) \ 32 | TEST_PRINT(stdout, __VA_ARGS__) 33 | 34 | #define TEST_PRINT_ERR(...) \ 35 | TEST_PRINT(stderr, __VA_ARGS__) 36 | 37 | #define TEST_PASS \ 38 | do { \ 39 | TEST_PRINT_OUT( \ 40 | "%s:%d [INFO] Test passed.\n", \ 41 | __FILE__, \ 42 | __LINE__ \ 43 | ); \ 44 | exit(TEST_RESULT_PASS); \ 45 | } while(0) 46 | 47 | #define TEST_FAIL(...) \ 48 | do { \ 49 | TEST_PRINT_ERR(__VA_ARGS__); \ 50 | exit(TEST_RESULT_FAIL); \ 51 | } while(0) 52 | 53 | #define TEST_SKIP(reason) \ 54 | do { \ 55 | TEST_PRINT_ERR( \ 56 | "%s:%d [WARNING] Test skipped because" \ 57 | " " reason ".\n", \ 58 | __FILE__, \ 59 | __LINE__ \ 60 | ); \ 61 | exit(TEST_RESULT_SKIP); \ 62 | } while(0) 63 | 64 | # define TEST_ASSERT_THAT(check, action) \ 65 | do { \ 66 | if (!(check)){ \ 67 | action; \ 68 | } \ 69 | } while(0) 70 | 71 | #define TEST_ASSERT(check) \ 72 | TEST_ASSERT_THAT( \ 73 | check, \ 74 | TEST_FAIL( \ 75 | "%s:%d [ERROR] Test failed because" \ 76 | " assertion failed: " #check "\n", \ 77 | __FILE__, \ 78 | __LINE__ \ 79 | ) \ 80 | ) 81 | 82 | #define TEST_ASSERT_EQUALS(check, format, found, expected) \ 83 | TEST_ASSERT_THAT( \ 84 | (check), \ 85 | TEST_FAIL( \ 86 | "%s:%d [ERROR] Test failed because" \ 87 | " `%s` is: " format " (expecting: " format ").\n", \ 88 | __FILE__, \ 89 | __LINE__, \ 90 | #found, \ 91 | (found), \ 92 | (expected) \ 93 | ) \ 94 | ) 95 | 96 | #define TEST_ASSERT_INT_EQUALS(found, expected) \ 97 | TEST_ASSERT_EQUALS(found == expected, "%d", found, expected) 98 | 99 | #define TEST_ASSERT_PTR_EQUALS(found, expected) \ 100 | TEST_ASSERT_EQUALS(found == expected, "%p", (void *) found, (void *) expected) 101 | 102 | #define TEST_ASSERT_CHAR_EQUALS(found, expected) \ 103 | TEST_ASSERT_EQUALS(found == expected, "'%c'", found, expected) 104 | 105 | #define TEST_ASSERT_STR_EQUALS(found, expected) \ 106 | TEST_ASSERT_EQUALS(strcmp(found, expected) == 0, "\"%s\"", found, expected) 107 | 108 | #define TEST_ASSERT_STR_CONTAINS(haystack, needle) \ 109 | TEST_ASSERT_THAT( \ 110 | strstr((haystack), (needle)) != NULL, \ 111 | TEST_FAIL( \ 112 | "%s:%d [ERROR] Test failed because" \ 113 | " `%s` does not contain \"%s\" (found: \"%s\").\n", \ 114 | __FILE__, \ 115 | __LINE__, \ 116 | #haystack, \ 117 | (needle), \ 118 | (haystack) \ 119 | ) \ 120 | ) 121 | 122 | #define TEST_ASSERT_BOOL_EQUALS(found, expected) \ 123 | TEST_ASSERT_THAT( \ 124 | (found) == (expected), \ 125 | TEST_FAIL( \ 126 | "%s:%d [ERROR] Test failed because" \ 127 | " `%s` is: %s (expecting: %s).\n", \ 128 | __FILE__, \ 129 | __LINE__, \ 130 | #found, \ 131 | (found) ? "true" : "false", \ 132 | (expected) ? "true" : "false" \ 133 | ) \ 134 | ) 135 | 136 | #define TEST_ASSERT_TRUE(found) \ 137 | TEST_ASSERT_BOOL_EQUALS(found, true) 138 | 139 | #define TEST_ASSERT_FALSE(found) \ 140 | TEST_ASSERT_BOOL_EQUALS(found, false) 141 | 142 | #define TEST_ASSERT_NULL(pointer) \ 143 | TEST_ASSERT_THAT( \ 144 | (pointer) == NULL, \ 145 | TEST_FAIL( \ 146 | "%s:%d [ERROR] Test failed because" \ 147 | " `%s` is not null.\n", \ 148 | __FILE__, \ 149 | __LINE__, \ 150 | #pointer \ 151 | ) \ 152 | ) 153 | 154 | #define TEST_ASSERT_NOT_NULL(pointer) \ 155 | TEST_ASSERT_THAT( \ 156 | (pointer) != NULL, \ 157 | TEST_FAIL( \ 158 | "%s:%d [ERROR] Test failed because" \ 159 | " `%s` is null.\n", \ 160 | __FILE__, \ 161 | __LINE__, \ 162 | #pointer \ 163 | ) \ 164 | ) 165 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | # 2 | # exceptions4c 3 | # 4 | # Copyright (c) 2025 Guillermo Calvo 5 | # Licensed under the Apache License, Version 2.0 6 | # 7 | 8 | AUTOMAKE_OPTIONS = foreign subdir-objects 9 | 10 | AM_CFLAGS = -Wall -Werror --pedantic -Wno-missing-braces -Wno-dangling-else -coverage -O0 -I$(EXCEPTIONS4C_PATH) 11 | 12 | 13 | # Library 14 | 15 | EXCEPTIONS4C_PATH = src 16 | EXCEPTIONS4C_LIBRARY = lib/libexceptions4c.a 17 | 18 | 19 | # Install 20 | 21 | lib_LIBRARIES = $(EXCEPTIONS4C_LIBRARY) 22 | 23 | include_HEADERS = src/exceptions4c.h 24 | 25 | 26 | # Documentation 27 | 28 | docsdir = $(datadir)/docs/exceptions4c 29 | docs_DATA = docs/* 30 | 31 | 32 | # Cleanup 33 | 34 | CLEANFILES = \ 35 | *.log \ 36 | *.gcda \ 37 | *.gcno \ 38 | *.gcov \ 39 | *.stackdump \ 40 | src/*.gcda \ 41 | src/*.gcno \ 42 | src/*.gcov \ 43 | tests/*.gcda \ 44 | tests/*.gcno \ 45 | tests/*.gcov 46 | 47 | 48 | # Check 49 | 50 | check_PROGRAMS = \ 51 | bin/check/catch-all \ 52 | bin/check/catch-duplicate \ 53 | bin/check/catch-generic \ 54 | bin/check/catch-sigint \ 55 | bin/check/catch-sigsegv \ 56 | bin/check/catch-sigterm \ 57 | bin/check/catch-specific \ 58 | bin/check/catch-unordered \ 59 | bin/check/examples/customization \ 60 | bin/check/examples/pet-store \ 61 | bin/check/examples/pthreads \ 62 | bin/check/examples/signals \ 63 | bin/check/examples/uncaught-handler \ 64 | bin/check/finally \ 65 | bin/check/get-exception \ 66 | bin/check/handler-finalize \ 67 | bin/check/handler-initialize \ 68 | bin/check/handler-terminate \ 69 | bin/check/handler-uncaught \ 70 | bin/check/is-uncaught \ 71 | bin/check/panic-block-catch \ 72 | bin/check/panic-block-next \ 73 | bin/check/panic-block-try \ 74 | bin/check/panic-context \ 75 | bin/check/panic-dangling \ 76 | bin/check/panic-reacquire \ 77 | bin/check/panic-retry \ 78 | bin/check/panic-try \ 79 | bin/check/reacquire \ 80 | bin/check/retry \ 81 | bin/check/throw-cause \ 82 | bin/check/throw-format \ 83 | bin/check/throw-suppressed \ 84 | bin/check/throw-uncaught-1 \ 85 | bin/check/throw-uncaught-2 \ 86 | bin/check/with-use 87 | 88 | TESTS = \ 89 | bin/check/catch-all \ 90 | bin/check/catch-duplicate \ 91 | bin/check/catch-generic \ 92 | bin/check/catch-sigint \ 93 | bin/check/catch-sigsegv \ 94 | bin/check/catch-sigterm \ 95 | bin/check/catch-specific \ 96 | bin/check/catch-unordered \ 97 | bin/check/examples/customization \ 98 | bin/check/examples/pet-store \ 99 | bin/check/examples/pthreads \ 100 | bin/check/examples/signals \ 101 | bin/check/examples/uncaught-handler \ 102 | bin/check/finally \ 103 | bin/check/get-exception \ 104 | bin/check/handler-finalize \ 105 | bin/check/handler-initialize \ 106 | bin/check/handler-terminate \ 107 | bin/check/handler-uncaught \ 108 | bin/check/is-uncaught \ 109 | bin/check/panic-block-catch \ 110 | bin/check/panic-block-next \ 111 | bin/check/panic-block-try \ 112 | bin/check/panic-context \ 113 | bin/check/panic-dangling \ 114 | bin/check/panic-reacquire \ 115 | bin/check/panic-retry \ 116 | bin/check/panic-try \ 117 | bin/check/reacquire \ 118 | bin/check/retry \ 119 | bin/check/throw-cause \ 120 | bin/check/throw-format \ 121 | bin/check/throw-suppressed \ 122 | bin/check/throw-uncaught-1 \ 123 | bin/check/throw-uncaught-2 \ 124 | bin/check/with-use 125 | 126 | XFAIL_TESTS = \ 127 | bin/check/examples/uncaught-handler \ 128 | bin/check/panic-block-catch \ 129 | bin/check/panic-block-next \ 130 | bin/check/panic-block-try \ 131 | bin/check/panic-context \ 132 | bin/check/panic-dangling \ 133 | bin/check/panic-reacquire \ 134 | bin/check/panic-retry \ 135 | bin/check/panic-try \ 136 | bin/check/throw-uncaught-1 \ 137 | bin/check/throw-uncaught-2 138 | 139 | tests: check 140 | 141 | 142 | # Library 143 | 144 | lib_libexceptions4c_a_CFLAGS = -Wall -Werror --pedantic -Wno-missing-braces -I$(EXCEPTIONS4C_PATH) 145 | lib_libexceptions4c_a_SOURCES = src/exceptions4c.c 146 | 147 | 148 | # Tests 149 | 150 | bin_check_catch_all_SOURCES = src/exceptions4c.c tests/catch-all.c 151 | bin_check_catch_duplicate_SOURCES = src/exceptions4c.c tests/catch-duplicate.c 152 | bin_check_catch_generic_SOURCES = src/exceptions4c.c tests/catch-generic.c 153 | bin_check_catch_sigint_SOURCES = src/exceptions4c.c tests/catch-sigint.c 154 | bin_check_catch_sigsegv_SOURCES = src/exceptions4c.c tests/catch-sigsegv.c 155 | bin_check_catch_sigterm_SOURCES = src/exceptions4c.c tests/catch-sigterm.c 156 | bin_check_catch_specific_SOURCES = src/exceptions4c.c tests/catch-specific.c 157 | bin_check_catch_unordered_SOURCES = src/exceptions4c.c tests/catch-unordered.c 158 | bin_check_finally_SOURCES = src/exceptions4c.c tests/finally.c 159 | bin_check_get_exception_SOURCES = src/exceptions4c.c tests/get-exception.c 160 | bin_check_handler_finalize_SOURCES = src/exceptions4c.c tests/handler-finalize.c 161 | bin_check_handler_initialize_SOURCES = src/exceptions4c.c tests/handler-initialize.c 162 | bin_check_handler_terminate_SOURCES = src/exceptions4c.c tests/handler-terminate.c 163 | bin_check_handler_uncaught_SOURCES = src/exceptions4c.c tests/handler-uncaught.c 164 | bin_check_is_uncaught_SOURCES = src/exceptions4c.c tests/is-uncaught.c 165 | bin_check_panic_block_catch_SOURCES = src/exceptions4c.c tests/panic-block-catch.c 166 | bin_check_panic_block_next_SOURCES = src/exceptions4c.c tests/panic-block-next.c 167 | bin_check_panic_block_try_SOURCES = src/exceptions4c.c tests/panic-block-try.c 168 | bin_check_panic_context_SOURCES = src/exceptions4c.c tests/panic-context.c 169 | bin_check_panic_dangling_SOURCES = src/exceptions4c.c tests/panic-dangling.c 170 | bin_check_panic_reacquire_SOURCES = src/exceptions4c.c tests/panic-reacquire.c 171 | bin_check_panic_retry_SOURCES = src/exceptions4c.c tests/panic-retry.c 172 | bin_check_panic_try_SOURCES = src/exceptions4c.c tests/panic-try.c 173 | bin_check_reacquire_SOURCES = src/exceptions4c.c tests/reacquire.c 174 | bin_check_retry_SOURCES = src/exceptions4c.c tests/retry.c 175 | bin_check_throw_cause_SOURCES = src/exceptions4c.c tests/throw-cause.c 176 | bin_check_throw_format_SOURCES = src/exceptions4c.c tests/throw-format.c 177 | bin_check_throw_suppressed_SOURCES = src/exceptions4c.c tests/throw-suppressed.c 178 | bin_check_throw_uncaught_1_SOURCES = src/exceptions4c.c tests/throw-uncaught-1.c 179 | bin_check_throw_uncaught_2_SOURCES = src/exceptions4c.c tests/throw-uncaught-2.c 180 | bin_check_with_use_SOURCES = src/exceptions4c.c tests/with-use.c 181 | 182 | # Examples 183 | 184 | bin_check_examples_customization_LDADD = $(EXCEPTIONS4C_LIBRARY) 185 | bin_check_examples_customization_SOURCES = examples/customization.c 186 | bin_check_examples_pet_store_LDADD = $(EXCEPTIONS4C_LIBRARY) 187 | bin_check_examples_pet_store_SOURCES = examples/pet-store.c 188 | bin_check_examples_pthreads_LDADD = $(EXCEPTIONS4C_LIBRARY) 189 | bin_check_examples_pthreads_SOURCES = examples/pthreads.c 190 | bin_check_examples_signals_LDADD = $(EXCEPTIONS4C_LIBRARY) 191 | bin_check_examples_signals_SOURCES = examples/signals.c 192 | bin_check_examples_uncaught_handler_LDADD = $(EXCEPTIONS4C_LIBRARY) 193 | bin_check_examples_uncaught_handler_SOURCES = examples/uncaught-handler.c 194 | 195 | 196 | # Coverage 197 | 198 | coverage: exceptions4c.c.gcov 199 | 200 | coverage-report: docs/html/coverage/index.html 201 | 202 | docs/html/coverage/index.html: coverage.info 203 | genhtml coverage.info --output-directory docs/html/coverage 204 | 205 | coverage.info: exceptions4c.c.gcov 206 | lcov --ignore-errors mismatch --capture --directory . --output-file coverage.info 207 | 208 | exceptions4c.c.gcov: src/exceptions4c.gcda 209 | gcov --verbose src/exceptions4c.c 210 | 211 | src/exceptions4c.gcda: check 212 | 213 | 214 | # Generate documentation 215 | 216 | docs: docs/html/index.html 217 | 218 | docs/html/index.html: docs/Doxyfile docs/doxygen-awesome.css docs/doxygen-awesome-sidebar-only.css 219 | doxygen docs/Doxyfile 220 | 221 | docs/doxygen-awesome.css: 222 | wget --verbose --output-document docs/doxygen-awesome.css https://github.com/jothepro/doxygen-awesome-css/raw/refs/heads/main/doxygen-awesome.css 223 | 224 | docs/doxygen-awesome-sidebar-only.css: 225 | wget --verbose --output-document docs/doxygen-awesome-sidebar-only.css https://github.com/jothepro/doxygen-awesome-css/raw/refs/heads/main/doxygen-awesome-sidebar-only.css 226 | -------------------------------------------------------------------------------- /examples/pet-store.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Guillermo Calvo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | typedef enum pet_status { ERROR, UNKNOWN, AVAILABLE, PENDING, SOLD } pet_status; 22 | 23 | typedef const struct pet *Pet; 24 | 25 | struct pet { 26 | int id; 27 | const char *name; 28 | pet_status status; 29 | }; 30 | 31 | //! [exception_types] 32 | /* Generic errors */ 33 | const struct e4c_exception_type NOT_ENOUGH_MEMORY = {NULL, "Not enough memory"}; 34 | const struct e4c_exception_type TIMED_OUT = {NULL, "Timed out"}; 35 | 36 | /* Base exception for all pet-related errors */ 37 | const struct e4c_exception_type PET_ERROR = {NULL, "Pet error"}; 38 | 39 | /* Specific types of pet errors */ 40 | const struct e4c_exception_type PET_NOT_FOUND = {&PET_ERROR, "Pet not found"}; 41 | const struct e4c_exception_type PET_STORE_CLOSED = {&PET_ERROR, "Pet store closed"}; 42 | //! [exception_types] 43 | 44 | // Available pets in the store 45 | static struct pet pets[] = { 46 | {.id = 0, .name = "Rocky", .status = AVAILABLE}, 47 | {.id = 1, .name = "Garfield", .status = PENDING}, 48 | {.id = 2, .name = "Rantanplan", .status = SOLD} 49 | }; 50 | 51 | static void pet_free(const void *pet) { 52 | (void) pet; 53 | } 54 | 55 | static Pet pet_clone(const int id) { 56 | for (int index = 0; index < sizeof(pets) / sizeof(pets[0]); index++) { 57 | if (pets[index].id == id) { 58 | return &pets[index]; 59 | } 60 | } 61 | return NULL; 62 | } 63 | 64 | static bool pet_store_is_closed() { 65 | return false; 66 | } 67 | 68 | //! [throw] 69 | /* Returns a pet by id */ 70 | Pet pet_find(int id) { 71 | Pet pet = pet_clone(id); 72 | if (!pet) { 73 | THROW(PET_NOT_FOUND, "Pet %d not found", id); 74 | } 75 | return pet; 76 | } 77 | //! [throw] 78 | 79 | /* Returns a pet by id */ 80 | Pet pet_find_retry(int id) { 81 | static int last_id = -100; 82 | static int attempts = 0; 83 | if (last_id != id) { 84 | last_id = id; 85 | THROW(TIMED_OUT, "First attempt to find %d", id); 86 | } 87 | attempts++; 88 | Pet pet = pet_clone(id); 89 | if (!pet) { 90 | THROW(PET_NOT_FOUND, "Pet %d not found", id); 91 | } 92 | return pet; 93 | } 94 | 95 | #define get_pet_status get_pet_status_try 96 | //! [try] 97 | /* Returns the status of a pet by id */ 98 | pet_status get_pet_status(int id) { 99 | pet_status status = ERROR; 100 | TRY { 101 | status = pet_find(id)->status; 102 | } 103 | return status; 104 | } 105 | //! [try] 106 | #undef get_pet_status 107 | 108 | #define get_pet_status get_pet_status_catch 109 | //! [catch] 110 | /* Returns the status of a pet by id */ 111 | pet_status get_pet_status(int id) { 112 | pet_status status = ERROR; 113 | TRY { 114 | status = pet_find(id)->status; 115 | } CATCH (PET_ERROR) { 116 | status = UNKNOWN; 117 | } CATCH (NOT_ENOUGH_MEMORY) { 118 | abort(); 119 | } 120 | return status; 121 | } 122 | //! [catch] 123 | #undef get_pet_status 124 | 125 | #define get_pet_status get_pet_status_catch_all 126 | //! [catch_all] 127 | /* Returns the status of a pet by id */ 128 | pet_status get_pet_status(int id) { 129 | pet_status status = ERROR; 130 | TRY { 131 | status = pet_find(id)->status; 132 | } CATCH_ALL { 133 | status = UNKNOWN; 134 | } 135 | return status; 136 | } 137 | //! [catch_all] 138 | #undef get_pet_status 139 | 140 | #define get_pet_status get_pet_status_catch_all_get_exception 141 | //! [catch_all_get_exception] 142 | /* Returns the status of a pet by id */ 143 | pet_status get_pet_status(int id) { 144 | pet_status status = ERROR; 145 | TRY { 146 | status = pet_find(id)->status; 147 | } CATCH_ALL { 148 | if (e4c_get_exception()->type == &NOT_ENOUGH_MEMORY) { 149 | abort(); 150 | } 151 | status = UNKNOWN; 152 | } 153 | return status; 154 | } 155 | //! [catch_all_get_exception] 156 | #undef get_pet_status 157 | 158 | #define get_pet_status get_pet_status_retry 159 | #define pet_find pet_find_retry 160 | //! [retry] 161 | /* Returns the status of a pet by id */ 162 | pet_status get_pet_status(int id) { 163 | pet_status status = ERROR; 164 | TRY { 165 | status = pet_find(id)->status; 166 | } CATCH (TIMED_OUT) { 167 | RETRY(3, PET_NOT_FOUND, "Timed out too many times"); 168 | } 169 | return status; 170 | } 171 | //! [retry] 172 | #undef pet_find 173 | #undef get_pet_status 174 | 175 | #define get_pet_status get_pet_status_finally 176 | //! [finally] 177 | /* Returns the status of a pet by id */ 178 | pet_status get_pet_status(int id) { 179 | pet_status status = ERROR; 180 | Pet pet = NULL; 181 | TRY { 182 | pet = pet_find(id); 183 | status = pet->status; 184 | } CATCH (PET_NOT_FOUND) { 185 | status = UNKNOWN; 186 | } FINALLY { 187 | pet_free(pet); 188 | } 189 | return status; 190 | } 191 | //! [finally] 192 | #undef get_pet_status 193 | 194 | #define get_pet_status get_pet_status_using 195 | //! [using] 196 | /* Returns the status of a pet by id */ 197 | pet_status get_pet_status(int id) { 198 | pet_status status = ERROR; 199 | Pet pet = NULL; 200 | USING (pet = pet_find(id), pet != NULL, pet_free(pet)) { 201 | status = pet->status; 202 | } 203 | return status; 204 | } 205 | //! [using] 206 | #undef get_pet_status 207 | 208 | #define get_pet_status get_pet_status_using_catch 209 | //! [using_catch] 210 | /* Returns the status of a pet by id */ 211 | pet_status get_pet_status(int id) { 212 | pet_status status = ERROR; 213 | Pet pet = NULL; 214 | USING (pet = pet_find(id), pet != NULL, pet_free(pet)) { 215 | status = pet->status; 216 | } CATCH (PET_NOT_FOUND) { 217 | status = UNKNOWN; 218 | } 219 | return status; 220 | } 221 | //! [using_catch] 222 | #undef get_pet_status 223 | 224 | #define get_pet_status get_pet_status_with_use 225 | //! [with_use] 226 | /* Returns the status of a pet by id */ 227 | pet_status get_pet_status(int id) { 228 | pet_status status = ERROR; 229 | Pet pet = NULL; 230 | WITH (pet_free(pet)) { 231 | if (pet_store_is_closed()) { 232 | THROW(PET_STORE_CLOSED, NULL); 233 | } 234 | pet = pet_find(id); 235 | } USE (pet != NULL) { 236 | status = pet->status; 237 | } CATCH (PET_NOT_FOUND) { 238 | status = UNKNOWN; 239 | } 240 | return status; 241 | } 242 | //! [with_use] 243 | #undef get_pet_status 244 | 245 | #define get_pet_status get_pet_status_reacquire 246 | #define pet_find pet_find_retry 247 | //! [reacquire] 248 | /* Returns the status of a pet by id */ 249 | pet_status get_pet_status(int id) { 250 | pet_status status = ERROR; 251 | Pet pet = NULL; 252 | USING (pet = pet_find(id), pet != NULL, pet_free(pet)) { 253 | status = pet->status; 254 | } CATCH (TIMED_OUT) { 255 | REACQUIRE(3, PET_NOT_FOUND, "Timed out too many times"); 256 | } 257 | return status; 258 | } 259 | //! [reacquire] 260 | #undef pet_find 261 | #undef get_pet_status 262 | 263 | void process_data(int argc, char * argv[]) { /* NOSONAR */ 264 | printf("Processing data\n"); 265 | } 266 | 267 | #define main main_is_uncaught 268 | //! [is_uncaught] 269 | int main(int argc, char * argv[]) { 270 | TRY { 271 | process_data(argc, argv); 272 | } FINALLY { 273 | if (e4c_is_uncaught()) { 274 | fprintf(stderr, "Fatal error while processing data.\n"); 275 | } else { 276 | fprintf(stdout, "Data processed successfully.\n"); 277 | } 278 | } 279 | return EXIT_SUCCESS; 280 | } 281 | //! [is_uncaught] 282 | #undef main 283 | 284 | # define FAIL \ 285 | ((void) fprintf(stderr, "Error: %s:%d\n", __FILE__, __LINE__), abort()) 286 | 287 | int main(void) { /* NOSONAR */ 288 | if (get_pet_status_try(0) != AVAILABLE) { 289 | FAIL; 290 | } 291 | 292 | if (get_pet_status_try(1) != PENDING) { 293 | FAIL; 294 | } 295 | 296 | if (get_pet_status_try(2) != SOLD) { 297 | FAIL; 298 | } 299 | 300 | if (get_pet_status_catch(0) != AVAILABLE) { 301 | FAIL; 302 | } 303 | 304 | if (get_pet_status_catch(1) != PENDING) { 305 | FAIL; 306 | } 307 | 308 | if (get_pet_status_catch(2) != SOLD) { 309 | FAIL; 310 | } 311 | 312 | if (get_pet_status_catch(-1) != UNKNOWN) { 313 | FAIL; 314 | } 315 | 316 | if (get_pet_status_catch_all(0) != AVAILABLE) { 317 | FAIL; 318 | } 319 | 320 | if (get_pet_status_catch_all(1) != PENDING) { 321 | FAIL; 322 | } 323 | 324 | if (get_pet_status_catch_all(2) != SOLD) { 325 | FAIL; 326 | } 327 | 328 | if (get_pet_status_catch_all(-1) != UNKNOWN) { 329 | FAIL; 330 | } 331 | 332 | if (get_pet_status_catch_all_get_exception(0) != AVAILABLE) { 333 | FAIL; 334 | } 335 | 336 | if (get_pet_status_catch_all_get_exception(1) != PENDING) { 337 | FAIL; 338 | } 339 | 340 | if (get_pet_status_catch_all_get_exception(2) != SOLD) { 341 | FAIL; 342 | } 343 | 344 | if (get_pet_status_catch_all_get_exception(-1) != UNKNOWN) { 345 | FAIL; 346 | } 347 | 348 | if (get_pet_status_finally(0) != AVAILABLE) { 349 | FAIL; 350 | } 351 | 352 | if (get_pet_status_finally(1) != PENDING) { 353 | FAIL; 354 | } 355 | 356 | if (get_pet_status_finally(2) != SOLD) { 357 | FAIL; 358 | } 359 | 360 | if (get_pet_status_finally(-1) != UNKNOWN) { 361 | FAIL; 362 | } 363 | 364 | if (get_pet_status_retry(0) != AVAILABLE) { 365 | FAIL; 366 | } 367 | 368 | if (get_pet_status_retry(1) != PENDING) { 369 | FAIL; 370 | } 371 | 372 | if (get_pet_status_retry(2) != SOLD) { 373 | FAIL; 374 | } 375 | 376 | if (get_pet_status_using_catch(0) != AVAILABLE) { 377 | FAIL; 378 | } 379 | 380 | if (get_pet_status_using_catch(1) != PENDING) { 381 | FAIL; 382 | } 383 | 384 | if (get_pet_status_using_catch(2) != SOLD) { 385 | FAIL; 386 | } 387 | 388 | if (get_pet_status_using_catch(-1) != UNKNOWN) { 389 | FAIL; 390 | } 391 | 392 | if (get_pet_status_with_use(0) != AVAILABLE) { 393 | FAIL; 394 | } 395 | 396 | if (get_pet_status_with_use(1) != PENDING) { 397 | FAIL; 398 | } 399 | 400 | if (get_pet_status_with_use(2) != SOLD) { 401 | FAIL; 402 | } 403 | 404 | if (get_pet_status_with_use(-1) != UNKNOWN) { 405 | FAIL; 406 | } 407 | 408 | if (get_pet_status_reacquire(0) != AVAILABLE) { 409 | FAIL; 410 | } 411 | 412 | if (get_pet_status_reacquire(1) != PENDING) { 413 | FAIL; 414 | } 415 | 416 | if (get_pet_status_reacquire(2) != SOLD) { 417 | FAIL; 418 | } 419 | 420 | return EXIT_SUCCESS; 421 | } 422 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
5 | 6 | # Introduction 7 | 8 | Bring the power of exceptions to your C applications! 9 | 10 | ![][EXAMPLE] 11 | 12 | > [!NOTE] 13 | > This library provides you with a set of macros and functions that map the exception handling semantics you are 14 | > probably already used to. 15 | 16 | 17 | # Getting Started 18 | 19 | ## Adding Exceptions to Your Project 20 | 21 | This library consists of two files: 22 | 23 | - exceptions4c.h 24 | - exceptions4c.c 25 | 26 | To use it in your project, include the header file in your source code files. 27 | 28 | ```c 29 | #include 30 | ``` 31 | 32 | And then link your program against the library code. 33 | 34 | > [!TIP] 35 | > There is also a lightweight version of this library, intended for small projects and embedded systems. 36 | > [exceptions4c-lite][EXCEPTIONS4C_LITE] is a header-only library that provides the core functionality of exceptions4c 37 | > in just one file. 38 | 39 | ## Defining Exception Types 40 | 41 | Create meaningful exceptions that reflect problematic situations in the program. 42 | 43 | @snippet pet-store.c exception_types 44 | 45 | An exception type is [a simple structure](#e4c_exception_type) with an optional supertype and a default error message. 46 | 47 | > [!NOTE] 48 | > Exception types create a hierarchy, where a more specific type can be built upon a more generic one. 49 | 50 | # Basic Usage 51 | 52 | Exception handling lets a program deal with errors without crashing. When something goes wrong, the program pauses its 53 | normal flow, jumps to code that handles the issue, and then either recovers or exits cleanly. 54 | 55 | This library provides the following macros that are used to handle exceptions: 56 | 57 | - #THROW 58 | - #TRY 59 | - #CATCH 60 | - #FINALLY 61 | 62 | ## Throwing Exceptions 63 | 64 | When we #THROW an exception, the flow of the program moves to the appropriate #CATCH block. If the exception is not 65 | handled by any of the blocks in the current function, it propagates up the call stack to the function that called the 66 | current function. This continues until the top level of the program is reached. If no block handles the exception, the 67 | program terminates and an error message is printed to the console. 68 | 69 | Use #THROW to trigger an exception when something goes wrong. 70 | 71 | @snippet pet-store.c throw 72 | 73 | When we #THROW an exception, the flow of the program moves from the #TRY block to the appropriate #CATCH block. If the 74 | exception is not handled by any of the blocks in the current function, it propagates up the call stack to the function 75 | that called the current function. This continues until the top level of the program is reached. If no block handles the 76 | exception, the program terminates and an error message is printed to the console. 77 | 78 | > [!NOTE] 79 | > Error messages can be formatted, just as you would with `printf`. Additionally, if you don't provide an error message, 80 | > the default one for that exception type will be used. 81 | 82 | ## Trying Risky Code 83 | 84 | Use a #TRY block to wrap code that might cause an exception. 85 | 86 | @snippet pet-store.c try 87 | 88 | These code blocks, by themselves, don't do anything special. But they allow the introduction of other blocks that do 89 | serve specific purposes. 90 | 91 | > [!TIP] 92 | > A single #TRY block must be followed by one or more #CATCH blocks to handle the errors, and an optional #FINALLY block 93 | > to execute cleanup code. 94 | 95 | > [!CAUTION] 96 | > Never exit these blocks using `goto`, `break`, `continue`, or `return`. 97 | 98 | ## Catching Exceptions 99 | 100 | To prevent the program from crashing, exceptions need to be handled properly in designated sections of the code. 101 | 102 | ### Handling Specific Types of Exceptions 103 | 104 | Use a #CATCH block to handle a specific type of exceptions when they occur. 105 | 106 | @snippet pet-store.c catch 107 | 108 | If the type in the #CATCH block is the same as (or a supertype of) the thrown exception, then the block will be used to 109 | handle it. 110 | 111 | One or more #CATCH blocks can follow a #TRY block. Each #CATCH block must specify the type of exception it handles. If 112 | its type doesn't match the thrown exception, then that block is ignored, and the exception may be caught by the 113 | following blocks. 114 | 115 | > [!IMPORTANT] 116 | > When looking for a match, #CATCH blocks are inspected in the order they appear. If you place a generic handler before 117 | > a more specific one, the second block will be unreachable. 118 | 119 | ### Handling All Kinds of Exceptions 120 | 121 | On the other hand, the #CATCH_ALL block is a special block that can handle all types of exceptions. 122 | 123 | @snippet pet-store.c catch_all 124 | 125 | Only one #CATCH_ALL block is allowed per #TRY block, and it must appear after all type-specific #CATCH blocks if any are 126 | present. 127 | 128 | ### Retrieving the Current Exception 129 | 130 | Use #e4c_get_exception to retrieve the exception currently being handled. 131 | 132 | @snippet pet-store.c catch_all_get_exception 133 | 134 | > [!TIP] 135 | > This allows for inspection and further handling of the error, based on both its type and the detailed context of the 136 | > situation. 137 | 138 | ### Trying Again 139 | 140 | Use #RETRY to restart the previous #TRY block that threw an exception. 141 | 142 | @snippet pet-store.c retry 143 | 144 | > [!NOTE] 145 | > If the block has already been restarted too many times, then #RETRY will throw the provided exception. 146 | 147 | ## Ensuring Cleanup 148 | 149 | A #FINALLY block always runs, no matter whether an exception happens or not. 150 | 151 | @snippet pet-store.c finally 152 | 153 | This block is optional. And, for each #TRY block, there can be only one #FINALLY block. If an exception occurs, the 154 | #FINALLY block is executed after the #CATCH or block that can handle it. Otherwise, it is executed after the #TRY block. 155 | 156 | > [!TIP] 157 | > Use #e4c_is_uncaught to determine whether the thrown exception hasn't been handled yet. 158 | 159 | 160 | # Advanced Usage 161 | 162 | ## Dispose Pattern 163 | 164 | This is a powerful design pattern for resource management. It is a clean and terse way to handle the acquisition and 165 | disposal of all kinds of resources. 166 | 167 | These macros will help you make sure that no resource is leaked in your program. 168 | 169 | - #USING 170 | - #WITH ... #USE 171 | 172 | ### Simple Resource Acquisition 173 | 174 | A #USING block allows you to easily acquire and dispose of a resource. It is similar to a `for` statement, because it 175 | receives three comma-separated expressions that will be evaluated in order. 176 | 177 | - An **acquisition** expression that will try to acquire the resource. 178 | - A **test** expression that defines the condition for using the resource. 179 | - A **disposal** expression that will dispose of the resource. 180 | 181 | Both these expressions and the code block that uses the resource are free to throw exceptions. 182 | 183 | @snippet pet-store.c using 184 | 185 | 1. The resource will be acquired, using the expression `pet = pet_find(id)`. 186 | 2. If the expression `pet != NULL` holds true, then the #USING block will be executed. 187 | 3. The resource `pet` will be disposed of, using the expression `pet_free(pet)`, no matter whether an exception happens 188 | or not. 189 | 190 | You can append #CATCH blocks to deal with exceptions that may happen during the manipulation of the resource. Just 191 | remember: by the time the #CATCH block is executed, the resource will already have been disposed of. 192 | 193 | @snippet pet-store.c using_catch 194 | 195 | > [!TIP] 196 | > You can even append a #FINALLY block for cleanup code other than disposing of the resource. 197 | 198 | ### Complex Resource Acquisition 199 | 200 | The #WITH and #USE blocks come in pairs. They are used when the steps to acquire a resource are more complex than simply 201 | evaluating an expression. It works exactly like the #USING block, except that you can write the code block responsible 202 | for acquiring the resource. 203 | 204 | @snippet pet-store.c with_use 205 | 206 | > [!TIP] 207 | > You can also append #CATCH blocks and an optional #FINALLY block. 208 | 209 | ### Trying Acquisition Again 210 | 211 | Use #REACQUIRE to restart the previous #USING or #WITH block that threw an exception. 212 | 213 | @snippet pet-store.c reacquire 214 | 215 | > [!NOTE] 216 | > If the block has already been restarted too many times, then #RETRY will throw the provided exception. 217 | 218 | ## Customization 219 | 220 | To customize the way this library behaves you may configure a structure that represents the 221 | [exception context](#e4c_context) of the program. 222 | 223 | ### Retrieving the Exception Context 224 | 225 | Use #e4c_get_context to retrieve the current [exception context](#e4c_context) of the program. 226 | 227 | @snippet customization.c get_context 228 | 229 | Then use this object to set up different handlers. 230 | 231 | ### Custom Exception Initializer 232 | 233 | Exceptions support [custom data](#e4c_exception.data). By default, this data is left uninitialized when an exception is 234 | thrown. 235 | 236 | You can set a custom [exception initializer](#e4c_context.initialize_exception) and your function will be executed 237 | whenever an exception is thrown. 238 | 239 | @snippet customization.c initialize_exception 240 | 241 | > [!TIP] 242 | > For example, you could use this opportunity to capture the entire stacktrace of your program. 243 | 244 | ### Custom Exception Finalizer 245 | 246 | You can also set a [exception finalizer](#e4c_context.finalize_exception) to execute your function whenever an exception 247 | is deleted. 248 | 249 | @snippet customization.c finalize_exception 250 | 251 | > [!TIP] 252 | > This allows you to free any resources you acquired when you initialized an exception's custom data. 253 | 254 | ### Custom Uncaught Handler 255 | 256 | By default, when an exception reaches the top level of the program, it gets printed to the standard error stream. 257 | 258 | You can customize this behavior by setting the [uncaught handler](#e4c_context.uncaught_handler) to a custom function 259 | that will be executed in the event of an uncaught exception. 260 | 261 | @snippet uncaught-handler.c uncaught_handler 262 | 263 | > [!TIP] 264 | > Instead of simply using `stderr` you could save an error report in a local file. 265 | 266 | ### Custom Termination Handler 267 | 268 | After the uncaught handler has been executed, the program is terminated by calling `exit(EXIT_FAILURE)`. 269 | 270 | You can make the library do anything else by setting the [termination handler](#e4c_context.termination_handler) to 271 | execute a function in the event of program termination. 272 | 273 | @snippet customization.c termination_handler 274 | 275 | > [!TIP] 276 | > In a multithreaded program, you may want to cancel the current thread, instead of terminating the whole program. 277 | 278 | ### Exception Context Supplier 279 | 280 | By default, a predefined exception context is provided and used by the library. But you can create a supplying function 281 | and pass it to #e4c_set_context_supplier so you are in full control of your program's exception context. 282 | 283 | @snippet customization.c set_context_supplier 284 | 285 | > [!TIP] 286 | > This mechanism can be useful to provide a concurrent exception handler. For example, your custom context supplier 287 | > could return different instances, depending on which thread is active. 288 | 289 | ## Multithreading 290 | 291 | There is an extension for this library, intended for multithreaded programs. 292 | [exceptions4c-pthreads][EXCEPTIONS4C_PTHREADS] allows you to safely and concurrently use exceptions. 293 | 294 | All you have to do is set the exception context supplier, so that each POSIX thread gets its own exception context. 295 | 296 | @snippet pthreads.c setup 297 | 298 | In the event of an uncaught exception, instead of terminating the program, only the current thread will be canceled. 299 | 300 | > [!NOTE] 301 | > [Read the docs][EXCEPTIONS4C_PTHREADS_DOCS] for more information about this extension. 302 | 303 | ## Signal Handling 304 | 305 | You can turn some standard signals such as [`SIGTERM`][SIGTERM], [`SIGFPE`][SIGFPE], and [`SIGSEGV`][SIGSEGV] into 306 | exceptions so they can be handled in a regular #CATCH block. For example, you could do that to prevent your program from 307 | crashing when a null pointer is dereferenced. 308 | 309 | @snippet signals.c null_pointer 310 | 311 | However, it's easy to enter undefined behavior territory, due to underspecified behavior and significant implementation 312 | variations regarding signal delivery while a signal handler is executed, so use this technique with caution. 313 | 314 | > [!IMPORTANT] 315 | > Keep in mind that the behavior is undefined when [`signal`][SIGNAL] is used in a multithreaded program. 316 | 317 | 318 | # Additional Info 319 | 320 | ## Compatibility 321 | 322 | This library relies on modern C features such as [designated initializers][DESIGNATED_INITIALIZERS], 323 | [compound literals][COMPOUND_LITERALS], and [`__VA_OPT__`][__VA_OPT__]. 324 | 325 | > [!TIP] 326 | > If you need support for older compilers, you can try [exceptions4c-lite][EXCEPTIONS4C_LITE]. It's header-only and 327 | > fully compatible with ANSI C. And if you're looking for a cleaner, safer, and more modern approach to error handling 328 | > that doesn't involve throwing or catching exceptions, you may want to take a look at [Result Library][RESULT_LIBRARY]. 329 | 330 | ## Caveat 331 | 332 | Exception handling is based on standard C library functions [`setjmp`][SETJMP] to save the current execution context 333 | and [`longjmp`][LONGJMP] to restore it. According to the documentation: 334 | 335 | > Upon return to the scope of [`setjmp`][SETJMP]: 336 | > - all accessible objects, floating-point status flags, and other components of the abstract machine have the same 337 | > values as they had when [`longjmp`][LONGJMP] was executed, 338 | > - except for the non-[volatile][VOLATILE] local variables in the function containing the invocation of 339 | > [`setjmp`][SETJMP], whose values are indeterminate if they have been changed since the [`setjmp`][SETJMP] 340 | > invocation. 341 | 342 | Since each #TRY block invokes [`setjmp`][SETJMP], modified local variables in scope must be [`volatile`][VOLATILE]. 343 | 344 | ## Similar Projects 345 | 346 | There are other exception handling implementations and libraries. 347 | 348 |
349 | Here you can find some of them, in no particular order. 350 | 351 | - [Exceptions in C](https://web.archive.org/web/20141228062604/http://ldeniau.home.cern.ch/ldeniau/html/exception/exception.html) 352 | By Laurent Deniau 353 | 354 | - [Implementing Exceptions in C Programming language](http://adomas.org/excc/) 355 | By Adomas Paltanavičius 356 | 357 | - [Exceptions in C](https://web.archive.org/web/20090627200944/https://math.umd.edu/~asnowden/c-except.html) 358 | and 359 | [LIBXC](http://web.archive.org/web/20051217142403/http://www.math.umd.edu/~asnowden/libxc/ "A C-library which wraps LIBC and implements exceptions") 360 | By Andrew Snowden 361 | 362 | - [Handling exceptions in C code](https://www.swig.org/Doc1.1/HTML/Exceptions.html#n2) 363 | SWIG (Simplified Wrapper and Interface Generator) 364 | 365 | - [Exception Handling in C without C++](http://www.on-time.com/ddj0011.htm) 366 | By Tom Schotland and Peter Petersen 367 | 368 | - [Trycatch library](http://llg.cubic.org/trycatch/) 369 | By Michael Stickel and Dirk Jagdmann 370 | 371 | - [XXL](https://web.archive.org/web/20120106082715/http://zork.org/xxl/) 372 | By Matt Messier and John Viega 373 | 374 | - [libex](http://code.google.com/p/libex/ "Exception handling and RAII for C") 375 | By Sandro Magi 376 | 377 | - [libexcept](http://sourceforge.net/projects/libexcept/ "An exceptions framework for the C programming language") 378 | By Simon Howard 379 | 380 | - [cexcept](http://www.nicemice.net/cexcept/ "try/catch/throw exception-handling interface for C") 381 | By Adam M. Costello and Cosmin Truta 382 | 383 | - [A Manual of C Style](http://www.cs.uiowa.edu/~jones/syssoft/style.html#except "Exception Handling") 384 | By Douglas W. Jones 385 | 386 | - [Ex package](http://cexcept.sourceforge.net/ex.txt.gz "Exception handling package") 387 | By Douglas A. Gwyn 388 | 389 | - [GEF](http://web.archive.org/web/20071210184301/http://home.rochester.rr.com/bigbyofrocny/GEF/ "General Exception-Handling Facility") 390 | By Bruce W. Bigby 391 | 392 | - [cexception](http://sourceforge.net/apps/trac/cexception/ "Simple exception handling in C") 393 | and 394 | [ThrowTheSwitch](https://github.com/ThrowTheSwitch/CException "Simple exception handling in C") 395 | By Mark VanderVoord 396 | 397 | - [EXC](https://www.ibiblio.org/pub/historic-linux/ftp-archives/sunsite.unc.edu/Sep-29-1996/devel/lang/c/exc-1.0.tar.gz "An Exception Handling Library for ANSI-C") 398 | By Taj Khattra 399 | 400 | - [libexception](https://github.com/hollow/libexception "Exception handling library for C") 401 | By Benedikt Böhm 402 | 403 | - [cii](http://code.google.com/p/cii/source/browse/trunk/include/except.h "C Interfaces and Implementations") 404 | By David R. Hanson 405 | 406 | - [Exceptions in C with Longjmp and Setjmp](http://www.di.unipi.it/~nids/docs/longjump_try_trow_catch.html) 407 | By Francesco Nidito 408 | 409 | - [sauf](https://gist.github.com/kulp/1348052 "A toy exception handling system for C") 410 | By Darren Kulp 411 | 412 | - [errhdl](http://www.public-software-group.org/errhdl "Error Handler for C") 413 | By Jan Behrens 414 | 415 | - [A Proposal for Exception Handling in C](https://web.archive.org/web/20120122022844/http://lyngvig.org/a-proposal-for-exception-handling-in-c.ashx) 416 | By Mikael Lyngvig 417 | 418 | - [Implementing Exceptions in C](http://bitsavers.informatik.uni-stuttgart.de/pdf/dec/tech_reports/SRC-RR-40.pdf) 419 | By Eric S. Roberts 420 | 421 | - [Complete exception handling for pure C code](https://github.com/takanuva/ANSI-ISO-C-Exception-Handling) 422 | By Paulo H. "Taka" Torrens 423 | 424 | - [OSSP ex](http://www.ossp.org/pkg/lib/ex/) 425 | By Ralf S. Engelschall 426 | 427 |
428 | 429 | ## Releases 430 | 431 | This library adheres to [Semantic Versioning][SEMVER]. All notable changes for each version are documented in a 432 | [change log][CHANGELOG]. 433 | 434 | Head over to GitHub for the [latest release][LATEST_RELEASE]. 435 | 436 | [![Latest Release][BADGE_LATEST_RELEASE]][LATEST_RELEASE] 437 | 438 | ## Source Code 439 | 440 | The source code is [available on GitHub][SOURCE_CODE]. 441 | 442 | [![Fork me on GitHub][BADGE_GITHUB]][SOURCE_CODE] 443 | 444 | 445 | [BADGE_GITHUB]: https://img.shields.io/badge/Fork_me_on_GitHub-black?logo=github 446 | [BADGE_LATEST_RELEASE]: https://img.shields.io/github/v/release/guillermocalvo/exceptions4c 447 | [CHANGELOG]: https://github.com/guillermocalvo/exceptions4c/blob/main/CHANGELOG.md 448 | [COMPOUND_LITERALS]: https://gcc.gnu.org/onlinedocs/gcc-3.3/gcc/Compound-Literals.html 449 | [DESIGNATED_INITIALIZERS]: https://gcc.gnu.org/onlinedocs/gcc-3.3/gcc/Designated-Inits.html 450 | [EXAMPLE]: example.png 451 | [EXCEPTIONS4C_LITE]: https://github.com/guillermocalvo/exceptions4c-lite 452 | [EXCEPTIONS4C_PTHREADS]: https://github.com/guillermocalvo/exceptions4c-pthreads 453 | [EXCEPTIONS4C_PTHREADS_DOCS]: https://exceptions4c-pthreads.guillermo.dev 454 | [LATEST_RELEASE]: https://github.com/guillermocalvo/exceptions4c/releases/latest 455 | [LONGJMP]: https://devdocs.io/c/program/longjmp 456 | [RESULT_LIBRARY]: https://result.guillermo.dev/ 457 | [SEMVER]: https://semver.org/ 458 | [SETJMP]: https://devdocs.io/c/program/setjmp 459 | [SIGFPE]: https://devdocs.io/c/program/sig_types "Erroneous arithmetic operation (such as divide by zero)" 460 | [SIGNAL]: https://devdocs.io/c/program/signal 461 | [SIGSEGV]: https://devdocs.io/c/program/sig_types "Invalid memory access (segmentation fault)" 462 | [SIGTERM]: https://devdocs.io/c/program/sig_types "Termination request, sent to the program" 463 | [SOURCE_CODE]: https://github.com/guillermocalvo/exceptions4c 464 | [TYPEOF]: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2899.htm 465 | [VOLATILE]: https://devdocs.io/c/language/volatile 466 | [__VA_OPT__]: https://open-std.org/JTC1/SC22/WG14/www/docs/n3033.htm 467 | -------------------------------------------------------------------------------- /src/exceptions4c.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Guillermo Calvo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * Implementation of the exception handling library for C. 19 | * 20 | * 21 | * 22 | * 23 | * 24 | * @file exceptions4c.c 25 | * @version 4.0.0 26 | * @author [Guillermo Calvo] 27 | * @copyright Licensed under [Apache 2.0] 28 | * @see For more information, visit the [project on GitHub]. 29 | * 30 | * [Guillermo Calvo]: https://guillermo.dev 31 | * [Apache 2.0]: http://www.apache.org/licenses/LICENSE-2.0 32 | * [project on GitHub]: https://github.com/guillermocalvo/exceptions4c 33 | */ 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | /** 42 | * @internal 43 | * @brief Represents the execution stage of the current exception block. 44 | */ 45 | enum block_stage { 46 | /** 47 | * @internal 48 | * The exception block has started. 49 | */ 50 | BEGINNING, 51 | 52 | /** 53 | * @internal 54 | * The exception block is [acquiring a resource](#WITH). 55 | */ 56 | ACQUIRING, 57 | 58 | /** 59 | * @internal 60 | * The exception block is [trying something](#TRY) or [using a resource](#USE). 61 | */ 62 | TRYING, 63 | 64 | /** 65 | * @internal 66 | * The exception block is [disposing of a resource](#WITH). 67 | */ 68 | DISPOSING, 69 | 70 | /** 71 | * @internal 72 | * The exception block is [catching an exception](#CATCH). 73 | */ 74 | CATCHING, 75 | 76 | /** 77 | * @internal 78 | * The exception block is [finalizing](#FINALLY). 79 | */ 80 | FINALIZING, 81 | 82 | /** 83 | * @internal 84 | * The exception block has finished. 85 | */ 86 | DONE 87 | }; 88 | 89 | /** 90 | * @internal 91 | * @brief Represents an exception block. 92 | */ 93 | struct e4c_block { 94 | /** 95 | * A possibly-null pointer to the outer exception block. 96 | */ 97 | struct e4c_block *outer_block; 98 | 99 | /** 100 | * The stage of this block. 101 | */ 102 | enum block_stage stage; 103 | 104 | /** 105 | * Whether this block currently has an uncaught exception. 106 | */ 107 | bool uncaught; 108 | 109 | /** 110 | * A possibly-null pointer to the currently thrown exceptions. 111 | */ 112 | struct e4c_exception *exception; 113 | 114 | /** 115 | * Current number of times the #TRY block has been attempted. 116 | */ 117 | int retry_attempts; 118 | 119 | /** 120 | * Current number of times the #WITH block has been attempted. 121 | */ 122 | int reacquire_attempts; 123 | 124 | /** 125 | * The execution context of this exception block. 126 | */ 127 | e4c_env env; 128 | }; 129 | 130 | /* Causes abnormal program termination due to a fatal error. */ 131 | static noreturn void panic(const char *error_message, const struct e4c_debug_info *debug); 132 | 133 | /* Allocates memory for an object; panics if not enough memory. */ 134 | static void *allocate(size_t size, const char *error_message, const struct e4c_debug_info *debug); 135 | 136 | /* Retrieves the current exception context; panics if NULL. */ 137 | static struct e4c_context *get_context(const struct e4c_debug_info *debug); 138 | 139 | /* Retrieves the current exception block (if any). */ 140 | static struct e4c_block *e4c_get_block(void); 141 | 142 | /* Checks for dangling exception blocks at program exit. */ 143 | static void cleanup_default_context(void); 144 | 145 | /* Creates and propagates a new exception. */ 146 | static void throw( 147 | const struct e4c_context *context, const struct e4c_exception_type *type, const char *name, int error_number, 148 | const struct e4c_debug_info *debug, const char *format, va_list arguments_list); 149 | 150 | /* Propagates the supplied exception in the supplied context. */ 151 | static void propagate(const struct e4c_context *context, struct e4c_exception *exception); 152 | 153 | /* Returns the current exception block stage; panics if NULL. */ 154 | static enum block_stage get_stage(const struct e4c_debug_info *debug); 155 | 156 | /* Deletes the supplied exception, along with its cause. */ 157 | static void delete_exception(const struct e4c_context *context, struct e4c_exception *exception); 158 | 159 | /* Prints debug info (if available) to the standard error output. */ 160 | static void print_debug_info(const struct e4c_debug_info *debug); 161 | 162 | /* Prints the supplied exception to the standard error output. */ 163 | static void print_exception(const struct e4c_exception *exception, bool is_cause); 164 | 165 | /* Determines if the supplied type extends from the supplied supertype. */ 166 | static bool extends(const struct e4c_exception_type *type, const struct e4c_exception_type *supertype); 167 | 168 | /** 169 | * Stores the exception context supplier. 170 | */ 171 | static struct e4c_context * (*context_supplier)(void) = NULL; 172 | 173 | /** 174 | * Default exception context of the program when no custom supplier is provided. 175 | */ 176 | static struct e4c_context default_context = { 177 | ._innermost_block = NULL, 178 | .initialize_exception = NULL, 179 | .finalize_exception = NULL, 180 | .uncaught_handler = NULL 181 | }; 182 | 183 | /** 184 | * Flag that determines if the exception system has been already initialized. 185 | */ 186 | static bool is_cleanup_registered = false; 187 | 188 | void e4c_set_context_supplier(struct e4c_context * (*supplier)(void)) { 189 | context_supplier = supplier; 190 | } 191 | 192 | struct e4c_context *e4c_get_context(void) { 193 | return context_supplier != NULL ? context_supplier() : &default_context; 194 | } 195 | 196 | const struct e4c_exception *e4c_get_exception(void) { 197 | const struct e4c_block *block = e4c_get_block(); 198 | return block != NULL ? block->exception : NULL; 199 | } 200 | 201 | bool e4c_is_uncaught(void) { 202 | const struct e4c_block *block = e4c_get_block(); 203 | return block != NULL && block->uncaught; 204 | } 205 | 206 | e4c_env *e4c_start(const bool should_acquire, const struct e4c_debug_info debug) { 207 | struct e4c_block *new_block = allocate( 208 | sizeof(*new_block), "Not enough memory to create a new exception block", &debug); 209 | struct e4c_context *context = get_context(&debug); 210 | if (context == &default_context && !is_cleanup_registered) { 211 | if (atexit(cleanup_default_context) != 0) { 212 | panic("Cleanup function could not be registered.", &debug); 213 | } 214 | is_cleanup_registered = true; 215 | } 216 | 217 | new_block->outer_block = context->_innermost_block; 218 | new_block->stage = should_acquire ? BEGINNING : ACQUIRING; 219 | new_block->uncaught = false; 220 | new_block->reacquire_attempts = 0; 221 | new_block->retry_attempts = 0; 222 | new_block->exception = NULL; 223 | 224 | context->_innermost_block = new_block; 225 | 226 | return &new_block->env; 227 | } 228 | 229 | bool e4c_next(const struct e4c_debug_info debug) { 230 | struct e4c_context *context = get_context(&debug); 231 | struct e4c_block *block = context->_innermost_block; 232 | if (block == NULL) { 233 | panic("Invalid exception context state.", &debug); 234 | } 235 | 236 | /* advance the block to the next stage */ 237 | block->stage++; 238 | 239 | struct e4c_exception *exception = block->exception; 240 | const bool uncaught = block->uncaught; 241 | 242 | /* simple optimization: skip CATCHING stage if no exception thrown */ 243 | if (block->stage == CATCHING && (exception == NULL || !uncaught)) { 244 | block->stage++; 245 | } 246 | 247 | /* carry on until the block is DONE */ 248 | if (block->stage < DONE) { 249 | return true; 250 | } 251 | 252 | /* deallocate this block and promote its outer block */ 253 | context->_innermost_block = block->outer_block; 254 | free(block); 255 | 256 | /* deallocate or propagate its exception */ 257 | if (exception != NULL) { 258 | if (uncaught) { 259 | propagate(context, exception); 260 | } else { 261 | delete_exception(context, exception); 262 | } 263 | } 264 | 265 | /* get out of the loop */ 266 | return false; 267 | } 268 | 269 | e4c_env *e4c_get_env(void) { 270 | struct e4c_block *block = e4c_get_block(); 271 | return block != NULL ? &block->env : NULL; 272 | } 273 | 274 | bool e4c_acquire(const struct e4c_debug_info debug) { 275 | return get_stage(&debug) == ACQUIRING; 276 | } 277 | 278 | bool e4c_try(const struct e4c_debug_info debug) { 279 | return get_stage(&debug) == TRYING; 280 | } 281 | 282 | bool e4c_dispose(const struct e4c_debug_info debug) { 283 | return get_stage(&debug) == DISPOSING; 284 | } 285 | 286 | bool e4c_catch(const struct e4c_exception_type *type, const struct e4c_debug_info debug) { 287 | const struct e4c_context *context = get_context(&debug); 288 | struct e4c_block *block = context->_innermost_block; 289 | if (block == NULL) { 290 | panic("Invalid exception context state.", &debug); 291 | } 292 | /* check if this CATCH block can handle current exception */ 293 | if (block->stage == CATCHING 294 | && block->exception != NULL 295 | && block->uncaught 296 | && (type == NULL || extends(block->exception->type, type))) { 297 | block->uncaught = false; 298 | return true; 299 | } 300 | return false; 301 | } 302 | 303 | bool e4c_finally(const struct e4c_debug_info debug) { 304 | return get_stage(&debug) == FINALIZING; 305 | } 306 | 307 | /* 308 | * Suppressed warning: 309 | * - `c:S923` Functions should not be defined with a variable number of arguments 310 | * 311 | */ 312 | e4c_env *e4c_throw( /* NOSONAR */ 313 | const struct e4c_exception_type *type, const char *name, 314 | const struct e4c_debug_info debug, const char *format, ...) { 315 | const int error_number = errno; 316 | const struct e4c_context *context = get_context(&debug); 317 | 318 | va_list arguments_list; 319 | va_start(arguments_list, format); 320 | throw(context, type, name, error_number, &debug, format, arguments_list); 321 | va_end(arguments_list); 322 | 323 | return &((struct e4c_block *) context->_innermost_block)->env; 324 | } 325 | 326 | /* 327 | * Suppressed warnings: 328 | * - `c:S923` Functions should not be defined with a variable number of arguments 329 | * 330 | */ 331 | e4c_env *e4c_restart( /* NOSONAR */ 332 | const bool should_reacquire, const int max, 333 | const struct e4c_exception_type *type, const char *name, 334 | const struct e4c_debug_info debug, const char *format, ...) { 335 | const int error_number = errno; 336 | const struct e4c_context *context = get_context(&debug); 337 | struct e4c_block *block = context->_innermost_block; 338 | if (block == NULL) { 339 | panic(should_reacquire ? "No WITH block to reacquire." : "No TRY block to retry.", &debug); 340 | } 341 | /* update the number of retry/reacquire attempts */ 342 | bool max_reached; 343 | if (should_reacquire) { 344 | max_reached = block->reacquire_attempts >= max; 345 | if (!max_reached) { 346 | block->reacquire_attempts++; 347 | } 348 | } else { 349 | max_reached = block->retry_attempts >= max; 350 | if (!max_reached) { 351 | block->retry_attempts++; 352 | } 353 | } 354 | /* check if maximum number of attempts reached */ 355 | if (max_reached) { 356 | /* throw a new exception, possibly using the current one as cause */ 357 | va_list arguments_list; 358 | va_start(arguments_list, format); 359 | throw(context, type, name, error_number, &debug, format, arguments_list); 360 | va_end(arguments_list); 361 | } else { 362 | /* suppress currently thrown exception; jump back to TRY/WITH block */ 363 | if (block->exception != NULL) { 364 | delete_exception(context, block->exception); 365 | block->exception = NULL; 366 | } 367 | block->uncaught = false; 368 | block->stage = should_reacquire ? BEGINNING : ACQUIRING; 369 | } 370 | return &block->env; 371 | } 372 | 373 | /** 374 | * Causes abnormal program termination due to a fatal error. 375 | * 376 | * @param error_message The message to print to standard error output. 377 | * @param debug Debug info. 378 | */ 379 | static noreturn void panic(const char *error_message, const struct e4c_debug_info *debug) { 380 | fprintf(stderr, "[exceptions4c] %s\n", error_message); 381 | print_debug_info(debug); 382 | fflush(stderr); 383 | abort(); 384 | } 385 | 386 | /** 387 | * Allocates memory for an object; panics if not enough memory. 388 | * 389 | * @param size The size of the new object. 390 | * @param error_message The message to print to standard error output. 391 | * @param debug Debug info. 392 | * @return A pointer to zero-initialized, newly allocated memory. 393 | */ 394 | static void *allocate(const size_t size, const char *error_message, const struct e4c_debug_info *debug) { 395 | void *object = calloc(1, size); 396 | if (object == NULL) { 397 | panic(error_message, debug); 398 | } 399 | return object; 400 | } 401 | 402 | /** 403 | * Checks for dangling exception blocks at program exit. 404 | */ 405 | static void cleanup_default_context(void) { 406 | if (default_context._innermost_block != NULL) { 407 | const struct e4c_debug_info debug = { 408 | .file = NULL, 409 | .line = 0, 410 | .function = NULL 411 | }; 412 | panic("Dangling exception block detected.\n" 413 | " Some TRY block may have been exited improperly (via goto, break, continue, or return).", &debug); 414 | } 415 | } 416 | 417 | /** 418 | * Retrieves the current exception context; panics if NULL. 419 | * 420 | * @param debug Debug info. 421 | * @return A pointer to the current exception context. 422 | */ 423 | static struct e4c_context *get_context(const struct e4c_debug_info *debug) { 424 | struct e4c_context *context = e4c_get_context(); 425 | if (context == NULL) { 426 | panic("Context supplier returned NULL.", debug); 427 | } 428 | return context; 429 | } 430 | 431 | /** 432 | * Retrieves the current exception block (if any). 433 | * 434 | * @return the current exception block; NULL if absent. 435 | */ 436 | static struct e4c_block *e4c_get_block(void) { 437 | const struct e4c_context *context = e4c_get_context(); 438 | return context != NULL ? context->_innermost_block : NULL; 439 | } 440 | 441 | /** 442 | * Propagates the supplied exception in the supplied context. 443 | * 444 | * @note 445 | * If the exception reached the top level of the program, then the program will 446 | * be abruptly terminated. 447 | * 448 | * @param context The context whose innermost exception block will receive the 449 | * exception. 450 | * @param exception The exception to propagate. 451 | */ 452 | static void propagate(const struct e4c_context *context, struct e4c_exception *exception) { 453 | struct e4c_block *block = context->_innermost_block; 454 | if (block == NULL) { 455 | /* uncaught exception handler */ 456 | if (context->uncaught_handler != NULL) { 457 | context->uncaught_handler(exception); 458 | } else { 459 | print_exception(exception, false); 460 | (void) fflush(stderr); 461 | } 462 | /* delete the exception to avoid memory leaks */ 463 | delete_exception(context, exception); 464 | /* abrupt termination handler */ 465 | if (context->termination_handler != NULL) { 466 | context->termination_handler(); 467 | return; 468 | } 469 | exit(EXIT_FAILURE); 470 | } 471 | 472 | /* suppress current exception (if any) */ 473 | if (block->exception != NULL && block->exception != exception) { 474 | delete_exception(context, block->exception); 475 | } 476 | 477 | block->exception = exception; 478 | block->uncaught = true; 479 | 480 | /* skip DISPOSING stage if the acquisition just failed */ 481 | if (block->stage == ACQUIRING) { 482 | block->stage = DISPOSING; 483 | } 484 | } 485 | 486 | /** 487 | * Returns the current exception block stage; panics if NULL. 488 | * 489 | * @param debug Debug info. 490 | * @return The stage of the current exception block. 491 | */ 492 | static enum block_stage get_stage(const struct e4c_debug_info *debug) { 493 | const struct e4c_context *context = get_context(debug); 494 | const struct e4c_block *block = context->_innermost_block; 495 | if (block == NULL) { 496 | panic("Invalid exception context state.", debug); 497 | } 498 | return block->stage; 499 | } 500 | 501 | /** 502 | * Determines if the supplied type extends from the supplied supertype. 503 | * 504 | * @param type The specific type to check. 505 | * @param supertype The generic type to check against. 506 | * @return true if the type extends from supertype. 507 | */ 508 | static bool extends(const struct e4c_exception_type *type, const struct e4c_exception_type *supertype) { 509 | for (; type != NULL; 510 | type = type != type->supertype ? type->supertype : NULL) { 511 | if (type == supertype) { 512 | return true; 513 | } 514 | } 515 | return false; 516 | } 517 | 518 | /** 519 | * Creates and propagates a new exception. 520 | * 521 | * @param context The current exception context. 522 | * @param type The exception type to throw. 523 | * @param name The name of the exception type. 524 | * @param error_number The value of errno when the exception is thrown. 525 | * @param debug Debug info. 526 | * @param format The optional error message. 527 | * @param arguments_list The list of arguments to be formatted. 528 | */ 529 | static void throw( 530 | const struct e4c_context *context, const struct e4c_exception_type *type, const char *name, int error_number, 531 | const struct e4c_debug_info *debug, const char *format, va_list arguments_list) { 532 | /* allocate and initialize the new exception */ 533 | struct e4c_exception *exception = allocate( 534 | sizeof(*exception), "Not enough memory to create a new exception", debug); 535 | 536 | exception->name = name; 537 | exception->debug.file = debug->file; 538 | exception->debug.line = debug->line; 539 | exception->debug.function = debug->function; 540 | exception->error_number = error_number; 541 | exception->type = type; 542 | exception->cause = NULL; 543 | exception->data = NULL; 544 | 545 | if (format == NULL && type != NULL) { 546 | (void) snprintf(exception->message, sizeof(exception->message), "%s", type->default_message); 547 | } else if (format != NULL) { 548 | /* 549 | * Suppressed warning: 550 | * - `c:S5281` Argument of "printf" should be a format string 551 | * 552 | */ 553 | (void) vsnprintf(exception->message, sizeof(exception->message), format, arguments_list); /* NOSONAR */ 554 | } 555 | 556 | /* capture the cause of this exception */ 557 | for ( 558 | struct e4c_block *block = context->_innermost_block; 559 | block != NULL; 560 | block = block->outer_block 561 | ) { 562 | if (block->exception != NULL 563 | && (block->uncaught || block->stage == CATCHING)) { 564 | exception->cause = block->exception; 565 | block->exception = NULL; 566 | break; 567 | } 568 | } 569 | 570 | /* initialize custom data */ 571 | if (context->initialize_exception != NULL) { 572 | context->initialize_exception(exception); 573 | } 574 | 575 | propagate(context, exception); 576 | } 577 | 578 | /** 579 | * Deletes the supplied exception, along with its cause. 580 | * 581 | * @param context The context the supplied exception belongs to. 582 | * @param exception The exception to delete. 583 | */ 584 | static void delete_exception(const struct e4c_context *context, struct e4c_exception *exception) { 585 | if (context->finalize_exception != NULL) { 586 | context->finalize_exception(exception); 587 | } 588 | if (exception->cause != NULL) { 589 | delete_exception(context, exception->cause); 590 | } 591 | free(exception); 592 | } 593 | 594 | /** 595 | * Prints debug info (if available) to the standard error output. 596 | * 597 | * @param debug The debug info to print if available. 598 | */ 599 | static void print_debug_info(const struct e4c_debug_info *debug) { 600 | if (debug->file != NULL) { 601 | (void) fprintf(stderr, " at %s (%s:%d)\n", debug->function, debug->file, debug->line); 602 | } 603 | } 604 | 605 | /** 606 | * Prints the supplied exception to the standard error output. 607 | * 608 | * @param exception The exception to print. 609 | * @param is_cause true if the supplied exception is the cause of 610 | * another one. 611 | */ 612 | static void print_exception(const struct e4c_exception *exception, const bool is_cause) { 613 | (void) fprintf(stderr, "%s%s: %s\n", is_cause ? "Caused by: " : "\n", exception->name, exception->message); 614 | print_debug_info(&exception->debug); 615 | if (exception->cause != NULL) { 616 | print_exception(exception->cause, true); 617 | } 618 | } 619 | --------------------------------------------------------------------------------