необработанный указатель на функцию из связанного метода - PullRequest
3 голосов
/ 05 февраля 2009

Мне нужно привязать метод к обратному вызову функции, за исключением того, что этот фрагмент недопустим, как обсуждено в указатель demote-boostfunction-to-a-plain-function-pointer .

Какой самый простой способ получить такое поведение?

struct C {
void m(int x) {
    (void) x;
    _asm int 3;
}};

typedef void (*cb_t)(int);
int main() {
    C c;
    boost::function<void (int x)> cb = boost::bind(&C::m, &c, _1);
    cb_t raw_cb = *cb.target<cb_t>(); //null dereference
    raw_cb(1);
    return 0;
}

Ответы [ 4 ]

2 голосов
/ 05 февраля 2009

Вы можете создать свой собственный класс, который будет выполнять ту же функцию, что и функция boost bind. Все, что должен сделать класс, это принять тип функции и указатель на объект, который содержит функцию. Например, это делегат void return и void param:

template<typename owner>
class VoidDelegate : public IDelegate
{
public:
   VoidDelegate(void (owner::*aFunc)(void), owner* aOwner)
   {
      mFunction = aFunc;
      mOwner = aOwner;
   }
   ~VoidDelegate(void)
   {}
   void Invoke(void)
   {
      if(mFunction != 0)
      {
         (mOwner->*mFunction)();
      }
   }

private:
   void (owner::*mFunction)(void);
   owner* mOwner;
};

Использование:

class C
{
   void CallMe(void)
   {
      std::cout << "called";
   }
};
int main(int aArgc, char** aArgv)
{
   C c;
   VoidDelegate<C> delegate(&C::CallMe, &c);
   delegate.Invoke();
}

Теперь, поскольку VoidDelegate<C> является типом, иметь коллекцию из них может быть непрактично, потому что, что, если список должен был бы также содержать функции класса B? Это не могло.

Это где полиморфизм вступает в игру. Вы можете создать интерфейс IDelegate, который имеет функцию Invoke:

class IDelegate
{
   virtual ~IDelegate(void) { }
   virtual void Invoke(void) = 0;
}

Если VoidDelegate<T> реализует IDelegate, вы можете иметь коллекцию IDelegate и, следовательно, иметь обратные вызовы для методов в различных типах классов.

2 голосов
/ 05 февраля 2009

Либо вы можете поместить этот связанный параметр в глобальную переменную и создать статическую функцию, которая может получить значение и вызвать его функцию, либо вам придется генерировать функции для каждого экземпляра на лету - это будет задействовать некоторый код на лету для генерации функции-заглушки в куче, в которой статическая локальная переменная установлена ​​на требуемое значение, а затем вызовет функцию для нее.

Первый способ прост и понятен, но совсем не ориентирован на многопотоковое или реентерабельное. Вторая версия грязная и сложная, но поточно-ориентированная и реентерабельная, если все сделано правильно.

Edit: я только что узнал, что ATL использует технику генерации кода, чтобы сделать именно это - они генерируют thunks на лету, которые устанавливают указатель this и другие данные, а затем переходят к функции обратного вызова. Вот статья CodeProject , которая объясняет, как это работает, и может дать вам представление о том, как сделать это самостоятельно. Особо посмотрите на последний пример (Программа 77).

Обратите внимание, что с момента написания статьи DEP вступил в действие, и вам нужно будет использовать VirtualAlloc с PAGE_EXECUTE_READWRITE, чтобы получить кусок памяти, в котором вы можете выделить свои блоки и выполнить их.

1 голос
/ 27 января 2010
#include <iostream>
typedef void(*callback_t)(int);

template< typename Class, void (Class::*Method_Pointer)(void) >
void wrapper( int class_pointer )
{
   Class * const self = (Class*)(void*)class_pointer;
   (self->*Method_Pointer)();
}

class A
{
public:
   int m_i;
   void callback( )
   { std::cout << "callback: " << m_i << std::endl; }
};

int main()
{
   A a = { 10 };
   callback_t cb = &wrapper<A,&A::callback>;
   cb( (int)(void*)&a);
}
0 голосов
/ 05 февраля 2009

у меня это работает прямо сейчас, превращая C в одноэлементный, переводя C :: m в C :: m_Impl и объявляя статический C :: m (int), который пересылается в экземпляр singleton. говорить о взломе.

...