Страницы

Поиск по вопросам

воскресенье, 7 июля 2019 г.

Callback метода класса из другого класса без использования static, или с использованием только одного static объекта в С++ 0х14

Всем доброго времени суток, столкнулся с такой проблемой.
Есть чисто абстрактный класс UART который имеет следующий набор виртуальных методов :
virtual int8_t initialize(char* port_name, int baud_rate, Parity parity, int dataBits, StopBits stop_bits) = 0; virtual int8_t EDataReceived(void(*delegate)(char* data, const int16_t & length)) = 0; virtual void transmitDataToUart(char* data, const int16_t & length) = 0;
и есть 5 различных платформ на которых есть своя реализация класса, но все они унаследованы от класса UART, например class UART_Linux: UART. Это нужно для того что бы единожды написанный драйвер под микросхему SIM666 или SIM777, принимая в качестве параметра конкретный объект наследника UART мог бы работать на всех пяти платформах без изменений в коде. Когда на аппаратный uart приходят данные то класс UART должен делать колбэк вызов и передавать принятые данные с помощью функции, которая регистрируется в методе
virtual int8_t EDataReceived(void(*delegate)(char* data, const int16_t & length)).
Эта регистрация происходит внутри конструктора классов SIM666 и SIM777, например:
Sim666::Sim666(Uart& in_uart){ in_uart.EDataReceived(ReceiveDataFromUART); }
сам класс UART_Linux: UART не должен не чего знать о классе SIM666 т. к. это однажды написанный универсальный класс а SIM666 будет написан допустим спустя 5 лет после этого. Проблема заключается в том что метод ReceiveDataFromUART который является частью SIM666 должен обязательно быть статическим. Статическим его сделать не получается т. к. внутри этого метода должны использоваться не статик поля и свойства класса (т. е. конкретные экземпляры объекта).
Что бы решить мою проблему можно решить хотя бы один из трёх вопросов:
1) Реализовать возможность вызова внутри UART_Linux не статик стороннего метода.
2) Сделать метод Sim666.ReceiveDataFromUART статическим но реализовать в нём возможность обращения к не статическим полям класса (т. е. на уровне объекта).
3) Можно создать статический объект класса SIM666, тогда если регистрировать метода происходит снаружи класса SIM666 то всё работает, но внутри метода
void getPeriphery(const sd::Uart& in_uart) { in_uart.EDataReceived(ReceiveDataFromUART); };
компилятор не понимает что объект сейчас статический и данный вызов является корректным. Можно ли как то сообщить об этом компилятору? Что то типа как привести ReceiveDataFromUART к статику?
Стандарт языка С++ 0х14 , компиляторы и платформы как вы понимаете различны. Из стандартных библиотек можно использовать только стандартную библиотеку С.


Ответ

Если в Вашем случае использовать std::function, то это может покрыть все необходимые варианты использования
#include #include
typedef std::function Callback;
class MyClass { public: MyClass(Callback _cb): //регистрация колбэк функции в конструкторе m_callback(_cb) {
}
void invokeSomeFunc() { //много каких-то действий //... m_callback(0, 1); }
private: std::function m_callback; };
void func1(char* data, const int & length) { std::cout << __FUNCSIG__ << std::endl; }
class AnotherClass { public: static void staticFunc(char* data, const int & length) { std::cout << __FUNCSIG__ << std::endl; } };
class AnotherClass2 { public: void notStaticFunc(char* data, const int & length) { std::cout << __FUNCSIG__ << std::endl; } };
int main(int argc, char *argv[]) { //вариант использования с свободной функцией { MyClass someObj(func1); someObj.invokeSomeFunc(); }
//вариант использования с статической функцией класса { MyClass someObj(&AnotherClass::staticFunc); someObj.invokeSomeFunc(); }
//вариант использования с не статической функцией класса //тут нужно понимать, что объект someAnotherObj не должен быть разрушен на момент // вызова колбек функции, если invokeSomeFunc - асинхронная, то можно создать объект в куче. { AnotherClass2 someAnotherObj;
auto fn = [&someAnotherObj](char* data, const int & length) { someAnotherObj.notStaticFunc(data, length); };
MyClass someObj(fn); someObj.invokeSomeFunc(); }
{ AnotherClass2 *someAnotherObj = new AnotherClass2();
auto fn = [someAnotherObj](char* data, const int & length) { someAnotherObj->notStaticFunc(data, length); delete someAnotherObj; };
MyClass someObj(fn); someObj.invokeSomeFunc(); }
return 0; }
Если не хочется использовать std::function, можно в колбэк функцию добавить еще один параметр типа void* для пользовательских данных:
#include #include
typedef void(*Callback)(char* data, const int & length, void *_userData);
class MyClass { public: MyClass(Callback _cb, void *_userData = 0): m_callback(_cb), m_userData(_userData) {
}
void invokeSomeFunc() { //много каких-то действий //... m_callback(0, 1, m_userData); }
private: Callback m_callback; void *m_userData; };
void func1(char* data, const int & length, void *userData) { std::cout << __FUNCSIG__ << std::endl; }
class AnotherClass { public: static void staticFunc(char* data, const int & length, void *userData) { std::cout << __FUNCSIG__ << std::endl; } };
class AnotherClass2 { public: void notStaticFunc(char* data, const int & length) { std::cout << __FUNCSIG__ << std::endl; } };
void funcToCallObjectMethod(char* data, const int & length, void *userData) { AnotherClass2 *obj = static_cast(userData); obj->notStaticFunc(data, length); delete obj; }
int main(int argc, char *argv[]) { //вариант использования с свободной функцией { MyClass someObj(func1); someObj.invokeSomeFunc(); }
//вариант использования с статической функцией класса { MyClass someObj(&AnotherClass::staticFunc); someObj.invokeSomeFunc(); }
//вариант использования с не статической функцией класса //тут нужно понимать, что объект someAnotherObj не должен быть разрушен на момент // вызова колбек функции, если invokeSomeFunc - асинхронная, то можно создать объект в куче. { AnotherClass2 someAnotherObj;
auto fn = [](char* data, const int & length, void *userData) { AnotherClass2 *obj = static_cast(userData); obj->notStaticFunc(data, length); };
MyClass someObj(fn, &someAnotherObj); someObj.invokeSomeFunc(); }
{ AnotherClass2 *someAnotherObj = new AnotherClass2();
auto fn = [](char* data, const int & length, void *userData) { AnotherClass2 *obj = static_cast(userData); obj->notStaticFunc(data, length); delete obj; };
MyClass someObj(fn, someAnotherObj); someObj.invokeSomeFunc(); }
{ AnotherClass2 *someAnotherObj = new AnotherClass2();
MyClass someObj(funcToCallObjectMethod, someAnotherObj); someObj.invokeSomeFunc(); }
return 0; }
Как третий вариант, можно использовать реализацию паттерна Observer, пример уже приводить не буду.
IMHO, первый вариант самый удобный и экономит время в разработке.

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

Отправить комментарий