The OpenNET Project / Index page

[ новости /+++ | форум | wiki | теги | ]

Каталог документации / Раздел "Программирование, языки" / Оглавление документа

2.1.3 Макросы для отладки

glib имеет хороший набор макросов, которые вы можете использовать для увеличения инвариантов и предусловий в вашем коде. Они исчезнут после того, как вы определите "G_DISABLE_CHECKS" или "G_DISABLE_ASSERT", поэтому уменьшения производительности в реальном коде не будет. Рекомендуется широко использовать это. Тогда вы сможете находить ошибки намного быстрее. Вы можете даже добавлять условия и проверки всякий раз, когда находите ошибку, чтобы быть уверенными, что ошибка не появится в будущих версиях -- это дополняет регрессивный набор. Проверки особенно полезны, когда код, который вы пишете будет использоваться как черный ящик другими программистами; пользователи будут сразу же знать когда и как они неправильно использовали ваш код.

Естественно, вы должны быть уверены, что ваш код функционирует правильно не только в режиме отладки. Операторы, которые присутствуют в финальном коде никогда не должны иметь побочных эффектов.

Список макросов 2..3: Проверки предусловий
"#include "<glib.h>
g_return_if_fail(condition)
g_return_val_if_fail(condition, retval)

Список макросов 2..3 показывает проверки предусловий glib. "g_return_if_fail()" выводит предупреждение и выходит из текущей функции если условие condition ложно. "g_return_val_if_fail()" действует аналогично, но позволяет вам вернуть retval. Эти макросы невероятно полезны -- если вы их широко используете, особенно в комбинации с проверками Gtk+ периода исполнения, вы съэкономите время при нахождении плохих указателей или ошибок типов.

Использовать эти функции легко, ниже идет пример из реализации хэш-таблицы в glib:

void g_hash_table_foreach(GHashTable *hash_table, GHFunc func,
                          gpointer user_data)
{
  GHashNode *node;
  gint i;
  
  g_return_if_fail(hash_table != NULL);
  g_return_if_fail(func != NULL);
  
  for (i=0; i<hash_table->size; i++)
    for (node=hash_table->nodes[i]; node; node=node->next)
      (* func) (node->key, node->value, user_data);
}
Без проверок, передача NULL в качестве параметра в эту функцию вызовет загадочный аварийный выход. Человек, использующий библиотеку узнает, где случилась ошибка с помощью отладчика, и, возможно, посмотрит в код glib чтобы узнать, что было неправильно. С проверками, они получат красивое сообщение об ошибке, говорящее, что аргументы со значением NULL недопустимы.

Список макросов 2..4: Условия
"#include "<glib.h>
g_assert(condition)
g_assert_not_reached()

В glib также есть традиционные макросы условий, показанные в списке макросов 2..4. "g_assert()" в основном аналогичен "assert()", но реагирует на "G_DISABLE_ASSERT" и ведет себя одинаково на всех платформах. Есть также и "g_assert_not_reached()"; это условие, которое всегда ложно. Проверки условий вызывают "abort()" для выхода из программы и (если ваша среда это позволяет) записывает дамп памяти для отладочных целей.

Фатальные условия должны использоваться для проверки внутренней целостности функции или библиотеки, в то время как "g_return_if_fail()" предназначен для того, чтобы гарантировать приемлемые значения, которые передаются общим интерфейсам программного модуля. То есть, если условие не выполнится, вы, скорее всего будете искать ошибку в модуле, содержащем условие; если проверка "g_return_if_fail()" не пройдет, вы, скорее всего, будете искать ошибку в коде, который вызывает модуль.

Этот код, взятый из календарных вычислений glib показывает разницу:

GDate *g_date_new_dmy(GDateDay day, GDateMonth m, GDateYear y)
{
  GDate *d;
  g_return_val_if_fail(g_date_valid_dmy(day, m, y), NULL);
  
  d = g_new(GDate, 1);
  
  d->julian = FALSE;
  d->dmy    = TRUE;
  
  d->month = m;
  d->day   = day;
  d->year  = y;
  
  g_assert(g_date_valid(d));
  
  return d;
}

Проверка предусловия в начале гарантирует то, что пользователь передал осмысленные аргументы для числа, месяца и года; условие в конце дает гарантию того, что glib создал приемлемый объект с данными нормальными значениями.

"g_assert_not_reached()" должен быть использован для отметки невозможных ситуаций; обычное использование -- обнаружение операторов в блоке switch, которые не обрабатывают все возможные значения перечисления:

switch (val)
{
  case FOO_ONE:
    break;
  case FOO_TWO:
    break;
  default:
" /* Неправильное значение перечисления */"
    g_assert_not_reached();
    break;
}

Все отладочные макросы печатают предупреждения, используя "g_log()" из glib, что означает, что предупреждение включает имя порождающей его программы или библиотеки, и вы можете дополнительно установить замену для процедуры печати предупреждений. Например, вы можете посылать все предупреждения в диалоговое окно или лог-файл, вместо печати их на консоль.


Linux Land
2000-09-15



Спонсоры:
Inferno Solutions
Hosting by Hoster.ru
Хостинг:

Закладки на сайте
Проследить за страницей
Created 1996-2021 by Maxim Chirkov
Добавить, Поддержать, Вебмастеру