Функции обратного вызова при использовании класса

Этот вопрос чем-то похож на вопрос Как передать нестатический член класса в обратный вызов?, но есть несколько отличий, тем более что многие мои варианты использования имеют «интересные» дополнительные сложности. Я хотел бы лучше понять свои варианты их использования, так как я не получил достаточного понимания из других ответов (и мне нужно написать как обратный вызов, так и функцию, которая его вызывает).

У меня есть несколько библиотек, которые создают "события", и я хотел бы иметь возможность писать отдельные классы или функции для реагирования на них (поэтому мне не нужно настраивать каждую библиотеку каждый раз, когда я что-то разрабатываю). Раздражает то, что я давно обнаружил указатель this, который спрятан в вызовах методов класса, что мешает мне передать currentgui.leftbutton как функцию в menu.leftbuttonpressed(void (*callbackfuncptr)(void)).

Я также использую библиотеку веб-сервера ESP8266, которая требует обратного вызова в контексте объекта сервера, который может вызывать сервер как глобальный для фактического получения данных, отправленных на него! (Если только сервер не передаст свой собственный this в качестве параметра обратного вызова, чего я не знаю, поскольку документация состоит почти исключительно из небольшое количество «примеров», которые не показывают большинство фактических функций веб-сервера, а не реальное объяснение.)

Я мог бы использовать какой-то вариант указателя на метод класса, но синтаксис для этого выдавливает глаза, и я использую более одного класса для обеспечения обратных вызовов (особенно для планировщика задач), поэтому это потребует настройка библиотеки для обработки указателей на каждый класс, который я снова использую. Я также не могу использовать полиморфизм, так как некоторые классы содержат несколько обратных вызовов планировщика, которые должны выполняться в разное время и при разных условиях.

Как мне обрабатывать эти обратные вызовы? Мой текущий метод заключается в написании функции, которая обертывает функцию метода, но опирается на конкретный экземпляр этого метода, поэтому его нельзя использовать повторно. Это также очень неудобно, так как теперь я имею дело с несколькими функциями/методами, которые теперь очень похожи.

Я бы также хотел бы иметь класс, который при передаче объекта веб-сервера регистрирует себя как обратный вызов для определенного события страницы (я хочу написать диспетчер сетевых подключений, аналогичный в библиотеку WiFiManager, но с возможностью интеграции с существующей веб-страницей. У меня есть весь код, но в настоящее время он не может быть хорошо интегрирован в автономный класс из-за этой проблемы). Он должен хранить указатель на объект сервера, чтобы получить данные после вызова обратного вызова, но он также должен быть присоединен к обратному вызову класса, который содержит этот конкретный указатель И другие функции обработчика после извлечения данных. Уже есть библиотека, которая делает что-то подобное, но я никак не могу понять, как это работает, так как у меня сложилось впечатление, что это невозможно. Может ли кто-нибудь помочь мне понять, как работает следующий класс? https://github.com/esp8266/Arduino/tree/master/libraries/ESP8266HTTPUpdateServer/src

, 👍1

Обсуждение

Не могли бы вы уточнить... если серверу требуется обратный вызов, а вы предоставляете обратный вызов, ожидаете ли вы получить доступ к переменным или функциям сервера? Если это так, ваш обратный вызов должен быть получен из класса сервера, не так ли? * Может ли кто-нибудь помочь мне понять, как работает следующий класс * - это довольно широкий вопрос. Беглый взгляд показывает, что он использует лямбда-функции и шаблоны, но помимо этого, я думаю, вам нужно сузить круг того, что вы хотите знать., @Nick Gammon

ESP8266HTTPUpdateServer регистрирует лямбда-функцию как обработчик server->on. лямбда-функция не является членом класса, @Juraj

Способ объектно-ориентированного программирования (ООП) для этого заключается в использовании «виртуальных» функций-членов и подклассов. Это позволяет получить доступ к данным участника. Обратный вызов потребует объект в качестве параметра, но не будет иметь доступа к закрытым или защищенным данным или функциям-членам., @Mikael Patel

@NickGammon, он использует обычные функции, а не производные от класса. Они вызывают один и тот же экземпляр функций-членов класса для возврата данных. Я хотел бы знать, как заставить класс, который я написал (ничего особенного в этом нет, но как его подключить?), чтобы зарегистрировать себя как обратный вызов, чтобы ИТ-специалисты получали данные, и мне не нужна куча отключенных неклассов функции вокруг., @RDragonrydr

