Лучший метод, чтобы функция-член C ++ была вызвана обратным вызовом C? - PullRequest
4 голосов
/ 26 сентября 2008

Учитывая типичный класс:

struct Whatever
{
    void Doit();
};

Whatever w;

Каков наилучший способ получить функцию-член для вызова с помощью обратного вызова на основе C void *, такого как pthread_create () или обработчик сигнала?

pthread_t pid;

pthread_create(&pid, 0, ... &w.Doit() ... );

Ответы [ 9 ]

6 голосов
/ 26 сентября 2008

Самое краткое решение - определить в заголовочном файле, который используется всеми вашими кодами:

template <typename T, void (T::*M)()>
void* thunk(
    void* p)
{
    T* pt = static_cast<T*>(p);

    (pt->*M)();

    return 0;
}

Возможно, вы захотите определить 4 версии: по одной, в которой thunk возвращает void и void *, и по одной, где функция-член возвращает void и void *. Таким образом, компилятор может соответствовать лучшему, в зависимости от обстоятельств (и на самом деле он будет жаловаться, если все не совпадает.)

Тогда все, что вам нужно вводить каждый раз, когда вы сталкиваетесь с одной из следующих ситуаций:

pthread_create (& pid, 0, & thunk <Все, что угодно :: doit>, & w);

Это будет работать даже когда метод закрыт, если на метод ссылаются из кода класса. (Если нет, я должен задаться вопросом, почему код ссылается на закрытый метод.)

6 голосов
/ 26 сентября 2008

Большинство обратных вызовов C позволяют указать аргумент, например,

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                   void *(*start_routine)(void*), void *arg);

Так что вы могли бы

void myclass_doit(void* x)
{
  MyClass* c = reinterpret_cast<MyClass*>(x);
  c->doit();
}

pthread_create(..., &myclass_doit, (void*)(&obj));
3 голосов
/ 26 сентября 2008

Является ли функция-член приватной? Если нет, используйте стандартную идиому:

void* pthread_foo_caller(void* arg) {
    Foo* foo = static_cast<Foo*>(arg);
    foo->bar();
    return NULL;
}

Если функция-член является закрытой, вы можете объявить статический метод в классе, который принимает указатель "this" и вызывает соответствующий метод. Например:

class Foo {
 public:
  static pthread_foo_caller(void* arg);
  ...
};

void* Foo::pthread_foo_caller(void* arg) {
    Foo* foo = static_cast<Foo*>(arg);
    foo->private_bar();
    return NULL;
}
3 голосов
/ 26 сентября 2008

Используйте оболочку C-функции следующим образом:

struct Whatever
{
    void Doit();
};

extern "C" static int DoItcallback (void * arg)
{
  Whatever * w = (Whatever *) arg;
  w->DoIt();
  return something;
}

Работает, только если вы можете как-то передать указатель на класс. Большинство механизмов обратного вызова позволяют это.

Афаик, это единственный способ сделать это. Вы не можете напрямую вызвать метод из C без большого взлома.

1 голос
/ 27 сентября 2008

Одна вещь, о которой вы должны знать, это то, что если вы напишите такой код:

try {
    CallIntoCFunctionThatCallsMeBack((void *)this, fCallTheDoItFunction);
} catch (MyException &err)
{
   stderr << "badness.";
}

void fCallTheDoItFunction(void *cookie)
{
    MyClass* c = reinterpret_cast<MyClass*>(cookie);
    if (c->IsInvalid())
        throw MyException;
    c->DoIt();
}

Вы можете столкнуться с серьезными проблемами в зависимости от вашего компилятора. Оказывается, что в некоторых компиляторах при оптимизации они видят один вызов C в блоке try / catch и радостно восклицают: «Я вызываю функцию C, которая, поскольку это старый добрый C, не может генерировать! Calloo-cally Я уберу все остатки попытки / улова, так как она никогда не будет достигнута.

Глупый компилятор.

Не звоните в C, который перезванивает вам и ожидает, что сможет поймать.

1 голос
/ 26 сентября 2008

Вот простой способ сделать это, не забудьте правильно управлять временем жизни вашего объекта MemberFunction.

#include 

class MyClass
{
public:
    void DoStuff()
    {
        printf("Doing Stuff!");
    }
};

struct MemberFunction
{
    virtual ~MemberFunction(){}
    virtual void Invoke() = 0;
};

void InvokeMember(void *ptr)
{
    static_cast(ptr)->Invoke();
}

template 
struct MemberFunctionOnT : MemberFunction
{
    typedef void (T::*function_t)();
public:
    MemberFunctionOnT(T* obj, function_t fun)
    {
        m_obj = obj;
        m_fun = fun;
    }

    void Invoke()
    {
        (m_obj->*m_fun)();
    }
private:
    T *m_obj;
    function_t m_fun;
};

template 

MemberFunction* NewMemberFunction(T *obj, void (T::*fun)())
{ 
    return new MemberFunctionOnT(obj, fun); 
}

//simulate a C-style function offering callback functionality.
void i_will_call_you_later(void (*fun)(void*), void *arg)
{
    fun(arg);
}

int main()
{
    //Sample usage.
    MyClass foo;

    MemberFunction *arg = NewMemberFunction(&foo, &MyClass::DoStuff);
    i_will_call_you_later(&InvokeMember, arg);
    return 0;
}
1 голос
/ 26 сентября 2008

Функция-член ДОЛЖНА быть статической. Нестатические имеют подразумеваемый аргумент "это". Передайте указатель на ваш экземпляр Whither как void *, чтобы статический член мог получить в этом экземпляре.

0 голосов
/ 26 сентября 2008

Хотя я не использовал его из C, для выполнения обратных вызовов я настоятельно рекомендую посмотреть libsigc ++ . Это было именно то, что мне было нужно несколько раз при выполнении обратных вызовов C ++.

0 голосов
/ 26 сентября 2008

См. ссылка

По сути, это невозможно напрямую, потому что: «Указатели на нестатические члены отличаются от обычных указателей на функции C, поскольку им требуется передать указатель this объекта класса. Таким образом, обычные указатели на функции и [указатели на] нестатические функции-члены имеют разные и несовместимые подписи» 1005 *

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...