Использование общих указателей на функции - PullRequest
0 голосов
/ 28 июля 2011

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

Вот прототипы функций, на которые я пытаюсь ссылаться:

int edit_translate_concise(struct ged *gedp, const union edit_cmd * const cmd);
int edit_translate_add_arg(union edit_cmd * const cmd, struct edit_arg * const arg);

Структура имассив этой структуры выглядит следующим образом:

struct edit_cmd_tab {
    char *name;
    char *opt_global;
    char *usage;
    char *help;
    int (*exec_concise)(struct ged *gedp, const union edit_cmd *const cmd);
    int (*add_arg)(union edit_cmd *const cmd, struct edit_arg *const arg);
};

static const struct edit_cmd_tab edit_cmds[] = {
    ...
    {"translate",       (char *)NULL,
        "[FROM] TO OBJECT ...",
        "[[-n] -k {FROM_OBJECT | FROM_POS}]\n"
            "[-n] [-a | -r] {TO_OBJECT | TO_POS} OBJECT ...",
        &edit_translate_concise,
        &edit_translate_add_arg
    },
    ...
};

Итак, функции, на которые мне нужно указать, принимают те же аргументы и возвращают тот же тип, что и члены-указатели на функции структуры.

Я получаю эти предупреждения, ссылаясь на две последние строки первой структуры:

/home/bhinesley/brlcad-trunk/src/libged/edit.c:866:55: warning: ‘union edit_cmd’ declared inside parameter list [enabled by default]
/home/bhinesley/brlcad-trunk/src/libged/edit.c:866:55: warning: its scope is only this definition or declaration, which is probably not what you want [enabled by default]
/home/bhinesley/brlcad-trunk/src/libged/edit.c:867:54: warning: ‘union edit_cmd’ declared inside parameter list [enabled by default]

И эти предупреждения ссылаются на две последние строки массива:

/home/bhinesley/brlcad-trunk/src/libged/edit.c:1188:2: warning: initialization from incompatible pointer type [enabled by default]
/home/bhinesley/brlcad-trunk/src/libged/edit.c:1188:2: warning: (near initialization for ‘edit_cmds[1].exec_concise’) [enabled by default]
/home/bhinesley/brlcad-trunk/src/libged/edit.c:1190:5: warning: initialization from incompatible pointer type [enabled by default]
/home/bhinesley/brlcad-trunk/src/libged/edit.c:1190:5: warning: (near initialization for ‘edit_cmds[1].add_arg’) [enabled by default]

1 Ответ

8 голосов
/ 28 июля 2011

Вы делаете [почти] все правильно.

Однако вы должны убедиться, что фактическое объединение union edit_cmd объявлено, прежде чем использовать его в прототипе функции. Если вы забудете объявить его, компилятор будет обрабатывать union edit_cmd как объявление совершенно нового типа объединения, который local для прототипа функции. То есть это локальное объявление union edit_cmd не будет иметь никакого отношения к фактическому объявлению вашего union edit_cmd, которое вы будете иметь в другом месте.

Это то, что происходит в вашем случае. Об этом вас и предупреждает компилятор. То же самое относится к struct ged и struct edit_arg. Видимо, вы забыли включить заголовочные файлы, которые содержат объявления union edit_cmd, struct ged и struct edit_arg.

Например, этот простой код иллюстрирует проблему

void foo(union bar *p);

union bar {
  int a;
};

union bar, объявленный в прототипе foo, не имеет абсолютно никакого отношения к union bar, объявленному позже. Первый - локальный по отношению к прототипу, второй - глобальный (то есть тип уровня файла). Если позже попробуйте сделать

union bar u;
foo(&u);

вы получите диагностическое сообщение от компилятора о несоответствии типа аргумент-параметр. Это же несоответствие вызывает вторую группу предупреждений в вашем сообщении (о несовместимых типах указателей при инициализации массива).

Но если вы переставите декларации таким образом

union bar {
  int a;
};

void foo(union bar *p);

все будет работать нормально, поскольку union bar в прототипе теперь относится к ранее объявленному union bar. В качестве альтернативы вы можете объявить форвард union bar, как в

union bar;
void foo(union bar *p);

union bar {
  int a;
};

Это также заставит компилятор обрабатывать union bar в foo как глобальный тип (т.е. тип уровня файла), а не как локальный.

Что касается вызова ваших функций через указатели, это можно сделать как

(*edit_cmds[i].add_arg)( /* arguments go here */ );

или даже без оператора *

edit_cmds[i].add_arg( /* arguments go here */ );

Подводя итог вышесказанному, можно легко решить проблемы, которые вы наблюдаете, - добавить предварительные объявления на уровне файлов для типов struct и union перед прототипами функций

.
struct ged;
union edit_cmd;
struct edit_arg;
int edit_translate_concise(struct ged *gedp, const union edit_cmd * const cmd);
int edit_translate_add_arg(union edit_cmd * const cmd, struct edit_arg * const arg);

Более элегантный подход - включить полные определения этих типов до объявления прототипа.

...