Использование указателя функции в качестве параметра метода - PullRequest
2 голосов
/ 06 апреля 2020

Я пытаюсь использовать указатель на функцию, чтобы сделать мой код более эффективным. Тем не менее, я новичок в этой концепции, и у меня возникли проблемы с использованием его в качестве параметра внутри другой функции.

Это приводит к ошибке "инициализация из несовместимого типа указателя". Может кто-нибудь сказать мне, что я делаю неправильно и что я должен делать вместо этого?

Ответы [ 3 ]

4 голосов
/ 06 апреля 2020

Когда вы делаете это:

traverse(node, h, print(node, h));

Вы на самом деле пытаетесь вызвать печать вместо передачи указателя на него. Так что вы переходите к traverse на самом деле возвращаемое значение print вместо указателя на него.

Правильный вызов будет выглядеть так:

traverse(node, h, print);

Тогда внутри traverse вы бы назвали обратный вызов:

void traverse(node_t *node, void *param, func_t func)
{
    ...
    func(node,param);
    ...
}

Однако проблема все еще существует. Тип функции print несовместим с указателем функции func_t. Первый принимает FILE * в качестве первого параметра, в то время как второй принимает void * в качестве второго параметра. Неявное преобразование в / из void * работает только тогда, когда оно является источником или местом назначения преобразования. Это не относится к параметрам функции.

Предполагая, что вам необходим обратный вызов для обработки различных типов, вы можете изменить функцию print, чтобы она принимала void * и преобразовывать параметр внутри функции.

void print(node_t *node, void *param)
{
    FILE *f = param;  // no cast needed
    ...
1 голос
/ 06 апреля 2020

Прежде всего: просто потому, что void* может использоваться вместо любого другого указателя объекта , это не означает, что указатель функции на функцию, принимающую void* совместим с другой функцией, принимающей FILE*.

Так что вам нужно либо изменить тип указателя на функцию для работы с FILE*, либо вы должны изменить функцию печати для работы с void* .

Причина ошибки компилятора заключается в том, что вы вызываете фактическую функцию print здесь traverse(node, h, print(node, h));, а не передаете на нее указатель функции. Поскольку эта функция возвращает void, компилятор говорит: «Эй, я не могу передать параметр void этой функции, ожидающей указатель на функцию». Просто измените это на:

traverse(node, h, print);

и затем traverse вызовет функцию через переданный указатель функции.

0 голосов
/ 06 апреля 2020

Что бы это ни стоило, возможно, вы используете не тот язык программирования :) То, что вы намеревались написать, это в значительной степени C ++, где это будет:

traverse(node, [node,h]{ print(node, h); });

Тогда traverse будет шаблонная функция, так что вы не обязаны передавать только один вид функции, но вместо этого можете передавать любой функтор (вызываемый объект, например, класс, реализующий оператор вызова):

template <typename Fun> void traverse(node_t *, Fun &&f);

Но вы используется указатель на функцию, поскольку он C, а указатель на функцию не похож на замыкание в C ++: он не может содержать параметров. Таким образом, передача параметров на вас. Я представляю, что аргумент FILE* действительно исходит из узла? Это дерево каталогов, которое вы пытаетесь представить?

typedef struct {
  node_t **children;  // null-terminated array of child node pointers
  const char *name;
} node_t;

typedef void (*cnode_observer_t)(const node_t *, int level, void *);

void ctraverse_level(const node_t *node, int level, void *extra, 
                     cnode_observer_t cobserver)
{
  cobserver(node, extra);
  node_t **child = node->children;
  ++level;
  while (*child) {
    ctraverse_level(*child++, level, extra, cobserver);
  }
}

void ctraverse(const node_t *node, void *extra, cnode_observer_t cobserver) {
  ctraverse_level(node, 0, extra, cobserver);
}

void node_printer(const node_t *node, int level, void *file) {
  assert(node && file);
  FILE *f = file;
  fprintf(f, "%*c%s\n", level, ' ', node->name);
}

void test(const node_t *node, FILE *file) {
  ctraverse(node, file, node_printer);
}

Для полноты картины, вот как это будет выглядеть в C ++:

struct Node {
  std::vector<Node> children;
  std::string name;
};

template <typename Obs>
void ctraverse(const Node &node, F fun, int level = 0) {
  fun(node, level);
  ++level;
  for (auto &n : node.children) {
    ctraverse(n, fun);
  }
}

void test(const Node &node, std::ostream &out) {
  traverse(node, [&](auto &node, int level) { 
    out << setw(level) << setfill(' ') << " " << node.name << '\n';
  });
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...