Вызов метода из сохраненной ссылки на класс

#c #templates #callback #c 17 #std-function

Вопрос:

Я работаю над классом шаблона обратного вызова, в котором обратный вызов является не функцией или функтором, а ссылкой на класс. В зависимости от контекста обратный вызов вызывается путем вызова определенного метода из зарегистрированного класса, примерно реализованного следующим образом:

 template<typename T>
class callback
{
public:
   // ctors, moves, copies, assignments, ...

   template<typename F, typename ... A>
   void invoke(F method_ptr, A ... args)
   {
      (*m_interface_ptr.*method_ptr)(std::forward<A>(args) ...);
   }

private:
   T * m_interface_ptr;
};
 

Теперь использование такого шаблона выглядит примерно так:

 struct intf
{
   virtual void method_a() { ... }
   virtual void method_b(int n) { ... }
};

intf i;

callback<intf> c;
c.subscribe(i);
c.invoke(amp;intf::method_a);
c.invoke(amp;intf::method_b, 7);
 

Этот код работает просто отлично, но та часть, где я вызываю метод из ссылки на класс, кажется немного странной. Проблемы возникают, если я пытаюсь расширить callback класс для поддержки как обратных вызовов ссылок на классы, так и обратных вызовов функторов или простых функций.

Существует ли стандартный способ хранения ссылки на класс и вызова методов на нем? Что-то вроде std::function , но поддерживающее мое дело.


Пример использования #1: Вызов метода из сохраненной ссылки на класс

 intf i;
// Create some std:: reference wrapper which allows custom method calls by name
std::some_ref_callable ri(i);
// Pass into callback and invoke
callback<std::some_callable<intf>> ci(ri);
ci.invoke(amp;intf::method_b, 7); 
 

Пример использования № 2: Вызов лямбды

 auto l = []{ /* generic lambda */ };
callback<decltype(l)> cf(l);
cf.invoke();
 

Пример использования #3: Вызов простой функции

 void func(int n){ /* plain function with param */ }
callback<decltype(func)> cf(func);
cf.invoke(1337);
 

Возможная пользовательская реализация будет выглядеть примерно так:

 template<typename T>
struct some_ref_callable
{
    template<typename R, typename ... A>
    auto operator ()(R(T:: * method_ptr)(A ...), A ... args)
    {
        return (*m_object_ptr.*method_ptr)(std::forward<A>(args) ...);
    }
 
    T * m_object_ptr = nullptr;
};
 

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

1. С чем callback_intf и как это связано, если вообще связано intf ?

2. Похоже , вам, возможно , захочется посмотреть std::invoke , но трудно сказать с таким неполным примером.

3. Что вы подразумеваете под «обратными вызовами функтора или простой функции»? Не могли бы вы показать примеры всех вариантов использования, которые вы хотите использовать?

4. @IgorTandetnik Извините, я изменил имя и забыл отредактировать остальную часть примера кода. Исправлю вопрос, также с примерами, которые вы просили.

5. Вы могли бы обеспечить две перегрузки для invoke . Что-то вроде template<typename R, typename... Params, typename ... A> void invoke(R (T::*method_ptr)(Params...), A ... args) плюс обычная ваниль template <typename... Args> void invoke(Args...);