packages feed

gf-3.5: src/runtime/c/gu/exn.h

#ifndef GU_EXN_H_
#define GU_EXN_H_

#include <gu/mem.h>
#include <gu/type.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* const pool;

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

struct GuExn {
	/// @privatesection
	GuExnState state;
	GuExn* parent;
	GuKind* catch;
	GuType* caught;
	GuExnData data;
};


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


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


/// Allocate a new exception frame.
GuExn*
gu_new_exn(GuExn* parent, GuKind* catch_kind, GuPool* pool);



static inline bool
gu_exn_is_raised(GuExn* err) {
	return err && (err->state == GU_EXN_RAISED);
}

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



GuType*
gu_exn_caught(GuExn* err);

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

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

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

//@private
GuExnData*
gu_exn_raise_(GuExn* err, GuType* type);

//@private
GuExnData*
gu_exn_raise_debug_(GuExn* err, GuType* 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, gu_type(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

#define gu_raise_i(error_, t_, ...) \
	gu_raise_new(error_, t_, gu_raise_pool_, gu_new_i(gu_raise_pool_, t_, __VA_ARGS__))


/// 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;

extern GU_DECLARE_TYPE(GuErrno, signed);



#define gu_raise_errno(error_) \
	gu_raise_i(error_, GuErrno, errno)

#if 0

typedef void (*GuExnPrintFn)(GuFn* clo, void* err, FILE* out);

extern GuTypeTable gu_exn_default_printer;

void
gu_exn_print(GuExn* err, FILE* out, GuTypeMap printer_map);

#endif

/** @} */

#endif // GU_EXN_H_