Справочное описание GLib | ||||
---|---|---|---|---|
#include <glib.h>
GError;
GError* g_error_new (GQuark domain,
gint code,
const gchar *format,
...);
GError* g_error_new_literal (GQuark domain,
gint code,
const gchar *message);
void g_error_free (GError *error);
GError* g_error_copy (const GError *error);
gboolean g_error_matches (const GError *error,
GQuark domain,
gint code);
void g_set_error (GError **err,
GQuark domain,
gint code,
const gchar *format,
...);
void g_propagate_error (GError **dest,
GError *src);
void g_clear_error (GError **err);
GLib обеспечивает стандартный метод сообщения об ошибках из вызванной функции в вызываемом коде. (Это также решает проблему исключительной ситуации в других языках). Важно понять что это метод - data type (GError объект) и набора правил. Если вы используете GError неправильно, то ваш код не будет правильно взаимодействовать с другим кодом использующим GError, а пользователи вашего API вероятно запутаются.
Прежде всего: GError должен использоваться только для сообщения о восстанавливаемых ошибках в процессе выполнения.
Обычно вы должны использовать g_warning()
,
g_return_if_fail()
, g_assert()
, g_error()
, или похожее средство.
(помните что функция g_error()
должна использоваться
только для программных ошибок, она не должна использоваться для печати любой ошибки о которой сообщено через GError.)
Примером восстанавливаемых ошибок во время выполнения программы являются
"file not found" (файл не найден) или "failed to parse input" (не удаётся проанализировать ввод).
Примером программных ошибок являются "NULL passed to strcmp()
" или
"attempted to free the same pointer twice". Эти два вида ошибок существенно отличаются:
ошибки во время выполнения должны обрабатываться или о них должно быть сообщено пользователю,
програмные ошибки должны ликвидироваться исправлением в программе.
Поэтому большинство функций в GLib и GTK+ не используют GError.
Функции которые могут завершиться неудачно принимают в качестве последнего параметра место расположения возвращаемой GError. Например:
gboolean g_file_get_contents (const gchar *filename,
gchar **contents,
gsize *length,
GError **error);
Если вы поместили не-NULL
значение в параметр error
, он должен указывать на место куда может быть помещена ошибка. Например:
gchar *contents;
GError *err = NULL;
g_file_get_contents ("foo.txt", &contents, NULL, &err);
g_assert ((contents == NULL && err != NULL) || (contents != NULL && err == NULL));
if (err != NULL)
{
/* Сообщаем об ошибке пользователю, и освобождаем ошибку */
g_assert (contents == NULL);
fprintf (stderr, "Unable to read file: %s\n", err->message);
g_error_free (err);
}
else
{
/* Используем содержимое файла */
g_assert (contents != NULL);
}
Помните что err != NULL
в этом примере
надежно проверяет не закончилась ли неудачей
g_file_get_contents()
. Дополнительно, g_file_get_contents()
возвращает логическое которое указывает была ли она завершена удачно.
Поскольку g_file_get_contents()
возвращает FALSE
при неудаче, если вы интересуетесь только статусом завершения и не нуждаетесь в
отображении сообщения об ошибке, вы можете поместить NULL
в параметр error
:
if (g_file_get_contents ("foo.txt", &contents, NULL, NULL)) /* игнорирование ошибок */
/* нет произошедших ошибок */ ;
else
/* ошибка */ ;
Объект GError содержит три поля: domain
указывает функцию сообщения об ошибках,
code
указывает произошедшую специфическую ошибку,
а message
читабельное для пользователя сообщение которое более
детально описывает ошибку насколько это возможно. Некоторые функции предназначены для связи с
ошибкой полученной из вызываемой функции:
g_error_matches()
возвращает TRUE
если ошибка соответствует данной domain и code,
g_propagate_error()
копирует ошибку в место хранения ошибки (таким образом вызываемая функция получает её),
а g_clear_error()
очищает место расположения ошибки освобождая ошибку и переустанавливая место хранения ошибки в
NULL
.
Для отображения ошибки пользователю, просто отобразите
error->message
, возможно наряду с дополнительным контекстом
известным только вызываемой функции (открываемый файл, или любой -- хотя в случае g_file_get_contents()
,
error->message
уже содержит имя файла).
При реализации функций способных сообщать об ошибках основной инструмент g_set_error()
.
Обычно, если происходит фатальная ошибка вам необходима g_set_error()
,
затем немедленное возвращение. g_set_error()
ничего не делает если расположение ошибки помещённое в неё NULL
. Например:
gint
foo_open_file (GError **error)
{
gint fd;
fd = open ("file.txt", O_RDONLY);
if (fd < 0)
{
g_set_error (error,
FOO_ERROR, /* область ошибки (error domain) */
FOO_ERROR_BLAH, /* код ошибки (error code) */
"Failed to open file: %s", /* форматированная строка сообщения об ошибке */
g_strerror (errno));
return -1;
}
else
return fd;
}
Немного сложнее если вы самостоятельно вызываете функцию которая может сообщить
GError.
Если подчинённая функция указывает на неисправимые ошибки некоторым способом отличающимся от сообщения GError, таким как возвращение TRUE
при удачном исполнении, вы можете просто сделать следующее:
gboolean
my_function_that_can_fail (GError **err)
{
g_return_val_if_fail (err == NULL || *err == NULL, FALSE);
if (!sub_function_that_can_fail (err))
{
/* предполагается что ошибка была установлена с помощью подчинённой функции (sub-function) */
g_assert (err == NULL || *err != NULL);
return FALSE;
}
/* иначе продолжаем, ошибка не происходит */
g_assert (err == NULL || *err == NULL);
}
Если подчинённая функция не указывает о других ошибках кроме как сообщения GError,
вы должны создать временную GError та как помещаемая в неё может быть NULL
.
g_propagate_error()
предназначена для использования в этом случае.
gboolean
my_function_that_can_fail (GError **err)
{
GError *tmp_error;
g_return_val_if_fail (err == NULL || *err == NULL, FALSE);
tmp_error = NULL;
sub_function_that_can_fail (&tmp_error);
if (tmp_error != NULL)
{
/* сохраняем tmp_error в err, если err != NULL,
* иначе вызываем g_error_free() для tmp_error
*/
g_propagate_error (err, tmp_error);
return FALSE;
}
/* иначе продолжаем, ошибка не происходит */
}
Error pileups являются всегда ошибкой. Например, этот код неправильный:
gboolean
my_function_that_can_fail (GError **err)
{
GError *tmp_error;
g_return_val_if_fail (err == NULL || *err == NULL, FALSE);
tmp_error = NULL;
sub_function_that_can_fail (&tmp_error);
other_function_that_can_fail (&tmp_error);
if (tmp_error != NULL)
{
g_propagate_error (err, tmp_error);
return FALSE;
}
}
tmp_error
должна быть проверена немедленно после
,
и либо очищена либо распространена на верх.
Правило: после каждой ошибки, вы должны либо обработать ошибку, либо вернуть её в вызываемую функцию. Помните что sub_function_that_can_fail()
NULL
для параметра расположения ошибки эквивалентно обработке ошибки ничего не делая с ней.
Таким образом следующий код является хорошим примером, предполагается что ошибки в
являются не фатальными для sub_function_that_can_fail()
:
my_function_that_can_fail()
gboolean
my_function_that_can_fail (GError **err)
{
GError *tmp_error;
g_return_val_if_fail (err == NULL || *err == NULL, FALSE);
sub_function_that_can_fail (NULL); /* игнорирование ошибок */
tmp_error = NULL;
other_function_that_can_fail (&tmp_error);
if (tmp_error != NULL)
{
g_propagate_error (err, tmp_error);
return FALSE;
}
}
Помните что помещаемый NULL
в параметр размещения ошибки означает игнорирование
ошибок; это эквивалентно
try {
в C++. Это не значит оставить ошибки не обработанными; это значит обработать ошибки ничего не делая.
sub_function_that_can_fail()
; } catch
(...) {}
Области и коды ошибки традиционно именуются следующим образом:
Область ошибки называют согласно шаблона
<NAMESPACE>_<MODULE>_ERROR
, например
G_EXEC_ERROR
или G_THREAD_ERROR
.
Коды ошибки находятся в перечислении называемом согласно шаблона <Namespace><Module>Error
; например,
GThreadError или GSpawnError.
Элементы перечисления кодов ошибки называются согласно шаблона <NAMESPACE>_<MODULE>_ERROR_<CODE>
, например G_SPAWN_ERROR_FORK
или G_THREAD_ERROR_AGAIN
.
Если есть "generic" или "unknown" коды ошибки для неисправимых ошибок нет смысла различать их
другими специфическими кодами, они должны быть названы по шаблону <NAMESPACE>_<MODULE>_ERROR_FAILED
, например G_SPAWN_ERROR_FAILED
or G_THREAD_ERROR_FAILED
.
Сводные правила использования GError:
Не сообщайте о программных ошибках через GError.
Последним параметром функции возвращающей ошибку должно быть место размещения GError (то есть "GError ** error"). Если GError используется с переменным количеством аргументов, то GError** должна быть последним аргументом перед "...".
Вызывающий может помещать NULL
для GError** если его не интересуют подробности произошедшей ошибки.
Если NULL
помещён для GError**
аргумента, тогда ошибка не должна возвращаться к вызывающей функции, но ваша функция всё же должна
прерываться и возвращаться если произошла ошибка. Таким образом, на управление потоком не должно влиять получение или неполучение вызывающей функцией GError.
Если о GError сообщено, то ваша функция по определению имеет фатальную ошибку и не закончила то что должна была сделать. Если произошла не фатальная ошибка, то вы обрабатываете её и не должны сообщать о ней. Если она была фатальной, то вы должны сообщить о ней и прекратить выполнение немедленно.
GError* должна быть инициализирована в значение NULL
перед помещением её адреса в функцию которая может сообщать об ошибках.
"Накопление" ошибок - всегда ошибка. Таким образом, если вы назначили новую
GError для GError* которая не-NULL
, переписывая таким образом предыдущую ошибку,
она указывает вам что нужно прервать операцию вместо продолжения. Вы можете продолжать
если очистите предыдущую ошибку с помощью g_clear_error()
. g_set_error()
будет предупреждать если вы накапливаете ошибки.
В соответствие с соглашением, если вы возвращаете логическое значение, то
TRUE
означает успешное выполнение, а FALSE
означает неудачу. Если возвращено значение FALSE
,
ошибка должна быть установлена в значение
не-NULL
.
Возвращённое значение NULL
часто также означает что произошла ошибка. Вы должны разъяснить в вашей документации означает ли NULL
допустимое возвращаемое значение в случае безошибочного выполнения; если NULL
допустимое значение,
то пользователи должны проверить была ли возвращена ошибка, чтобы увидеть выполнена ли
функция успешно.
Когда реализуется функция которая может сообщать об ошибках, вы можете добавить
проверку в начале вашей функции является ли место расположения ошибки значением
NULL
или
используется ли значение NULL
в качестве указателя ошибки
(например g_return_if_fail (error == NULL || *error ==
NULL);
).
typedef struct {
GQuark domain;
gint code;
gchar *message;
} GError;
Структура GError содержит информацию о произошедшей ошибке.
GQuark domain ; |
область ошибки, например G_FILE_ERROR. |
gint code ; |
код ошибки, например G_FILE_ERROR_NOENT .
|
gchar *message ; |
удобное для чтения пользователем информационное сообщение об ошибке. |
GError* g_error_new (GQuark domain,
gint code,
const gchar *format,
...);
Создаёт новую GError с полученными domain
и code
,
а сообщение форматируется с помощью format
.
domain : |
область ошибки |
code : |
код ошибки |
format : |
printf() -стиль форматирования для сообщения об ошибке
|
... : |
параметры для форматирования сообщения |
Возвращает : | новая GError |
GError* g_error_new_literal (GQuark domain,
gint code,
const gchar *message);
Создаёт новую GError; в отличие от g_error_new()
, message
является строкой не в printf()
-стиле форматирования. Используйте эту функцию если message
содержит текст не контролируемый вами,
который мог бы включать printf()
escape-последовательность.
domain : |
область ошибки |
code : |
код ошибки |
message : |
сообщение об ошибке |
Возвращает : | новая GError |
void g_error_free (GError *error);
Освобождает GError и связанные с ней ресурсы.
error : |
GError |
gboolean g_error_matches (const GError *error,
GQuark domain,
gint code);
Возвращает TRUE
если
error
соответствует domain
и code
, FALSE
в любом другом случае.
error : |
GError |
domain : |
область ошибки |
code : |
код ошибки |
Возвращает : | соответствует ли error domain и code
|
void g_set_error (GError **err,
GQuark domain,
gint code,
const gchar *format,
...);
Ничего не делает если err
имеет значение NULL
; если err
не-NULL
, то *err
должна быть NULL
. Новая GError создаётся привязанной к *err
.
void g_propagate_error (GError **dest,
GError *src);
Если dest
имеет значение NULL
, освобождает src
; иначе,
перемещает src
в *dest
. *dest
должен быть NULL
.
dest : |
расположение возвращаемой ошибки |
src : |
ошибка для перемещения в расположение указанное параметром dest
|