C : закрытие для передачи функции-члена как обычного указателя функции

#c #arduino #atmega

#c #arduino #atmega

Вопрос:

Я пытаюсь вызвать функцию-член внешней библиотеки, которая принимает указатель на функцию в качестве параметра:

 Timer::every(unsigned long period, void (*callback)(void));
  

Но, к сожалению, параметр, который я хочу передать, является функцией-членом:

 void MyClass::the_method_i_want_to_pass(void);
  

Поскольку я программирую для ATmega под Arduino (AVR), поддержка c 11 ограничена. Мой первый подход вызывает ошибку типа:

 void MyClass::the_method_i_want_to_pass() {...}

MyClass::MyClass() {
    // constructor

    Timer *timer = new Timer();
    timer->every(500, [this](){this->the_method_i_want_to_pass();})
}
  

Вывод компилятора:

предупреждение: предупреждение: лямбда-выражения доступны только с -std= c 11 или -std= gnu 11 [включено по умолчанию]

ошибка: нет соответствующей функции для вызова ‘Timer:: every (int, MyClass::MyClass()::__lambda0)’

  1. Существуют ли другие / лучшие решения?
  2. Что касается моего текущего подхода: (Как) можно ли передать ссылку на лямбда-выражение, когда требуется указатель на функцию?
  3. Как я могу узнать, поддерживает ли Arduino / AVR эти лямбды (см. «Предупреждение»)?

Комментарии:

1. Поддерживает ли ваш компилятор std::mem_fn , хотя его c 11

2. Используя библиотеку StandardCplusplus, есть порт, который кажется похожим.

3. Этот вопрос не имеет смысла. Это все равно, что сказать: «Моя жена хочет, чтобы я принес домой помидоры. У меня есть коробка с гвоздями и буханка хлеба. 1. Есть ли какие-либо лучшие решения? 2. Как я могу угодить своей жене? 3. Как я могу найти жену, которая принимает гвозди и хлеб как помидоры? »

4. Я не понимаю вашей точки зрения @KerrekSB. На мой взгляд, это четко определенный вопрос об ограничении сторонней библиотеки.

5. Именно для этого и предназначено закрытие… инкапсуляция области / состояния в функцию. Каким будет ваше предложение?

Ответ №1:

Ваша основная проблема в том, что ваша Timer библиотека написана плохо: она должна занимать void(*)(void*), void* как минимум.

Без pvoid или эквивалента вы не можете передать какое-либо состояние, отличное от адреса в коде выполнения, для запуска процедуры. Поскольку метод также повторно this использует указатель, вам не повезло.

Теперь, если ваш экземпляр MyClass является одноэлементным, вы можете получить this его откуда-нибудь еще.

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

Это все хак. То, что следует, хуже.

Напишите динамическую библиотеку с некоторым глобальным состоянием и void() интерфейсом. Когда вы добавляете обратный вызов, дублируйте эту динамическую библиотеку, измените ее глобальное состояние во время выполнения, запишите ее как библиотеку с другим именем, загрузите ее и передайте чистую функцию обратного вызова вашему Timer классу.

Или выполните эквивалентное значение без библиотеки, вручную написав машинный код и пометив страницы как выполнимые.

Все это плохие решения. Что приводит меня к хорошему: найдите лучшее Timer . Если они испортили что-то настолько простое, остальная часть библиотеки, вероятно, тоже плоха.

Комментарии:

1. спасибо за ваш ответ! думаю, я начну с взлома, пока не смогу заменить его чем-то лучшим.