summaryrefslogtreecommitdiff
path: root/cesar/lib/try.h
blob: 0e8b577454cc52c129c4ad6274339460792c045d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#ifndef lib_try_h
#define lib_try_h
/* Cesar project {{{
 *
 * Copyright (C) 2007 Spidcom
 *
 * <<<Licence>>>
 *
 * }}} */
/**
 * \file    lib/try.h
 * \brief   Light exception system.
 * \ingroup lib
 *
 * This system mimics C++ exception handling using C setjmp/longjmp.  Hey, but
 * this is not as safe as C++ exceptions!
 *
 * Here is an example use:
 *
 * \code
 * try_begin
 * {
 *     ...some code...
 *     try_throw (code thrown);
 *     ...some code...
 * }
 * try_catch (code to catch)
 * {
 *     ...handle exception...
 * }
 * try_always
 * {
 *     ...optional code always executed when leaving this block scope...
 * }
 * try_end;
 * \endcode
 *
 * Main usage should be test code and fatal error trapping.  This is not safe
 * and fast enough to be used for production code.
 *
 * When an exception is thrown, it is caught by each try block in order to
 * clean up after itself.
 */

#include <setjmp.h>

/** Pointer to the most recently set setjmp context. */
extern jmp_buf *try_state_;

/** Reserved catch codes. */
enum try_code_t
{
    /** This is reserved. */
    TRY_CODE_NONE,
    /** Thrown for fatal error. */
    TRY_CODE_FATAL,
    /** Thrown for test failure. */
    TRY_CODE_TEST_FAILURE,
};

/** Begin a try block. */
#define try_begin try_begin_

/** Define the catch block inside a try block. */
#define try_catch(code) try_catch_(code)

/** Define the always block inside a try block. */
#define try_always try_always_

/** End a try block. */
#define try_end try_end_

#define try_begin_ \
{ \
    int thrown_; \
    bool caught_; \
    jmp_buf *old_try_state_ = try_state_; \
    jmp_buf this_try_state_; \
    try_state_ = &this_try_state_; \
    /* ANSI says assigning setjmp return value is forbidden, */ \
    /* this code is not portable... */ \
    thrown_ = setjmp (*try_state_); \
    caught_ = false; \
    if (thrown_ == 0) \
    {

#define try_catch_(code) \
    } \
    else \
    { \
        caught_ = thrown_ == (code); \
        if (caught_) \
        {

#define try_always_ \
        } \
    } \
    { \
        {

#define try_end_ \
        } \
    } \
    try_state_ = old_try_state_; \
    if (thrown_ != 0 && !caught_) \
    { \
        /* Jump again. */ \
        dbg_assert_print (try_state_, "uncaught exception"); \
        longjmp (*try_state_, thrown_); \
    } \
}

/** Throw an exception code. */
#define try_throw(code) try_throw_(code)

#define try_throw_(code) \
    do { \
        longjmp (*try_state_, (code)); \
    } while (0)

#endif /* lib_try_h */