Как я могу передать указатель универсальной функции в C ++?

0

Я пытаюсь выполнить регистрацию обратного вызова во встроенной среде. Этот обратный вызов будет в одной из двух форм:

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 *)) и действительно все работает как ожидалось (кроме предупреждений компилятора).

  • 0
    Это действительно virtual функция-член? Должно ли оно называться виртуальным или достаточно простой отправки?
  • 0
    Да, к сожалению, это должно быть виртуальным. Я выполняю загрузку elf-объектов во время выполнения, поэтому все функции-члены являются виртуальными в обоих классах. Я считаю, что это мешает мне использовать какой-либо шаблонный механизм, верно?
Показать ещё 4 комментария
Теги:
pointers

3 ответа

1

Приведение virtual функции-члена в void (*)(void *) звучит как суровый подход.

Ты сказал:

Функция обратного вызова регистров ДОЛЖНА быть виртуальной из-за некоторой динамической привязки, выполняемой во время выполнения.

Я предполагаю, что у вас есть объект типа ClassA. Если это правильно, вам необходимо:

  1. Зарегистрируйте static член ClassA для механизма обратного вызова, чья подпись:

    void (*)(ClassA& ref);
    
  2. Добавьте virtual функцию-член, которая выполняет реальную работу.

    void doStuff();
    
  3. Скажем, зарегистрированная функция обратного вызова:

    static void foo(Class& ref);
    

    В реализации foo вызовите doStuff на ref.

    static void foo(Class& ref)
    {
       ref.doStuff();
    }
    
  • 0
    На самом деле, если вы измените параметр на указатель, он фактически будет иметь ту же сигнатуру, что и обратный вызов свободной функции.
  • 0
    Я согласен, приведение к void (*) (void *) было просто для того, чтобы доказать, что остальная система работает. Поскольку это пользовательский API, я хочу избегать предупреждений компилятора. Однако, если бы была хитрость, чтобы заставить компилятор замолчать (без прагм и т. Д.), Я был бы счастлив с непереносимым решением. Виртуальным членом ClassA может быть любой класс. В конце мне нужно игнорировать информацию о типе и просто взять указатель на функцию.
0

Во-первых, нет никакой вещи, как универсальный указатель функции в C и C++.

Тем не менее, возможно полностью совместимое и высокопроизводительное решение.

Поскольку функция, не являющаяся членом, уже имеет правильный тип, она тривиальна.

  1. Если вы знаете полный набор не виртуальных функций-членов и слотов виртуальной функции, которые вы, возможно, захотите передать, определите для них простой форвардер в соответствии с строками:

    void forward_to_IsrHandler(void* p) {
        ((ClassA*)p)->IsrHandler();
    }
    gpio->RegisterIsr (forward_to_IsrHandler, (void*)this);
    
  2. Если вы не знаете или действительно хотите пожертвовать стандартным соответствием на алтаре производительности, вы можете просто преобразовать указатель на функцию-указатель на обычный указатель функции (расширение 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
  • 0
    1. Набор виртуальных функций обратного вызова не известен. 2. Поскольку вызывающим кодом будет код, скомпилированный пользователем (не связанный напрямую с ядром), я бы предпочел избегать одновременного отключения предупреждений о преобразовании, но, похоже, мне придется идти в этом направлении ...
  • 0
    @ an0n: Это отключает только одно предупреждение. Тем не менее, вам может быть лучше отключить его только для этой одной строки, добавил пример.
0

У вас будет намного меньше времени, если вы можете использовать либо 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, независимо от того, что один является функцией-членом, а другой - свободной функцией, нет необходимости в броске. Я должен признать, что я не могу записать фактический тип из памяти.

  • 0
    Кажется, это самый распространенный, переносимый подход. Однако это может быть тяжело для этой глубоко встроенной системы. Я понимаю, что при вызове boost :: bind происходит снижение производительности. В настоящее время перенаправление добавляет только 4 инструкции, которые, как я полагаю, составляют 6 тактов с очисткой конвейера после ветви. Минимизация добавленной задержки при прерывании является высоким приоритетом. Кроме того, мне нужно будет скомпилировать / добавить библиотеку надстройки и зависимости. Мой текущий общий объем ПЗУ составляет 256 КБ.
  • 0
    @ an0n Сколько инструкций было в коде с использованием bind и function ? Имейте в виду, что bind и friends являются компонентами только для заголовков.
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню