Re[4]: Преобразование указателей
От: Аноним  
Дата: 17.03.03 05:28
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Здравствуйте Нартов Андрей Евгеньевич, вы писали:


A>>>уточните зачем это надо.

НАЕ>>например, сделать нить (CreateThread()) из нестатической member function

VD>Ну, для этого можно обойтись и вызовом не статической функции из статической или глобальной, а вообще... я как то выпердривался с этим делом. Как сделать это на C++ я не раскопал. На Delpth это делается легко. На Плюсах пришлось выпендриваться с asm-ом.


VD>Общая идея такова — записываем указатель на фунцию и на this экземпляра объекта в структуру.

VD>И эмулируем вызов функции. Вот код:

Можно прекрасно обойтись и без asm'а. Идея в целом такая же. Вот пример того, как можно запустить методы в других потоках используя POSIX API (проверялось на Linux, gcc).

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

template<class T>
struct Launcher {
private:
    T *m_pThis; // указатель на объект, чей метод будет вызван
    void (T::*m_pMethod)(); // указатель на метод
    pthread_mutex_t m_mutex; // дополнительные поля для синхронизации потоков
    pthread_cond_t m_condv; // . . .
    bool volatile m_bReady; // . . .

    // системно-зависимая функция с которой начинается выполнение потока
    static void *threadfunc(void *p) {
        // достаем указатели на объект и его метод
        Launcher<T> *pObj = (Launcher<T> *) p;
        T *pThis = pObj->m_pThis;
        void (T::*pMethod)() = pObj->m_pMethod;
        // говорим главному потоку, что все нормально
        pthread_mutex_lock(&pObj->m_mutex);
        pObj->m_bReady = true;
        pthread_cond_signal(&pObj->m_condv);
        pthread_mutex_unlock(&pObj->m_mutex);
        // запускаем метод
        (pThis->*pMethod)();
        return 0;
    }
public:
    Launcher(T *pThis, void (T::*pMethod)()): m_pThis(pThis), m_pMethod(pMethod) {
        pthread_mutex_init(&m_mutex, 0);
        pthread_cond_init(&m_condv, 0);
    }
    ~Launcher() {
        pthread_cond_destroy(&m_condv);
        pthread_mutex_destroy(&m_mutex);
    }
    void start() {
        pthread_t thr;
        m_bReady = false;
        // запускаем поток, указываем функцию, с которой поток должен начать работу (threadfunc), и параметр для нее (this)
        pthread_create(&thr, 0, threadfunc, this);
        // ждем сигнала от запущенного потока, для того, чтобы потом можно было удалить объект Launcher из стека,
        // будучи уверенным, что запущенному потоку он уже не нужен.
        pthread_mutex_lock(&m_mutex);
        if(!m_bReady)
            pthread_cond_wait(&m_condv, &m_mutex);
        pthread_mutex_unlock(&m_mutex);
    }
};

template<class T>
void launch(T *pThis, void (T::*pMethod)()) {
    Launcher<T> box(pThis, pMethod);
    box.start();
}

// пример использования
struct A {
    void func() {
        printf("%s\n", __PRETTY_FUNCTION__);
    }
};

struct B {
    void func1() {
        printf("%s\n", __PRETTY_FUNCTION__);
    }
    void func2() {
        printf("%s\n", __PRETTY_FUNCTION__);
    }
};

int main() {
    A a;
    B b;
    launch(&a, &A::func);
    launch(&b, &B::func1);
    launch(&b, &B::func2);
    usleep(1000);
    return 0;
}


Тоже самое можно написать, используя Win32 API. Этот код никак не зависит от того, в каких регистрах что находится и как на низком уровне осуществляется вызов методов и функций.

--
Дмитрий

PS В этом примере потоки создаются joinable и им нужно было бы сделать pthread_join, чтобы не оставались потоки-зомби. Но я не стал этого делать чтобы не перегружать пример особенностями поточной библиотеки.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.