mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-28 08:26:14 +01:00
gerror: Add support for extended errors
This commit adds a G_DEFINE_EXTENDED_ERROR macro and g_error_domain_register() functions to register extended error domains.
This commit is contained in:
parent
b715e4c9d0
commit
ae72f9de35
@ -754,6 +754,13 @@ g_propagate_error
|
|||||||
g_clear_error
|
g_clear_error
|
||||||
g_prefix_error
|
g_prefix_error
|
||||||
g_propagate_prefixed_error
|
g_propagate_prefixed_error
|
||||||
|
<SUBSECTION>
|
||||||
|
GErrorInitFunc
|
||||||
|
GErrorCopyFunc
|
||||||
|
GErrorClearFunc
|
||||||
|
G_DEFINE_EXTENDED_ERROR
|
||||||
|
g_error_domain_register_static
|
||||||
|
g_error_domain_register
|
||||||
</SECTION>
|
</SECTION>
|
||||||
|
|
||||||
<SECTION>
|
<SECTION>
|
||||||
|
355
glib/gerror.c
355
glib/gerror.c
@ -372,27 +372,338 @@
|
|||||||
* to add a check at the top of your function that the error return
|
* to add a check at the top of your function that the error return
|
||||||
* location is either %NULL or contains a %NULL error (e.g.
|
* location is either %NULL or contains a %NULL error (e.g.
|
||||||
* `g_return_if_fail (error == NULL || *error == NULL);`).
|
* `g_return_if_fail (error == NULL || *error == NULL);`).
|
||||||
|
*
|
||||||
|
* Since GLib 2.68 it is possible to extend the #GError type. This is
|
||||||
|
* done with the G_DEFINE_EXTENDED_ERROR() macro. To create an
|
||||||
|
* extended #GError type do something like this in the header file:
|
||||||
|
* |[<!-- language="C" -->
|
||||||
|
* typedef enum
|
||||||
|
* {
|
||||||
|
* MY_ERROR_BAD_REQUEST,
|
||||||
|
* } MyError;
|
||||||
|
* #define MY_ERROR (my_error_quark ())
|
||||||
|
* GQuark my_error_quark (void);
|
||||||
|
* int
|
||||||
|
* my_error_get_parse_error_id (GError *error);
|
||||||
|
* const char *
|
||||||
|
* my_error_get_bad_request_details (GError *error);
|
||||||
|
* ]|
|
||||||
|
* and in implementation:
|
||||||
|
* |[<!-- language="C" -->
|
||||||
|
* typedef struct
|
||||||
|
* {
|
||||||
|
* int parse_error_id;
|
||||||
|
* char *bad_request_details;
|
||||||
|
* } MyErrorPrivate;
|
||||||
|
*
|
||||||
|
* static void
|
||||||
|
* my_error_private_init (MyErrorPrivate *priv)
|
||||||
|
* {
|
||||||
|
* priv->parse_error_id = -1;
|
||||||
|
* // No need to set priv->bad_request_details to NULL,
|
||||||
|
* // the struct is initialized with zeros.
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* static void
|
||||||
|
* my_error_private_copy (const MyErrorPrivate *src_priv, MyErrorPrivate *dest_priv)
|
||||||
|
* {
|
||||||
|
* dest_priv->parse_error_id = src_priv->parse_error_id;
|
||||||
|
* dest_priv->bad_request_details = g_strdup (src_priv->bad_request_details);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* static void
|
||||||
|
* my_error_private_clear (MyErrorPrivate *priv)
|
||||||
|
* {
|
||||||
|
* g_free (priv->bad_request_details);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // This defines the my_error_get_private and my_error_quark functions.
|
||||||
|
* G_DEFINE_EXTENDED_ERROR (MyError, my_error)
|
||||||
|
*
|
||||||
|
* int
|
||||||
|
* my_error_get_parse_error_id (GError *error)
|
||||||
|
* {
|
||||||
|
* MyErrorPrivate *priv = my_error_get_private (error);
|
||||||
|
* g_return_val_if_fail (priv != NULL, -1);
|
||||||
|
* return priv->parse_error_id;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* const char *
|
||||||
|
* my_error_get_bad_request_details (GError *error)
|
||||||
|
* {
|
||||||
|
* MyErrorPrivate *priv = my_error_get_private (error);
|
||||||
|
* g_return_val_if_fail (priv != NULL, NULL);
|
||||||
|
* g_return_val_if_fail (error->code != MY_ERROR_BAD_REQUEST, NULL);
|
||||||
|
* return priv->bad_request_details;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* static void
|
||||||
|
* my_error_set_bad_request (GError **error,
|
||||||
|
* const char *reason,
|
||||||
|
* int error_id,
|
||||||
|
* const char *details)
|
||||||
|
* {
|
||||||
|
* MyErrorPrivate *priv;
|
||||||
|
* g_set_error (error, MY_ERROR, MY_ERROR_BAD_REQUEST, "Invalid request: %s", reason);
|
||||||
|
* if (error != NULL && *error != NULL)
|
||||||
|
* {
|
||||||
|
* priv = my_error_get_private (error);
|
||||||
|
* g_return_val_if_fail (priv != NULL, NULL);
|
||||||
|
* priv->parse_error_id = error_id;
|
||||||
|
* priv->bad_request_details = g_strdup (details);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ]|
|
||||||
|
* An example of use of the error could be:
|
||||||
|
* |[<!-- language="C" -->
|
||||||
|
* gboolean
|
||||||
|
* send_request (GBytes *request, GError **error)
|
||||||
|
* {
|
||||||
|
* ParseFailedStatus *failure = validate_request (request);
|
||||||
|
* if (failure != NULL)
|
||||||
|
* {
|
||||||
|
* my_error_set_bad_request (error, failure->reason, failure->error_id, failure->details);
|
||||||
|
* parse_failed_status_free (failure);
|
||||||
|
* return FALSE;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* return send_one (request, error);
|
||||||
|
* }
|
||||||
|
* ]|
|
||||||
|
*
|
||||||
|
* Please note that if you are a library author and your library
|
||||||
|
* exposes an existing error domain, then you can't make this error
|
||||||
|
* domain an extended one without breaking ABI. This is because
|
||||||
|
* earlier it was possible to create an error with this error domain
|
||||||
|
* on the stack and then copy it with g_error_copy(). If the new
|
||||||
|
* version of your library makes the error domain an extended one,
|
||||||
|
* then g_error_copy() called by code that allocated the error on the
|
||||||
|
* stack will try to copy more data than it used to, which will lead
|
||||||
|
* to undefined behavior. You must not stack-allocate errors with an
|
||||||
|
* extended error domain, and it is bad practice to stack-allocate any
|
||||||
|
* other #GErrors.
|
||||||
|
*
|
||||||
|
* Extended error domains in unloadable plugins/modules are not
|
||||||
|
* supported.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "gerror.h"
|
#include "gerror.h"
|
||||||
|
|
||||||
|
#include "ghash.h"
|
||||||
|
#include "glib-init.h"
|
||||||
#include "gslice.h"
|
#include "gslice.h"
|
||||||
#include "gstrfuncs.h"
|
#include "gstrfuncs.h"
|
||||||
#include "gtestutils.h"
|
#include "gtestutils.h"
|
||||||
|
#include "gthread.h"
|
||||||
|
|
||||||
|
static GRWLock error_domain_global;
|
||||||
|
/* error_domain_ht must be accessed with error_domain_global
|
||||||
|
* locked.
|
||||||
|
*/
|
||||||
|
static GHashTable *error_domain_ht = NULL;
|
||||||
|
|
||||||
|
void
|
||||||
|
g_error_init (void)
|
||||||
|
{
|
||||||
|
error_domain_ht = g_hash_table_new (NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
/* private_size is already aligned. */
|
||||||
|
gsize private_size;
|
||||||
|
GErrorInitFunc init;
|
||||||
|
GErrorCopyFunc copy;
|
||||||
|
GErrorClearFunc clear;
|
||||||
|
} ErrorDomainInfo;
|
||||||
|
|
||||||
|
/* Must be called with error_domain_global locked.
|
||||||
|
*/
|
||||||
|
static inline ErrorDomainInfo *
|
||||||
|
error_domain_lookup (GQuark domain)
|
||||||
|
{
|
||||||
|
return g_hash_table_lookup (error_domain_ht,
|
||||||
|
GUINT_TO_POINTER (domain));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copied from gtype.c. */
|
||||||
|
#define STRUCT_ALIGNMENT (2 * sizeof (gsize))
|
||||||
|
#define ALIGN_STRUCT(offset) \
|
||||||
|
((offset + (STRUCT_ALIGNMENT - 1)) & -STRUCT_ALIGNMENT)
|
||||||
|
|
||||||
|
static void
|
||||||
|
error_domain_register (GQuark error_quark,
|
||||||
|
gsize error_type_private_size,
|
||||||
|
GErrorInitFunc error_type_init,
|
||||||
|
GErrorCopyFunc error_type_copy,
|
||||||
|
GErrorClearFunc error_type_clear)
|
||||||
|
{
|
||||||
|
g_rw_lock_writer_lock (&error_domain_global);
|
||||||
|
if (error_domain_lookup (error_quark) == NULL)
|
||||||
|
{
|
||||||
|
ErrorDomainInfo *info = g_new (ErrorDomainInfo, 1);
|
||||||
|
info->private_size = ALIGN_STRUCT (error_type_private_size);
|
||||||
|
info->init = error_type_init;
|
||||||
|
info->copy = error_type_copy;
|
||||||
|
info->clear = error_type_clear;
|
||||||
|
|
||||||
|
g_hash_table_insert (error_domain_ht,
|
||||||
|
GUINT_TO_POINTER (error_quark),
|
||||||
|
info);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const char *name = g_quark_to_string (error_quark);
|
||||||
|
|
||||||
|
g_critical ("Attempted to register an extended error domain for %s more than once", name);
|
||||||
|
}
|
||||||
|
g_rw_lock_writer_unlock (&error_domain_global);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* g_error_domain_register_static:
|
||||||
|
* @error_type_name: static string to create a #GQuark from
|
||||||
|
* @error_type_private_size: size of the private error data in bytes
|
||||||
|
* @error_type_init: function initializing fields of the private error data
|
||||||
|
* @error_type_copy: function copying fields of the private error data
|
||||||
|
* @error_type_clear: function freeing fields of the private error data
|
||||||
|
*
|
||||||
|
* This function registers an extended #GError domain.
|
||||||
|
*
|
||||||
|
* @error_type_name should not be freed. @error_type_private_size must
|
||||||
|
* be greater than 0.
|
||||||
|
*
|
||||||
|
* @error_type_init receives an initialized #GError and should then initialize
|
||||||
|
* the private data.
|
||||||
|
*
|
||||||
|
* @error_type_copy is a function that receives both original and a copy
|
||||||
|
* #GError and should copy the fields of the private error data. The standard
|
||||||
|
* #GError fields are already handled.
|
||||||
|
*
|
||||||
|
* @error_type_clear receives the pointer to the error, and it should free the
|
||||||
|
* fields of the private error data. It should not free the struct itself though.
|
||||||
|
*
|
||||||
|
* Normally, it is better to use G_DEFINE_EXTENDED_ERROR(), as it
|
||||||
|
* already takes care of passing valid information to this function.
|
||||||
|
*
|
||||||
|
* Returns: #GQuark representing the error domain
|
||||||
|
* Since: 2.68
|
||||||
|
*/
|
||||||
|
GQuark
|
||||||
|
g_error_domain_register_static (const char *error_type_name,
|
||||||
|
gsize error_type_private_size,
|
||||||
|
GErrorInitFunc error_type_init,
|
||||||
|
GErrorCopyFunc error_type_copy,
|
||||||
|
GErrorClearFunc error_type_clear)
|
||||||
|
{
|
||||||
|
GQuark error_quark;
|
||||||
|
|
||||||
|
g_return_val_if_fail (error_type_name != NULL, 0);
|
||||||
|
g_return_val_if_fail (error_type_private_size > 0, 0);
|
||||||
|
g_return_val_if_fail (error_type_init != NULL, 0);
|
||||||
|
g_return_val_if_fail (error_type_copy != NULL, 0);
|
||||||
|
g_return_val_if_fail (error_type_clear != NULL, 0);
|
||||||
|
|
||||||
|
error_quark = g_quark_from_static_string (error_type_name);
|
||||||
|
error_domain_register (error_quark,
|
||||||
|
error_type_private_size,
|
||||||
|
error_type_init,
|
||||||
|
error_type_copy,
|
||||||
|
error_type_clear);
|
||||||
|
return error_quark;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* g_error_domain_register:
|
||||||
|
* @error_type_name: string to create a #GQuark from
|
||||||
|
* @error_type_private_size: size of the private error data in bytes
|
||||||
|
* @error_type_init: function initializing fields of the private error data
|
||||||
|
* @error_type_copy: function copying fields of the private error data
|
||||||
|
* @error_type_clear: function freeing fields of the private error data
|
||||||
|
*
|
||||||
|
* This function registers an extended #GError domain.
|
||||||
|
* @error_type_name will be duplicated. Otherwise does the same as
|
||||||
|
* g_error_domain_register_static().
|
||||||
|
*
|
||||||
|
* Returns: #GQuark representing the error domain
|
||||||
|
* Since: 2.68
|
||||||
|
*/
|
||||||
|
GQuark
|
||||||
|
g_error_domain_register (const char *error_type_name,
|
||||||
|
gsize error_type_private_size,
|
||||||
|
GErrorInitFunc error_type_init,
|
||||||
|
GErrorCopyFunc error_type_copy,
|
||||||
|
GErrorClearFunc error_type_clear)
|
||||||
|
{
|
||||||
|
GQuark error_quark;
|
||||||
|
|
||||||
|
g_return_val_if_fail (error_type_name != NULL, 0);
|
||||||
|
g_return_val_if_fail (error_type_private_size > 0, 0);
|
||||||
|
g_return_val_if_fail (error_type_init != NULL, 0);
|
||||||
|
g_return_val_if_fail (error_type_copy != NULL, 0);
|
||||||
|
g_return_val_if_fail (error_type_clear != NULL, 0);
|
||||||
|
|
||||||
|
error_quark = g_quark_from_string (error_type_name);
|
||||||
|
error_domain_register (error_quark,
|
||||||
|
error_type_private_size,
|
||||||
|
error_type_init,
|
||||||
|
error_type_copy,
|
||||||
|
error_type_clear);
|
||||||
|
return error_quark;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GError *
|
||||||
|
g_error_allocate (GQuark domain, ErrorDomainInfo *out_info)
|
||||||
|
{
|
||||||
|
guint8 *allocated;
|
||||||
|
GError *error;
|
||||||
|
ErrorDomainInfo *info;
|
||||||
|
gsize private_size;
|
||||||
|
|
||||||
|
g_rw_lock_reader_lock (&error_domain_global);
|
||||||
|
info = error_domain_lookup (domain);
|
||||||
|
if (info != NULL)
|
||||||
|
{
|
||||||
|
if (out_info != NULL)
|
||||||
|
*out_info = *info;
|
||||||
|
private_size = info->private_size;
|
||||||
|
g_rw_lock_reader_unlock (&error_domain_global);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_rw_lock_reader_unlock (&error_domain_global);
|
||||||
|
if (out_info != NULL)
|
||||||
|
memset (out_info, 0, sizeof (*out_info));
|
||||||
|
private_size = 0;
|
||||||
|
}
|
||||||
|
allocated = g_slice_alloc0 (private_size + sizeof (GError));
|
||||||
|
error = (GError *) (allocated + private_size);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This function takes ownership of @message. */
|
||||||
static GError *
|
static GError *
|
||||||
g_error_new_steal (GQuark domain,
|
g_error_new_steal (GQuark domain,
|
||||||
gint code,
|
gint code,
|
||||||
gchar *message)
|
gchar *message,
|
||||||
|
ErrorDomainInfo *out_info)
|
||||||
{
|
{
|
||||||
GError *error = g_slice_new (GError);
|
ErrorDomainInfo info;
|
||||||
|
GError *error = g_error_allocate (domain, &info);
|
||||||
|
|
||||||
error->domain = domain;
|
error->domain = domain;
|
||||||
error->code = code;
|
error->code = code;
|
||||||
error->message = message;
|
error->message = message;
|
||||||
|
|
||||||
|
if (info.init != NULL)
|
||||||
|
info.init (error);
|
||||||
|
if (out_info != NULL)
|
||||||
|
*out_info = info;
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -424,7 +735,7 @@ g_error_new_valist (GQuark domain,
|
|||||||
g_warn_if_fail (domain != 0);
|
g_warn_if_fail (domain != 0);
|
||||||
g_warn_if_fail (format != NULL);
|
g_warn_if_fail (format != NULL);
|
||||||
|
|
||||||
return g_error_new_steal (domain, code, g_strdup_vprintf (format, args));
|
return g_error_new_steal (domain, code, g_strdup_vprintf (format, args), NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -479,7 +790,7 @@ g_error_new_literal (GQuark domain,
|
|||||||
g_return_val_if_fail (message != NULL, NULL);
|
g_return_val_if_fail (message != NULL, NULL);
|
||||||
g_return_val_if_fail (domain != 0, NULL);
|
g_return_val_if_fail (domain != 0, NULL);
|
||||||
|
|
||||||
return g_error_new_steal (domain, code, g_strdup (message));
|
return g_error_new_steal (domain, code, g_strdup (message), NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -491,11 +802,31 @@ g_error_new_literal (GQuark domain,
|
|||||||
void
|
void
|
||||||
g_error_free (GError *error)
|
g_error_free (GError *error)
|
||||||
{
|
{
|
||||||
|
gsize private_size;
|
||||||
|
ErrorDomainInfo *info;
|
||||||
|
guint8 *allocated;
|
||||||
|
|
||||||
g_return_if_fail (error != NULL);
|
g_return_if_fail (error != NULL);
|
||||||
|
|
||||||
g_free (error->message);
|
g_rw_lock_reader_lock (&error_domain_global);
|
||||||
|
info = error_domain_lookup (error->domain);
|
||||||
|
if (info != NULL)
|
||||||
|
{
|
||||||
|
GErrorClearFunc clear = info->clear;
|
||||||
|
|
||||||
g_slice_free (GError, error);
|
private_size = info->private_size;
|
||||||
|
g_rw_lock_reader_unlock (&error_domain_global);
|
||||||
|
clear (error);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_rw_lock_reader_unlock (&error_domain_global);
|
||||||
|
private_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free (error->message);
|
||||||
|
allocated = ((guint8 *) error) - private_size;
|
||||||
|
g_slice_free1 (private_size + sizeof (GError), allocated);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -509,14 +840,22 @@ g_error_free (GError *error)
|
|||||||
GError*
|
GError*
|
||||||
g_error_copy (const GError *error)
|
g_error_copy (const GError *error)
|
||||||
{
|
{
|
||||||
|
GError *copy;
|
||||||
|
ErrorDomainInfo info;
|
||||||
|
|
||||||
g_return_val_if_fail (error != NULL, NULL);
|
g_return_val_if_fail (error != NULL, NULL);
|
||||||
/* See g_error_new_valist for why these don't return */
|
/* See g_error_new_valist for why these don't return */
|
||||||
g_warn_if_fail (error->domain != 0);
|
g_warn_if_fail (error->domain != 0);
|
||||||
g_warn_if_fail (error->message != NULL);
|
g_warn_if_fail (error->message != NULL);
|
||||||
|
|
||||||
return g_error_new_steal (error->domain,
|
copy = g_error_new_steal (error->domain,
|
||||||
error->code,
|
error->code,
|
||||||
g_strdup (error->message));
|
g_strdup (error->message),
|
||||||
|
&info);
|
||||||
|
if (info.copy != NULL)
|
||||||
|
info.copy (error, copy);
|
||||||
|
|
||||||
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
137
glib/gerror.h
137
glib/gerror.h
@ -47,6 +47,143 @@ struct _GError
|
|||||||
gchar *message;
|
gchar *message;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* G_DEFINE_EXTENDED_ERROR:
|
||||||
|
* @ErrorType: name to return a #GQuark for
|
||||||
|
* @error_type: prefix for the function name
|
||||||
|
*
|
||||||
|
* A convenience macro which defines two functions. First, returning
|
||||||
|
* the #GQuark for the extended error type @ErrorType; it is called
|
||||||
|
* `@error_type_quark()`. Second, returning the private data from a
|
||||||
|
* passed #GError; it is called `@error_type_get_private()`.
|
||||||
|
*
|
||||||
|
* For this macro to work, a type named `@ErrorTypePrivate` should be
|
||||||
|
* defined, `@error_type_private_init()`, `@error_type_private_copy()`
|
||||||
|
* and `@error_type_private_clear()` functions need to be either
|
||||||
|
* declared or defined. The functions should be similar to
|
||||||
|
* #GErrorInitFunc, #GErrorCopyFunc and #GErrorClearFunc,
|
||||||
|
* respectively, but they should receive the private data type instead
|
||||||
|
* of #GError.
|
||||||
|
*
|
||||||
|
* Since: 2.68
|
||||||
|
*/
|
||||||
|
#define G_DEFINE_EXTENDED_ERROR(ErrorType, error_type) \
|
||||||
|
static inline ErrorType ## Private * \
|
||||||
|
error_type ## _get_private (const GError *error) \
|
||||||
|
{ \
|
||||||
|
/* Copied from gtype.c (STRUCT_ALIGNMENT and ALIGN_STRUCT macros). */ \
|
||||||
|
const gsize sa = 2 * sizeof (gsize); \
|
||||||
|
const gsize as = (sizeof (ErrorType ## Private) + (sa - 1)) & -sa; \
|
||||||
|
g_return_val_if_fail (error != NULL, NULL); \
|
||||||
|
g_return_val_if_fail (error->domain == error_type ## _quark (), NULL); \
|
||||||
|
return (ErrorType ## Private *) (((guint8 *)error) - as); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
static void \
|
||||||
|
g_error_with_ ## error_type ## _private_init (GError *error) \
|
||||||
|
{ \
|
||||||
|
ErrorType ## Private *priv = error_type ## _get_private (error); \
|
||||||
|
error_type ## _private_init (priv); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
static void \
|
||||||
|
g_error_with_ ## error_type ## _private_copy (const GError *src_error, \
|
||||||
|
GError *dest_error) \
|
||||||
|
{ \
|
||||||
|
const ErrorType ## Private *src_priv = error_type ## _get_private (src_error); \
|
||||||
|
ErrorType ## Private *dest_priv = error_type ## _get_private (dest_error); \
|
||||||
|
error_type ## _private_copy (src_priv, dest_priv); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
static void \
|
||||||
|
g_error_with_ ## error_type ## _private_clear (GError *error) \
|
||||||
|
{ \
|
||||||
|
ErrorType ## Private *priv = error_type ## _get_private (error); \
|
||||||
|
error_type ## _private_clear (priv); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
GQuark \
|
||||||
|
error_type ## _quark (void) \
|
||||||
|
{ \
|
||||||
|
static GQuark q; \
|
||||||
|
static gsize initialized = 0; \
|
||||||
|
\
|
||||||
|
if (g_once_init_enter (&initialized)) \
|
||||||
|
{ \
|
||||||
|
q = g_error_domain_register_static (#ErrorType, \
|
||||||
|
sizeof (ErrorType ## Private), \
|
||||||
|
g_error_with_ ## error_type ## _private_init, \
|
||||||
|
g_error_with_ ## error_type ## _private_copy, \
|
||||||
|
g_error_with_ ## error_type ## _private_clear); \
|
||||||
|
g_once_init_leave (&initialized, 1); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
return q; \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GErrorInitFunc:
|
||||||
|
* @error: extended error
|
||||||
|
*
|
||||||
|
* Specifies the type of function which is called just after an
|
||||||
|
* extended error instance is created and its fields filled. It should
|
||||||
|
* only initialize the fields in the private data, which can be
|
||||||
|
* received with the generated `*_get_private()` function.
|
||||||
|
*
|
||||||
|
* Normally, it is better to use G_DEFINE_EXTENDED_ERROR(), as it
|
||||||
|
* already takes care of getting the private data from @error.
|
||||||
|
*
|
||||||
|
* Since: 2.68
|
||||||
|
*/
|
||||||
|
typedef void (*GErrorInitFunc) (GError *error);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GErrorCopyFunc:
|
||||||
|
* @src_error: source extended error
|
||||||
|
* @dest_error: destination extended error
|
||||||
|
*
|
||||||
|
* Specifies the type of function which is called when an extended
|
||||||
|
* error instance is copied. It is passed the pointer to the
|
||||||
|
* destination error and source error, and should copy only the fields
|
||||||
|
* of the private data from @src_error to @dest_error.
|
||||||
|
*
|
||||||
|
* Normally, it is better to use G_DEFINE_EXTENDED_ERROR(), as it
|
||||||
|
* already takes care of getting the private data from @src_error and
|
||||||
|
* @dest_error.
|
||||||
|
*
|
||||||
|
* Since: 2.68
|
||||||
|
*/
|
||||||
|
typedef void (*GErrorCopyFunc) (const GError *src_error, GError *dest_error);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GErrorClearFunc:
|
||||||
|
* @error: extended error to clear
|
||||||
|
*
|
||||||
|
* Specifies the type of function which is called when an extended
|
||||||
|
* error instance is freed. It is passed the error pointer about to be
|
||||||
|
* freed, and should free the error's private data fields.
|
||||||
|
*
|
||||||
|
* Normally, it is better to use G_DEFINE_EXTENDED_ERROR(), as it
|
||||||
|
* already takes care of getting the private data from @error.
|
||||||
|
*
|
||||||
|
* Since: 2.68
|
||||||
|
*/
|
||||||
|
typedef void (*GErrorClearFunc) (GError *error);
|
||||||
|
|
||||||
|
GLIB_AVAILABLE_IN_2_68
|
||||||
|
GQuark g_error_domain_register_static (const char *error_type_name,
|
||||||
|
gsize error_type_private_size,
|
||||||
|
GErrorInitFunc error_type_init,
|
||||||
|
GErrorCopyFunc error_type_copy,
|
||||||
|
GErrorClearFunc error_type_clear);
|
||||||
|
|
||||||
|
GLIB_AVAILABLE_IN_2_68
|
||||||
|
GQuark g_error_domain_register (const char *error_type_name,
|
||||||
|
gsize error_type_private_size,
|
||||||
|
GErrorInitFunc error_type_init,
|
||||||
|
GErrorCopyFunc error_type_copy,
|
||||||
|
GErrorClearFunc error_type_clear);
|
||||||
|
|
||||||
GLIB_AVAILABLE_IN_ALL
|
GLIB_AVAILABLE_IN_ALL
|
||||||
GError* g_error_new (GQuark domain,
|
GError* g_error_new (GQuark domain,
|
||||||
gint code,
|
gint code,
|
||||||
|
@ -337,6 +337,7 @@ glib_init (void)
|
|||||||
g_messages_prefixed_init ();
|
g_messages_prefixed_init ();
|
||||||
g_debug_init ();
|
g_debug_init ();
|
||||||
g_quark_init ();
|
g_quark_init ();
|
||||||
|
g_error_init ();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined (G_OS_WIN32)
|
#if defined (G_OS_WIN32)
|
||||||
|
@ -27,6 +27,7 @@ extern GLogLevelFlags g_log_msg_prefix;
|
|||||||
|
|
||||||
void glib_init (void);
|
void glib_init (void);
|
||||||
void g_quark_init (void);
|
void g_quark_init (void);
|
||||||
|
void g_error_init (void);
|
||||||
|
|
||||||
#ifdef G_OS_WIN32
|
#ifdef G_OS_WIN32
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
@ -82,6 +82,105 @@ test_copy (void)
|
|||||||
g_error_free (copy);
|
g_error_free (copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int init_called;
|
||||||
|
int copy_called;
|
||||||
|
int free_called;
|
||||||
|
} TestErrorCheck;
|
||||||
|
|
||||||
|
static TestErrorCheck *init_check;
|
||||||
|
|
||||||
|
GQuark test_error_quark (void);
|
||||||
|
#define TEST_ERROR (test_error_quark ())
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int foo;
|
||||||
|
TestErrorCheck *check;
|
||||||
|
} TestErrorPrivate;
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_error_private_init (TestErrorPrivate *priv)
|
||||||
|
{
|
||||||
|
priv->foo = 13;
|
||||||
|
/* If that triggers, it's test bug.
|
||||||
|
*/
|
||||||
|
g_assert_nonnull (init_check);
|
||||||
|
/* Using global init_check, because error->check is still nil at
|
||||||
|
* this point.
|
||||||
|
*/
|
||||||
|
init_check->init_called++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_error_private_copy (const TestErrorPrivate *src_priv,
|
||||||
|
TestErrorPrivate *dest_priv)
|
||||||
|
{
|
||||||
|
dest_priv->foo = src_priv->foo;
|
||||||
|
dest_priv->check = src_priv->check;
|
||||||
|
|
||||||
|
dest_priv->check->copy_called++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_error_private_clear (TestErrorPrivate *priv)
|
||||||
|
{
|
||||||
|
priv->check->free_called++;
|
||||||
|
}
|
||||||
|
|
||||||
|
G_DEFINE_EXTENDED_ERROR (TestError, test_error)
|
||||||
|
|
||||||
|
static TestErrorPrivate *
|
||||||
|
fill_test_error (GError *error, TestErrorCheck *check)
|
||||||
|
{
|
||||||
|
TestErrorPrivate *test_error = test_error_get_private (error);
|
||||||
|
|
||||||
|
test_error->check = check;
|
||||||
|
|
||||||
|
return test_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_extended (void)
|
||||||
|
{
|
||||||
|
TestErrorCheck check = { 0, 0, 0 };
|
||||||
|
GError *error;
|
||||||
|
TestErrorPrivate *test_priv;
|
||||||
|
GError *copy_error;
|
||||||
|
TestErrorPrivate *copy_test_priv;
|
||||||
|
|
||||||
|
init_check = ✓
|
||||||
|
error = g_error_new_literal (TEST_ERROR, 0, "foo");
|
||||||
|
test_priv = fill_test_error (error, &check);
|
||||||
|
|
||||||
|
g_assert_cmpint (check.init_called, ==, 1);
|
||||||
|
g_assert_cmpint (check.copy_called, ==, 0);
|
||||||
|
g_assert_cmpint (check.free_called, ==, 0);
|
||||||
|
|
||||||
|
g_assert_cmpuint (error->domain, ==, TEST_ERROR);
|
||||||
|
g_assert_cmpint (test_priv->foo, ==, 13);
|
||||||
|
|
||||||
|
copy_error = g_error_copy (error);
|
||||||
|
g_assert_cmpint (check.init_called, ==, 2);
|
||||||
|
g_assert_cmpint (check.copy_called, ==, 1);
|
||||||
|
g_assert_cmpint (check.free_called, ==, 0);
|
||||||
|
|
||||||
|
g_assert_cmpuint (error->domain, ==, copy_error->domain);
|
||||||
|
g_assert_cmpint (error->code, ==, copy_error->code);
|
||||||
|
g_assert_cmpstr (error->message, ==, copy_error->message);
|
||||||
|
|
||||||
|
copy_test_priv = test_error_get_private (copy_error);
|
||||||
|
g_assert_cmpint (test_priv->foo, ==, copy_test_priv->foo);
|
||||||
|
|
||||||
|
g_error_free (error);
|
||||||
|
g_error_free (copy_error);
|
||||||
|
|
||||||
|
g_assert_cmpint (check.init_called, ==, 2);
|
||||||
|
g_assert_cmpint (check.copy_called, ==, 1);
|
||||||
|
g_assert_cmpint (check.free_called, ==, 2);
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
main (int argc, char *argv[])
|
main (int argc, char *argv[])
|
||||||
{
|
{
|
||||||
@ -91,6 +190,7 @@ main (int argc, char *argv[])
|
|||||||
g_test_add_func ("/error/prefix", test_prefix);
|
g_test_add_func ("/error/prefix", test_prefix);
|
||||||
g_test_add_func ("/error/literal", test_literal);
|
g_test_add_func ("/error/literal", test_literal);
|
||||||
g_test_add_func ("/error/copy", test_copy);
|
g_test_add_func ("/error/copy", test_copy);
|
||||||
|
g_test_add_func ("/error/extended", test_extended);
|
||||||
|
|
||||||
return g_test_run ();
|
return g_test_run ();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user