Как хранить и вызывать функции по thiscall?
Я делаю систему регистрации колбэков от аппаратных устройств и перевожу их на c++. Есть библиотечная функция со слабым связыванием в c-файле, которая вызывается при наступлении какого-то аппаратного события. Я её в своём файле переопределяю, и перенаправляю вызов в метод некоторого класса.
То есть имеется вызываемая функция (Callable), и объект класса, которому эта функция принадлежит (Object). Эти две сущности надо как-то хранить с тем, чтобы потом можно было вызвать.
Варианты с использованием std::function и подобных типов не подходят, так как они выделяют память в куче. Вот что у меня получилось:
template<class Callable, class Object>
struct Callback {
Callback(Callable f, Object o) :
func(f),
object(o)
{}
Callable func;
Object object;
};
class CallbackRegistry {
public:
template<class Callable, class Object>
static void setCallback(Object object, Callable func)
{
Callback1<Callable, Object> = Callback<Callable, Object>(func, object);
}
//Здесь надо хранить объект this и функцию. Возможно, надо уйти от шаблонности.
template<class Callable, class Object>
static Callback<Callable, Object> Callback1;
};
template<class Callable, class Object>
Callback<Callable, Object> CallbackRegistry::Callback1 = Callback<Callable, Object>(nullptr, nullptr);
//Эта функция вызывается аппаратно. В библиотечном файле она объявлена со слабым связыванием.
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
//Как определить эти типы? Если уходить от шаблонности переменной Callback1, то их здесь не будет
std::invoke(CallbackRegistry::Callback1<?, ?>.func,
CallbackRegistry::Callback1<?, ?>.object /* this */);
}
CallbackRegistry::setCallback(someObject, comeMethodOfObject);
Проблема в том, что для реализации нужного поведения надо как-то избавиться от шаблонности в переменной Callback1. Где-то отдельно хранить тип? Но как - я пока не понимаю.
Ответы (1 шт):
Храните в callback-е данные конкретного типа, а не шаблонные. Можно выделить специальный интерфейс для этого.
struct ICallable
{
virtual void operator() (void) const =0;
};
// std::function<void(void)> - своими руками, без выделения памятию.
template<class Lambda>
struct Callable : ICallable {
Lambda lambda;
Callable(Lambda lambda) : lambda(lambda) {}
void operator() (void) const { lambda(); }
};
class CallbackRegistry {
public:
// Сделаем универсально, для любого invorkable набора аргументов.
template<class... Args>
static void setCallback(Args...args)
{
auto binded=[=](){ std::invoke(args...); }; // сворачиваем набор аргументов в один.
static Callable<decltype(binded)> instance( binded ); // создаем экземпляр Callable.
assert(callback==nullptr);
callback=&instance;
}
static ICallable* callback;
};
ICallable* CallbackRegistry::callback = nullptr;
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(CallbackRegistry::callback!=nullptr)
(*CallbackRegistry::callback)();
}
struct Test
{
void foo() {}
} test;
CallbackRegistry::setCallback( &Test::foo, & test );