Проблема заключается в следующем.
Есть функция, вот её прототип.
void attachInterrupt(uint8_t interruptNum, void (*userFunc)(void), int mode);
есть так же класс
class Foo
{
..
void ololo();
..
}
в его конструкторе я пытаюсь вызвать функцию attachInterrupt следующим образом
attachInterrupt(0, this->ololo, FALLING);
на что получаю ответ
error: cannot convert ‘Foo::ololo’ from type ‘void* (Foo::)()’ to type ‘void (*)()’
Понимаю, что вся проблема скорее всего в том, что надо правильно преобразовать типы, но не могу допереть как именно. Если вызывать attachInterrupt не из класса и не для члена класса, то всё работает. Подскажите пожалуйста, как правильно вызвать?
Пните пожалуйста в нужном направлении, где я смогу раз и навсегда разобраться в этой теме.
Ололо это инстанс-метод, так что у него есть неявный параметр this, а также у него аби может быть thiscall виесто cdecl, так что под описанный прототип он не прет. Делай прокси-функцию, и как-то надо ей передать нужный this. Или делай ололо класс-методом, но я в крестах не силен, хз прокатит-нет.
Любой не-статический метод класса неявно получает первым аргументом this. Вы должны это как то обеспечить (скажем указать в типе указателя на ф-ю что она получает this, но этот this надо еще как то передать туда при вызове).
Можно написать обертку, которая будет вызывать соотв. функцию подсовывая this из какой нить глобально переменной, но это жуткий костыль.
Вызов функции ololo (которая, вообще говоря, зависит от внутреннего состояния объекта, раз уж это функция-член класса) происходит во время, когда объект, обладающий этим методом, еще не «построен».
Если эта функция не использует поля конкретного объекта этого класса, то ее можно объявить static, как уже написали выше - но в этом случае в ней нельзя использовать this, который неявно передается в не-static методы класса.
То есть другими словами: если функции ololo() необходимы поля конкретного экземпляра класса, как вы себе представляете вызов ololo() извне этого класса? Вот я процессор, поступило прерывание и мне нужно передать управление в ololo(). Что должно исполнять роль аргумента this?
Конструктор такой же метод как и все остальные. После первой { память под объект выделена, this приходит неявно через аргументы конструктора как и в других методах.
Передавать this, очевидно же. Если надо передать коллбек с состоянием в сишный код, то забрасывают указатель на функцию типа void (*)(void*) где та void* — это юзерский контекст, который внутри коллбека кастуется к чему надо. Можно передать this так. Если там можно передавать плюсовые объекты, а указатель на функцию стоит просто из-за незнания чего бы-то ещё, то тут уже насоветовали всяких std::function.
Не совсем понял, причем тут конструктор... WRG , судя по коду, хочет повесить метод класса на прерывание. Вот идет исполнение кода в другом месте, где про класс Foo никто слыхом не слыхивал. И тут вдруг надо выполнить функцию Foo::ololo(), которая не static, и стало быть требует аргументом некий Foo *this. Откуда этот this возьмется и что будет в this->some_int_field? В том виде, как это написано, взять его неоткуда, о чем компилятор и сообщил.
А написал я это к тому, чтобы WRG задумался, что же ему нужно в этом коде сделать на самом деле.
вряд ли оставит хоть каплю желания разобраться в том, что же происходит, у человека, который не понимает почему в данном случае ‘Foo::ololo’ имеет тип ‘void* (Foo::)()’ и чем это отличается от ‘void (*)()’. (:
Трамплины. В gcc (нестандартное расширение) так сделаны вложенные функции в C, которые работают как замыкания.
Пишут, что в C++14/17 возможно добавят каст из экземпляров типов с operator() в указатель на функцию, как раз для подобных случаев, реализовать это скорее всего можно только (ну, или по крайне мере проще всего) через трамплины.
Да, исполняемый стек, небезопасно и вообще костыли и ужас (не сарказм).
Пипец вы тут за ночь демагогию развели... Прокси функция, глобальный Foo*, все. Прототип аттача не подразумевает другого использования и/или идентификации события. Че за феерическое небыдло в треде?
Почитал ваши ответы, вроде понял суть проблемы. Хотел сделать красиво, как у людей, получилось как всегда. Какой из вариантов предпочтительнее - прокси функция или переопределить attachInterrupt? Не будет ли это выглядеть как уг?
Если в attachInterrupt тоже твой код, его лучше изменить на (int, void (*cb)(void *ud), void *ud, int), чтобы можно было ud в cb передавать, оно и будет твой this.
Заводи thread local указатель на текущий объект(глобальный объект не тредобезопасен). По другому можно делать только на фишках отдельного компилятора, вроде локальных функций в gcc.
Так прямо и не нужны? Ну вот собирает человек таблицу векторов прерываний которая как правило выглядит как массив указателей на функцию-колбек. Теперь вопрос, как ты планируешь хранить сами колбеки если половина из них функции, а половина вызов метода?
Ему вроде нельзя исходную функцию переделывать. Так что или глобальный указатель или thread-local указатель или же какие-то компиляторозависимые плюшки уже.
и да, attachInterrupt не мой, стандартная адруиновская библиотека.
Кто же пользуется на микроконтроллерах стандартными библиотеками? Помилуйте, этого совершенно нельзя делать. А если серьёзно, то вероятнее всего дёргать свой колбек ты будешь из одного конкретного объекта. Сделай этот объект синглетоном. Вокруг вызова указателя на синглетон и вызова метода оберни функцию, указатель на функцию-обёртку передавай в attachInterrupt. Всё.
У тс проблема в том, что он не знает чем отличается указатель на методы класса от указателя на ф-ю. Собственно и ответ на вопрос тс такой: никак (без костылей). Либо делать массив указателей на ф-ии, либо связаные структуры. Но я даже бояюсь представить нафига нужен связный список указателей на обработчики прерываний. Да и вообще, микроконтроллеры и с++ это какой-то упоротый постмодернистский сюрреализм кмк.