@Juraj, я начинаю думать, что это может быть так. Можете ли вы опубликовать ответ, объясняющий больше и как именно они это сделали? Если бы функция была встроенной, но не частью класса, я думаю, это сработало бы... Но как они все еще получают доступ к членам класса в нем?, @RDragonrydr

@MikaelPatel Я сделал это для своей системы меню, но здесь это не совсем применимо? Я могу получить доступ к унаследованным членам только в том случае, если они существуют в родительском элементе, поэтому я не могу легко сделать более одной точки входа (не говоря уже о том, чтобы правильно назвать их все) для подклассов, и, как минимум, веб-сервер не будет принимать полиморфные дочерние классы. , так как вместо этого он просто принимает ванильные функции обратного вызова. Дружественные функции также представляют собой проблему., @RDragonrydr


2 ответа


Лучший ответ:

1

Веб-сервер ESP8266 использует функции обратного вызова для обработки HTTP-запросов вашим кодом. Эти функции обратного вызова не могут быть членами класса. Но вы можете использовать анонимные функции для реализации этих обратных вызовов. И если хотите, вы можете реализовать их как «перенаправления» на функции ваших объектов.

server.on("/leftbutton", []() {
  currentgui.leftbutton(server); 
}

Примечание: эти анонимные функции называются лямбда-функциями, и они также могут делать некоторые трюки с параметрами

,

Бонусные интернет-баллы за ссылку. Похоже, мне нужно выяснить, как работают эти предложения захвата, поскольку похоже, что в этом примере они используются... Спасибо!, @RDragonrydr

С тех пор я столкнулся с проблемами, делая это с моими собственными функциями, принимающими обратный вызов, поскольку казалось, что лямбда-выражения имеют тип, определенный компилятором. Как мне написать код обработки обратного вызова, который может принимать лямбду?, @RDragonrydr


0

Они вызывают один и тот же экземпляр функций-членов класса для возврата данных.

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

Я хотел бы знать, как заставить класс, который я написал (ничего особенного в этом нет, но как его подключить?), зарегистрировать себя в качестве обратного вызова, чтобы ИТ-отдел получал данные, а мне не нужна была куча отключенных внеклассовые функции вокруг

Если функция X (не член этого класса) должна иметь доступ к данным из экземпляра A или экземпляра B, то, очевидно, X необходимо знать, какой экземпляр он просматривает (this указатель). Это не проблема синтаксиса, у вас должен быть способ решить эту проблему.

На самом деле, когда вы говорите «класс, который я написал», тогда у него также будут данные экземпляра (его собственный this), так что теперь вы пытаетесь соединить два экземпляра двух классов.

Сколько у вас серверных объектов? Если в этом конкретном случае только один, вы можете обойти его, сохранив его this где-нибудь или создав несколько связующих процедур.

,

Да, но если они в конечном итоге вызывают функцию-член класса для получения данных, отличных от статических данных, тогда необходимо знать, из какого экземпляра класса получить данные. -- да, это проблема. Мне нужно сохранить this для каждого экземпляра МОЕГО класса, но также подключить член моего класса к его обратному вызову, поэтому обратный вызов получает мой экземпляр, который может получить доступ к ЕГО экземпляру! Соединение двух экземпляров двух классов — да, именно так. Должен быть способ сделать это, верно? Кто-то сказал что-то об использовании лямбда-функций в существующем примере, но я не понимаю, что они сделали., @RDragonrydr

Лямбда-функция — это просто анонимная функция. То есть вы можете поместить лямбда-функцию «встроенную» вместо того, чтобы называть функцию, объявленную в другом месте. В остальном они не помогают решить эту проблему. Я думаю, вам нужно составить пример и отредактировать его в своем вопросе. И, честно говоря, я думаю, что это чисто проблема C++, которую лучше задать на [Stack Overflow](https://stackoverflow.com/questions). Тот факт, что это Arduino, не имеет отношения к тому, что вы пытаетесь сделать с классами и экземплярами классов., @Nick Gammon

Это может быть справедливым замечанием. Однако я не был уверен, может ли быть какой-то дополнительный метод, который Arduino использовал или реализовал, который мог бы упростить эту задачу., @RDragonrydr

@RDragonrydr Arduino просто использует g++ - стандартный компилятор (или, во всяком случае, один из них). Нет специальной функциональности «Arduino», к которой вы можете обратиться., @Nick Gammon

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