Книга: Разработка приложений в среде Linux. Второе издание

26.3. Использование обратных вызовов

26.3. Использование обратных вызовов

Мы показали два способа обработки параметров с помощью библиотеки popt: с помощью возврата параметра функцией poptGetNextOpt() и путем автоматического изменения переменных во время представления параметров. К сожалению, ни один из этих способов не подходит для вложенных таблиц. Очевидно, что возвращение параметров, определяемых во вложенной таблице для обработки в приложении, не будет работать, поскольку вложенные таблицы предназначены для того, чтобы приложению не нужно было знать, какие параметры предлагает библиотека. Присвоение переменным значений тоже не подходит, поскольку в этом случае не ясно, каким переменным нужно присваивать значения. Использование глобальных переменных часто тоже является неподходящим, а библиотека не имеет доступных для использования локальных переменных, поскольку синтаксический анализ выполняется из главного приложения, а не из библиотеки. Чтобы обеспечить гибкую обработку параметров во вложенных таблицах, библиотека popt предлагает использовать обратные вызовы (callback).

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

Обратные вызовы можно определять только в первом элементе таблицы параметров. Если этот элемент определяет обратный вызов, член argInfo будет иметь значение POPT_ARG_CALLBACK, a arg будет указывать на функцию обратного вызова. Член descrip может представлять любое значение указателя, и передается в обратный вызов каждый раз во время его инициирования, открывая доступ к любым произвольным данным. Все остальные члены структуры struct poptOption должны иметь нулевое значение или NULL.

Во время обработки параметров обратный вызов можно инициировать в трех точках: до начала обработки, при нахождении параметра в таблице для данного обратного вызова и после завершения обработки. Это дает библиотекам возможность инициализировать любые необходимые им структуры (включая данные, определяемые членом descrip), и выполнять любые служебные действия, которые могут понадобиться после завершения обработки (например, очищать динамическую память, выделенную для члена descrip). Они всегда вызываются при нахождении параметра, однако в таблице параметров необходимо указать, что их нужно вызывать в двух других местах. Чтобы сделать это, значения POPT_CBFLAG_PRE или POPT_CBFLAG_POST (или оба) должны объединяться битовым "ИЛИ" со значением POPT_ARG_CALLBACK, присвоенным члену arg структуры, которая определяет обратный вызов.

Далее показан прототип, который следует использовать для определения функции обратного вызова:

void callback(poptContext con, enum poptCallbackReason reason,
 const struct poptOption * opt, const char * arg,
 const void * data);

Первый параметр представляет содержимое, синтаксический анализ которого будет выполнен во время инициирования обратного вызова. Следующим параметров является POPT_CALLBACK_REASON_PRE, если обработка параметра еще не началась, POPT_CALLBACK_REASON_POST, если обработка параметров завершена, или POPT_CALLBACK_REASON_OPTION, если в таблице для данного обратного вызова был обнаружен параметр. Если этот параметр является последним, то аргумент opt будет указывать на элемент таблицы параметров для обнаруженного параметра, а аргумент arg будет указывать на строку, определяющую аргумент для данного параметра. Если ожидается аргумент, не представленный в виде строки, обратный вызов будет отвечать за проверку типа и преобразование аргумента. Последний параметр для обратного вызова, data, представляет собой значение поля descrip в элементе таблицы параметров, который задает обратный вызов.

Ниже показан пример библиотеки, которая использует вложенную таблицу popt и обратные вызовы для синтаксического анализа некоторых параметров командной строки. Структура данных инициализируется до начала синтаксического анализа командной строки, а затем отображаются последние значения.

 1: /* popt-lib.с */
 2:
 3: #include <popt.h>
 4: #include <stdlib.h>
 5:
 6: struct params {
 7:  int height, width;
 8:  char*fg,*bg;
 9: };
10:
11: static void callback(poptContext con,
12:  enum poptCallbackReason reason,
13:  const struct poptOption * opt,
14:  const char * arg,
15:  const void * data);
16:
17: /* Здесь сохраняются переменные, которые прошли синтаксический анализ. Обычно
18:    глобальные переменные использовать не рекомендуется, зато работать с ними проще.*/
19: struct params ourParam;
20:
21: struct poptOption libTable[] = {
22:  { NULL, '',
23:    POPT_ARG_CALLBACK | POPT_CBFLAG_PRE | POPT_CBFLAG_POST,
24:    callback, '', (void *) &ourParam, NULL },
25:  { "height", 'h', POPT_ARG_STRING, NULL, '', NULL, NULL },
26:  { "width", 'w', POPT_ARG_STRING, NULL, '', NULL, NULL },
27:  { "fg", 'f', POPT_ARG_STRING, NULL, '', NULL, NULL },
28:  { "bg", 'b', POPT_ARG_STRING, NULL, '', NULL, NULL },
29:  { NULL, '', POPT_ARG_NONE, NULL, '', NULL, NULL }
30: };
31:
32: static void callback(poptContext con,
33:  enum poptCallbackReason reason,
34:  const struct poptOption * opt,
35:  const char * arg,
36:  const void * data) {
37:  struct params * p = (void *) data;
38:  char * chptr = NULL;
39:
40:  if (reason == POPT_CALLBACK_REASON_PRE) {
41:   p->height = 640;
42:   p->width = 480;
43:   p->fg = "white";
44:   p->bg = "black";
45:  } else if (reason == POPT_CALLBACK_REASON_POST) {
46:   printf("используется высота %d ширина %d передний план %s фон %sn",
47:   p->height, p->width, p->fg, p->bg);
48:
49:  } else {
50:   switch (opt->shortName) {
51:   case 'h': p->height = strtol(arg, &chptr, 10); break;
52:   case 'w': p->width = strtol(arg, &chptr, 10); break;
53:   case 'f' : p->fg = (char *) arg; break;
54:   case 'b': p->bg = (char *) arg; break;
55:   }
56:
57:   if (chptr && *chptr) {
58:    fprintf(stderr, "для %s ожидался числовой аргументn",
59:     opt->longName);
60:    exit(1);
61:   }
62:  }
63: }
64:

Программа, для которой необходимо обеспечить эти аргументы командной строки, должна включать одну дополнительную строку в своей таблице popt. Обычно этой строкой является макрос, задаваемый в заголовочном файле (подобно тому, как реализуется POPT_AUTOHELP), но в целях упрощения в данном примере мы просто явным образом покажем эту строку.

 1: /* popt-nest.c */
 2:
 3: #include <popt.h>
 4:
 5: /* Обычно это объявление осуществляется в заголовочном файле */
 6: extern struct poptOption libTable[];
 7:
 8: int main(int argc, const char * argv[]) {
 9:  poptContext optCon;
10:  int rc;
11:  struct poptOption options[] = {
12:   { "app1", '', POPT_ARG_NONE, NULL, '' },
13:   { NULL, '', POPT_ARG_INCLUDE_TABLE, libTable,
14:     '', "Nested:", }
15:     POPT_AUTOHELP
16:   { NULL, '', POPT_ARG_NONE, NULL, '' }
17:  };
18:
19:  optCon = poptGetContext("popt-nest", argc, argv, options, 0);
20:
21:  if ((rc = poptGetNextOpt (optCon)) < -1) {
22:   fprintf(stderr, "%s: %sn",
23:    poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
24:   poptStrerror(rc));
25:   return 1;
26:  }
27:
28:  return 0;
29: }

Оглавление книги


Генерация: 3.306. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз