summaryrefslogtreecommitdiff
path: root/src/runtime/c/gu/exn.h
blob: 6f5d0ff3826b0af23e8035641b757e9179abe6f4 (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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#ifndef GU_EXN_H_
#define GU_EXN_H_

#include <gu/mem.h>

/** @file
 *
 * @defgroup GuExn Exceptions
 * Defined in <gu/exn.h>.
 * @{
 */

/// An exception frame.
typedef struct GuExn GuExn;

/// @private
typedef enum {
	GU_EXN_RAISED,
	GU_EXN_OK,
	GU_EXN_BLOCKED
} GuExnState;

typedef struct GuExnData GuExnData;

/// A structure for storing exception values.
struct GuExnData
/**
 * When an exception is raised, if there is an associated value, it
 * must be allocated from a pool that still exists when control
 * returns to the handler of that exception. This structure is used to
 * communicate the exception from the raiser to the handler: the
 * handler sets #pool when setting up the exception frame, and the
 * raiser uses that pool to allocate the value and stores that in
 * #data. When control returns to the handler, it reads the value from
 * there.
 */
{
	
	/// The pool that the exception value should be allocated from.
	GuPool* pool;

	/// The exception value.
	void* data;
};

struct GuExn {
	/// @privatesection
	GuExnState state;
	const char* caught;
	GuExnData data;
};


/// @name Creating exception frames
//@{


/// Allocate a new local exception frame.
#define gu_exn(pool_) &(GuExn){	\
	.state = GU_EXN_OK,	\
	.caught = NULL,	\
	.data = {.pool = pool_, .data = NULL} \
}


/// Allocate a new exception frame.
GU_API_DECL GuExn*
gu_new_exn(GuPool* pool);


GU_API_DECL bool
gu_exn_is_raised(GuExn* err);

static inline void
gu_exn_clear(GuExn* err) {
	err->caught = NULL;
	err->state = GU_EXN_OK;
}

#define gu_exn_caught(err, type) \
	(err->caught && strcmp(err->caught, #type) == 0)
	
GU_API_DECL bool
gu_exn_caught_(GuExn* err, const char* type);

static inline const void*
gu_exn_caught_data(GuExn* err)
{
	return err->data.data;
}

/// Temporarily block a raised exception.
GU_API_DECL void
gu_exn_block(GuExn* err);

/// Show again a blocked exception.
GU_API_DECL void
gu_exn_unblock(GuExn* err);

//@private
GU_API_DECL GuExnData*
gu_exn_raise_(GuExn* err, const char* type);

//@private
GU_API_DECL GuExnData*
gu_exn_raise_debug_(GuExn* err, const char* type,
		      const char* filename, const char* func, int lineno);

#ifdef NDEBUG
#define gu_exn_raise(err_, type_)	\
	gu_exn_raise_(err_, type_)
#else
#define gu_exn_raise(err_, type_)	\
	gu_exn_raise_debug_(err_, type_, \
			      __FILE__, __func__, __LINE__)
#endif

/// Raise an exception.
#define gu_raise(exn, T)		\
	gu_exn_raise(exn, #T)
/**< 
 * @param exn The current exception frame.
 *
 * @param T   The C type of the exception to raise.
 *
 * @return    A #GuExnData object that can be used to store the exception value, or
 * \c NULL if no value is required.
 * 
 * @note The associated #GuType object for type \p T must be visible.
 */

#define gu_raise_new(error_, t_, pool_, expr_)				\
	GU_BEGIN							\
	GuExnData* gu_raise_err_ = gu_raise(error_, t_);		\
	if (gu_raise_err_) {						\
		GuPool* pool_ = gu_raise_err_->pool;			\
		gu_raise_err_->data = expr_;				\
	}								\
	GU_END

/// Check the status of the current exception frame
static inline bool
gu_ok(GuExn* exn) {
	return !GU_UNLIKELY(gu_exn_is_raised(exn));
}
/**<
 * @return \c false if an exception has been raised in the frame \p exn
 * and it has not been blocked, \c true otherwise.
 */


/// Return from current function if an exception has been raised.
#define gu_return_on_exn(exn_, retval_)		\
	GU_BEGIN					\
	if (gu_exn_is_raised(exn_)) return retval_;	\
	GU_END
/**<
 * @showinitializer
 */


#include <errno.h>

typedef int GuErrno;

GU_API_DECL void
gu_raise_errno(GuExn* err);

/** @} */

#endif // GU_EXN_H_