mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-11-10 11:26:16 +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_prefix_error
|
||||
g_propagate_prefixed_error
|
||||
<SUBSECTION>
|
||||
GErrorInitFunc
|
||||
GErrorCopyFunc
|
||||
GErrorClearFunc
|
||||
G_DEFINE_EXTENDED_ERROR
|
||||
g_error_domain_register_static
|
||||
g_error_domain_register
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
|
359
glib/gerror.c
359
glib/gerror.c
@ -372,27 +372,338 @@
|
||||
* 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.
|
||||
* `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 <string.h>
|
||||
|
||||
#include "gerror.h"
|
||||
|
||||
#include "ghash.h"
|
||||
#include "glib-init.h"
|
||||
#include "gslice.h"
|
||||
#include "gstrfuncs.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_new_steal (GQuark domain,
|
||||
gint code,
|
||||
gchar *message)
|
||||
g_error_allocate (GQuark domain, ErrorDomainInfo *out_info)
|
||||
{
|
||||
GError *error = g_slice_new (GError);
|
||||
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 *
|
||||
g_error_new_steal (GQuark domain,
|
||||
gint code,
|
||||
gchar *message,
|
||||
ErrorDomainInfo *out_info)
|
||||
{
|
||||
ErrorDomainInfo info;
|
||||
GError *error = g_error_allocate (domain, &info);
|
||||
|
||||
error->domain = domain;
|
||||
error->code = code;
|
||||
error->message = message;
|
||||
|
||||
if (info.init != NULL)
|
||||
info.init (error);
|
||||
if (out_info != NULL)
|
||||
*out_info = info;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -424,7 +735,7 @@ g_error_new_valist (GQuark domain,
|
||||
g_warn_if_fail (domain != 0);
|
||||
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 (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
|
||||
g_error_free (GError *error)
|
||||
{
|
||||
gsize private_size;
|
||||
ErrorDomainInfo *info;
|
||||
guint8 *allocated;
|
||||
|
||||
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*
|
||||
g_error_copy (const GError *error)
|
||||
{
|
||||
GError *copy;
|
||||
ErrorDomainInfo info;
|
||||
|
||||
g_return_val_if_fail (error != NULL, NULL);
|
||||
/* See g_error_new_valist for why these don't return */
|
||||
g_warn_if_fail (error->domain != 0);
|
||||
g_warn_if_fail (error->message != NULL);
|
||||
|
||||
return g_error_new_steal (error->domain,
|
||||
copy = g_error_new_steal (error->domain,
|
||||
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;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
GError* g_error_new (GQuark domain,
|
||||
gint code,
|
||||
|
@ -337,6 +337,7 @@ glib_init (void)
|
||||
g_messages_prefixed_init ();
|
||||
g_debug_init ();
|
||||
g_quark_init ();
|
||||
g_error_init ();
|
||||
}
|
||||
|
||||
#if defined (G_OS_WIN32)
|
||||
|
@ -27,6 +27,7 @@ extern GLogLevelFlags g_log_msg_prefix;
|
||||
|
||||
void glib_init (void);
|
||||
void g_quark_init (void);
|
||||
void g_error_init (void);
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
#include <windows.h>
|
||||
|
@ -82,6 +82,105 @@ test_copy (void)
|
||||
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
|
||||
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/literal", test_literal);
|
||||
g_test_add_func ("/error/copy", test_copy);
|
||||
g_test_add_func ("/error/extended", test_extended);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user