Я пытаюсь выполнить регистрацию обратного вызова во встроенной среде. Этот обратный вызов будет в одной из двух форм:
void (*cb) (void *ctxt); or
void ClassA::VirtualFn (void);
Этот код будет работать только на платформе ARM с использованием GCC. Функция обратного вызова регистров ДОЛЖНА быть виртуальной из-за некоторой динамической привязки, выполняемой во время выполнения. Обе указанные функции эквивалентны на уровне сборки (оба они берут один указатель). Кроме того, механизм обратного вызова находится в сборе для целей производительности, поскольку он встречается в контексте ISR, поэтому мне не нужно беспокоиться об этом. Все, что мне действительно нужно, это функция, которая берет любой из приведенных выше и сохраняет переданный указатель функции и указатель контекста. то есть:
void isr_cb (void *ctxt) {}
gpio->RegisterIsr (isr_cb, cptr);
gpio->RegisterIsr (&ClassA::IsrHandler, this);
Я проверил это, отбросив виртуальную функцию-член (void (*) (void *))
и действительно все работает как ожидалось (кроме предупреждений компилятора).
Приведение virtual
функции-члена в void (*)(void *)
звучит как суровый подход.
Ты сказал:
Функция обратного вызова регистров ДОЛЖНА быть виртуальной из-за некоторой динамической привязки, выполняемой во время выполнения.
Я предполагаю, что у вас есть объект типа ClassA
. Если это правильно, вам необходимо:
Зарегистрируйте static
член ClassA
для механизма обратного вызова, чья подпись:
void (*)(ClassA& ref);
Добавьте virtual
функцию-член, которая выполняет реальную работу.
void doStuff();
Скажем, зарегистрированная функция обратного вызова:
static void foo(Class& ref);
В реализации foo
вызовите doStuff
на ref
.
static void foo(Class& ref)
{
ref.doStuff();
}
Во-первых, нет никакой вещи, как универсальный указатель функции в C и C++.
Тем не менее, возможно полностью совместимое и высокопроизводительное решение.
Поскольку функция, не являющаяся членом, уже имеет правильный тип, она тривиальна.
Если вы знаете полный набор не виртуальных функций-членов и слотов виртуальной функции, которые вы, возможно, захотите передать, определите для них простой форвардер в соответствии с строками:
void forward_to_IsrHandler(void* p) {
((ClassA*)p)->IsrHandler();
}
gpio->RegisterIsr (forward_to_IsrHandler, (void*)this);
Если вы не знаете или действительно хотите пожертвовать стандартным соответствием на алтаре производительности, вы можете просто преобразовать указатель на функцию-указатель на обычный указатель функции (расширение gcc, другие компиляторы имеют свои собственные):
void (*tmp)(ClassA*) = (void(*)(ClassA*))&ClassA::IsrHandler;
Используйте -Wno-pmf-conversions
чтобы удалить предупреждение об использовании расширения.
Поскольку вызов функции void(*)(void*)
и void(*)(ClassA*)
выглядит точно так же на ARM, вы можете использовать его таким образом, хотя это, строго говоря, UB:
gpio->RegisterIsr ((void(*)(void*))tmp, (void*)this);
Выборочно отключив предупреждения GCC только для небольшого сегмента кода:
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wno-pmf-conversions"
// Do the foul deed here
#pragma GCC diagnostic pop
У вас будет намного меньше времени, если вы можете использовать либо std::bind
, либо в С++ 11, либо boost::bind
. Оба позволят вам смешивать указатели функций функций и свободные функции друг с другом, если интерфейс (т.е. Фактический тип вывода и фактический тип входных параметров одинаковы), например
int f(double val)
{
}
class A {
int memberf(double val);
}
A instanceOfA;
auto f1 = std::bind(f, std::placeholders::_1);
auto f2 = std::bind(&A::memberf, &instanceOfA, std::placeholders::_1);
f1
и f2
теперь обе функции, которые берут double
и возвращают int
, независимо от того, что один является функцией-членом, а другой - свободной функцией, нет необходимости в броске. Я должен признать, что я не могу записать фактический тип из памяти.
bind
и function
? Имейте в виду, что bind
и friends являются компонентами только для заголовков.
virtual
функция-член? Должно ли оно называться виртуальным или достаточно простой отправки?