Есть ли способ конвертировать void (*) (uint32_t, void (*) (uint32_t)) в std :: function)>? - PullRequest
1 голос
/ 13 октября 2019

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

typedef void (*log_function_t)(const char *);
typedef void (*delay_function_callback_t)(uint32_t);
typedef void (*delay_function_t)(uint32_t, delay_function_callback_t);

extern "C" void core_init(log_function_t logFunction, delay_function_t delayFunction);

Однако, поскольку я пишу библиотеку на C ++, было бы неплохо работать с объектами std :: function вместоуказатели на функции, поэтому я хотел бы вызывать функции следующим образом:

using LogFunction = std::function<void(const char*)>;
using DelayFunctionCallback = std::function<void(uint32_t)>;
using DelayFunction = std::function<void(uint32_t, DelayFunctionCallback)>;

void setLogFunction(const LogFunction& logFunction);
void setDelayFunction(const DelayFunction& delayFunction);

Вызов setLogFunction работает просто отлично, но когда я пытаюсь вызвать setDelayFunction, он не работает.

void core_init(log_function_t logFunction, delay_function_t delayFunction)
{
    Utility::getInstance().setLogFunction(logFunction);
    Utility::getInstance().setDelayFunction(delayFunction);
}

Там написано: Reference to type 'const DelayFunction' (aka 'const function<void (unsigned int, function<void (unsigned int)>)>') could not bind to an lvalue of type 'delay_function_t' (aka 'void(*)(unsigned int, void (*)(unsigned int))')

Очевидно, я понимаю, почему это не работает, но у меня есть ощущение, что это можно решить, и я просто недостаточно опытен, чтобы решатьэто.

Ответы [ 2 ]

2 голосов
/ 13 октября 2019

То, что вы спрашиваете, похоже, передает указатель функции из C в C ++, где функция принимает std::function в качестве аргумента. Боюсь, что это невозможно, так как C не может передать указатель на функцию, которая принимает std::vector в качестве аргумента.

При вызове Utility::getInstance().setDelayFunction(delayFunction), ctor специализированного std::function (то есть DelayFunction) соответствует конструкции из указателя функции. Однако сопоставление не удается, потому что ctor (из DelayFunction) принимает в качестве второго аргумента специализированный std::function (т. Е. DelayFunctionCallback), а не указатель на функцию (т. Е. delay_function_callback_t).

IЯ думаю, что проблема заключается в реализации std::function, которая инкапсулирует указатель функции и стирает тип последнего. (См. Как реализована функция std :: function? ). В результате тип C ++ std::function отличается от указателя на функцию обычного C.

Чтобы обойти это, вы могли бынемного ослабьте C ++ и объявите DelayFunction принятием void(*)(unsigned). Т.е. в файле C ++:

using LogFunction = std::function<void(const char*)>;
using DelayFunction = std::function<void(unsigned, delay_function_callback_t)>;
//                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^

РЕДАКТИРОВАТЬ: Re. комментарий о вызове объекта DelayFunction из C ++ вместо передачи функции lamba в качестве обратного вызова (что не сработало бы с обходным путем, описанным выше, поскольку лямбда-функция может создать только DelayFunctionCallback, а не delay_function_callback_t), онаможет быть проще реализовать обратный вызов как статическую функцию-член и использовать ее напрямую:

Utility::getInstance().delay(delay, (delay_function_callback_t)&Utility::next);

Кстати, если Utility собирается хранить объекты std::function внутри, то это может бытьболее эффективно передавать по значению, поскольку объекты LogFunction и DelayFunction всегда будут создаваться в любом случае (т. е. они имеют значение r в core_init).

1 голос
/ 13 октября 2019

A void(*)() принципиально отличается от std::function<void()>.

Вы можете приблизиться с помощью void(*)( void* ), void*;функция std имеет состояния callable-ness и , указатель функции имеет только callable-ness. (функция std также содержит RTTI, состояние как для очистки и состояние для копирования).

Теперь вы можете преобразовать void(*)() в функцию std, которая сильнее;но не наоборот. И аргументы функции преобразуются в другую сторону , когда происходит вызов.

struct callback {
  void* state;
  void(*action)(int32_t);
  void(*cleanup)(void*);
  void*(*copy)(void*);
};

, что является грубым C-эквивалентом std::function<void(int32_t)>.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...