Обратный вызов в C ++, член шаблона?
-
18-09-2019 - |
Вопрос
Следующий код НЕ работает, но он хорошо выражает то, что я хочу сделать.Существует проблема с контейнером template struct, который, я думаю, ДОЛЖЕН работать, потому что его размер известен для любого аргумента шаблона.
class callback {
public:
// constructs a callback to a method in the context of a given object
template<class C>
callback(C& object, void (C::*method)())
: ptr.o(object), ptr.m(method) {}
// calls the method
void operator()() {
(&ptr.o ->* ptr.m) ();
}
private:
// container for the pointer to method
template<class C>
struct {
C& o;
void (C::*m)();
} ptr;
};
Есть ли какой-нибудь способ сделать такую вещь?Я имею в виду обратный вызов не шаблонного класса, который переносит любой указатель на метод?
Спасибо гуру C ++!
Редактировать:
Пожалуйста, посмотрите на это:
Решение 7
Пожалуйста, посмотрите на это
Другие советы
Это полный рабочий пример, который делает то, что, я думаю, вы пытаетесь сделать:
#include <iostream>
#include <memory>
// INTERNAL CLASSES
class CallbackSpecBase
{
public:
virtual ~CallbackSpecBase() {}
virtual void operator()() const = 0;
};
template<class C>
class CallbackSpec : public CallbackSpecBase
{
public:
CallbackSpec(C& o, void (C::*m)()) : obj(o), method(m) {}
void operator()() const { (&obj->*method)(); }
private:
C& obj;
void (C::*method)();
};
// PUBLIC API
class Callback
{
public:
Callback() {}
void operator()() { (*spec)(); }
template<class C>
void set(C& o, void (C::*m)()) { spec.reset(new CallbackSpec<C>(o, m)); }
private:
std::auto_ptr<CallbackSpecBase> spec;
};
// TEST CODE
class Test
{
public:
void foo() { std::cout << "Working" << std::endl; }
void bar() { std::cout << "Like a charm" << std::endl; }
};
int main()
{
Test t;
Callback c;
c.set(t, &Test::foo);
c();
c.set(t, &Test::bar);
c();
}
Недавно я реализовал это:
#define UNKOWN_ITEM 0xFFFFFFFF
template <typename TArg>
class DelegateI
{
public:
virtual void operator()(TArg& a)=0;
virtual bool equals(DelegateI<TArg>* d)=0;
};
template <class TArg>
class Event
{
public:
Event()
{
}
~Event()
{
for (size_t x=0; x<m_vDelegates.size(); x++)
delete m_vDelegates[x];
}
void operator()(TArg& a)
{
for (size_t x=0; x<m_vDelegates.size(); x++)
{
m_vDelegates[x]->operator()(a);
}
}
void operator+=(DelegateI<TArg>* d)
{
if (findInfo(d) != UNKOWN_ITEM)
{
delete d;
return;
}
m_vDelegates.push_back(d);
}
void operator-=(DelegateI<TArg>* d)
{
uint32 index = findInfo(d);
delete d;
if (index == UNKOWN_ITEM)
return;
m_vDelegates.erase(m_vDelegates.begin()+index);
}
protected:
int findInfo(DelegateI<TArg>* d)
{
for (size_t x=0; x<m_vDelegates.size(); x++)
{
if (m_vDelegates[x]->equals(d))
return (int)x;
}
return UNKOWN_ITEM;
}
private:
std::vector<DelegateI<TArg>*> m_vDelegates;
};
template <class TObj, typename TArg>
class ObjDelegate : public DelegateI<TArg>
{
public:
typedef void (TObj::*TFunct)(TArg&);
ObjDelegate(TObj* t, TFunct f)
{
m_pObj = t;
m_pFunct = f;
}
virtual bool equals(DelegateI<TArg>* di)
{
ObjDelegate<TObj,TArg> *d = dynamic_cast<ObjDelegate<TObj,TArg>*>(di);
if (!d)
return false;
return ((m_pObj == d->m_pObj) && (m_pFunct == d->m_pFunct));
}
virtual void operator()(TArg& a)
{
if (m_pObj && m_pFunct)
{
(*m_pObj.*m_pFunct)(a);
}
}
TFunct m_pFunct; // pointer to member function
TObj* m_pObj; // pointer to object
};
template <typename TArg>
class FunctDelegate : public DelegateI<TArg>
{
public:
typedef void (*TFunct)(TArg&);
FunctDelegate(TFunct f)
{
m_pFunct = f;
}
virtual bool equals(DelegateI<TArg>* di)
{
FunctDelegate<TArg> *d = dynamic_cast<FunctDelegate<TArg>*>(di);
if (!d)
return false;
return (m_pFunct == d->m_pFunct);
}
virtual void operator()(TArg& a)
{
if (m_pFunct)
{
(*m_pFunct)(a);
}
}
TFunct m_pFunct; // pointer to member function
};
template <typename TArg>
class ProxieDelegate : public DelegateI<TArg>
{
public:
ProxieDelegate(Event<TArg>* e)
{
m_pEvent = e;
}
virtual bool equals(DelegateI<TArg>* di)
{
ProxieDelegate<TArg> *d = dynamic_cast<ProxieDelegate<TArg>*>(di);
if (!d)
return false;
return (m_pEvent == d->m_pEvent);
}
virtual void operator()(TArg& a)
{
if (m_pEvent)
{
(*m_pEvent)(a);
}
}
Event<TArg>* m_pEvent; // pointer to member function
};
template <class TObj, class TArg>
DelegateI<TArg>* delegate(TObj* pObj, void (TObj::*NotifyMethod)(TArg&))
{
return new ObjDelegate<TObj, TArg>(pObj, NotifyMethod);
}
template <class TArg>
DelegateI<TArg>* delegate(void (*NotifyMethod)(TArg&))
{
return new FunctDelegate<TArg>(NotifyMethod);
}
template <class TArg>
DelegateI<TArg>* delegate(Event<TArg>* e)
{
return new ProxieDelegate<TArg>(e);
}
используйте это вот так:
определить:
Event<SomeClass> someEvent;
привлекать обратные вызовы:
someEvent += delegate(&someFunction);
someEvent += delegate(classPtr, &class::classFunction);
someEvent += delegate(&someOtherEvent);
триггер:
someEvent(someClassObj);
Вы также можете создать своих собственных делегатов и переопределять то, что они делают.Я сделал пару других, в одном из которых можно было убедиться, что событие запускает функцию в потоке gui вместо потока, в котором оно было вызвано.
Вам нужно использовать полиморфизм.Используйте абстрактный базовый класс с виртуальным методом вызова (operator()
если вам угодно), с шаблонным потомком, который реализует виртуальный метод, используя правильную сигнатуру типа.
В том виде, в каком вы имеете это сейчас, данные, содержащие тип, являются шаблонными, но код, предназначенный для вызова метода и передачи объекта, - нет.Это не сработает;параметры типа шаблона должны проходить как через построение, так и через вызов.
@Барри Келли
#include <iostream>
class callback {
public:
virtual void operator()() {};
};
template<class C>
class callback_specialization : public callback {
public:
callback_specialization(C& object, void (C::*method)())
: o(object), m(method) {}
void operator()() {
(&o ->* m) ();
}
private:
C& o;
void (C::*m)();
};
class X {
public:
void y() { std::cout << "ok\n"; }
};
int main() {
X x;
callback c(callback_specialization<X>(x, &X::y));
c();
return 0;
}
Я попробовал это, но это не сработало (выведите "ok")...почему?
Редактировать:
Как упоминал Нил Баттеруорт, полиморфизм работает через указатели и ссылки,
X x;
callback& c = callback_specialization<X>(x, &X::y);
c();
Редактировать: С помощью этого кода я получаю сообщение об ошибке:
invalid initialization of non-const reference of type ‘callback&’
from a temporary of type ‘callback_specialization<X>’
Теперь я не понимаю этой ошибки, но если я заменю обратный вызов и c с постоянный обратный вызов и c и оператор виртуальной пустоты()() с оператор виртуальной пустоты()() const, это работает.
Вы не сказали, какие ошибки вы обнаружили, но я обнаружил, что это сработало:
template<typename C>
class callback {
public:
// constructs a callback to a method in the context of a given object
callback(C& object, void (C::*method)())
: ptr(object,method) {}
// calls the method
void operator()() {
(&ptr.o ->* ptr.m) ();
}
private:
// container for the pointer to method
// template<class C>
struct Ptr{
Ptr(C& object, void (C::*method)()): o(object), m(method) {}
C& o;
void (C::*m)();
} ptr;
};
Обратите внимание, что Ptr нуждается в конструкторе, поскольку у него есть ссылочный элемент.
Вы могли бы обойтись без struct Ptr и иметь необработанные элементы.
Протестировано с помощью VS2008 express.
Улучшение ответа OP:
int main() {
X x;
callback_specialization<X> c(x, &X::y);
callback& ref(c);
c();
return 0;
}
При этом выводится "ok".
Протестировано на VS2008 express.