Re: Преобразование указателей
От: bkat  
Дата: 17.03.03 10:21
Оценка: 4 (2)
Здравствуйте, Нартов Андрей Евгеньевич, Вы писали:

НАЕ>Преобразовать указатель на функцию — метод объекта (не статическую) в указатель на обычную функцию. Возможно ли это?


Давай забудем на время об указателях...

А вообще есть смысл говорить о методах объекта без самого объекта?
Ну представь, к примеру, класс "Дверь" и метод "Открыть".
А теперь попробуй открыть дверь в твою квартиру не имея самой двери.
Есть в этом смысл? На мой взгляд смысла в этом нем.
С указателями на методы объекта в С++ ситуация абсолютно такая же.
Re[3]: Преобразование указателей
От: VladD2 Российская Империя www.nemerle.org
Дата: 17.07.01 20:24
Оценка: -1
Здравствуйте Нартов Андрей Евгеньевич, вы писали:

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

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

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

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

// Структура для передачи указателя на функцию класса
struct CUniversalFuncPtr
{
void * pF; // Указатель на функцию класса
// Заметьте — это простой указатель на начало области
// кодом занимаемой кодом функции.

void * pThis; // Указатель на this экземпляр класса
};

// Подопытный кролик. :)
class CClass
{
public:
void f1(int i)
{
printf("i = %d, m_i = %d", i, m_i);
}
int m_i;
};

// Функция динамически вызывающаяя функцию любого класса.
// UniversalFuncPtr — унивирсальный указатель :))
// i — параметр передаваемый в вызываемую функцию.
void SomeCaller(CUniversalFuncPtr UniversalFuncPtr, int i)
{
__asm
{
// Эмулируем thiscall
mov ecx, UniversalFuncPtr.pThis;
mov eax, UniversalFuncPtr.pF;

push i ; // закладываем значение параметра
call eax // ecx->f1(i);
}
}

int main(int argc, char* argv[])
{
// Создаем переменную — подопытного кролика...
CClass c1;
// и инициализируем ее переменную-член...
c1.m_i = 123456;


// Поулучаем указатель на функцию некоторого класса.
// Совершенно неважно какой это класс, главное что функция
// должна быть описанна как:
// void FuncName(int);
typedef void ( CClass::*pmfnP)(int);
pmfnP pFunction = &CClass::f1;

CUniversalFuncPtr UniversalFuncPtr;
// Закладываем указатель на объекта (this)
UniversalFuncPtr.pThis = (void*)&c1;
// Закладываем указатель на функцию
__asm { mov eax, dword ptr pFunction }
__asm { mov UniversalFuncPtr.pF, eax }
// Приведенные выше операторы аналогичны конструкции:
// UniversalFuncPtr.pF = (void*)pFunction;
// но к сожалению такая конструкция вызывает сообщение об ошибке:
// error C2440: 'type cast' : cannot convert from
// 'void (__thiscall CClass::*)(int)' to 'void *'

// ...

SomeCaller(UniversalFuncPtr, 55555);

getchar();

return 0;
}
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Преобразование указателей
От: Аноним  
Дата: 17.03.03 01:35
Оценка: -1
Здравствуйте, Андрей Тарасевич, Вы писали:

АТ>Нет. Указатель на функцию и указатель на метод класса — объекты совершенно разной природы в С++ и никакого преобразования между ними быть не может. И даже на уровне бинарного представления в общем случае внутреннее устройство этих указателей может сильно различаться (здесь уже речь идет о конкретных платформах/компиляторах, разумеется).


не правда
точно такой же вызов, только дополнительно ECX = this
Re[3]: Преобразование указателей
От: Андрей Тарасевич Беларусь  
Дата: 13.11.03 18:21
Оценка: :)
Здравствуйте, Аноним, Вы писали:

АТ>>Нет. Указатель на функцию и указатель на метод класса — объекты совершенно разной природы в С++ и никакого преобразования между ними быть не может. И даже на уровне бинарного представления в общем случае внутреннее устройство этих указателей может сильно различаться (здесь уже речь идет о конкретных платформах/компиляторах, разумеется).


А>не правда

А>точно такой же вызов, только дополнительно ECX = this

Неверно.

(Я не понимаю, что происходим с форумом и что здесь делает это замшелое сообщение, но на всякий случай отвечу).

Вызов тебе кажется "точно таким же" потому, что ты еще, скорее всего, мало видел этих вызовов. Советую тебе на досуге подумать над результатами, респечатываемыми вот такой программой

#include <iostream>

#ifdef _MSC_VER
#pragma pointers_to_members(full_generality, multiple_inheritance)
#endif

class A {};

int main()
{
  std::cout << sizeof(void (A::*)()) << std::endl;
  std::cout << sizeof(void (*)()) << std::endl;
}


О результатах своих раздумий сообщи нам.
Best regards,
Андрей Тарасевич
Re[2]: Преобразование указателей
От: shura_mam  
Дата: 13.11.03 21:16
Оценка: -1
Здравствуйте, zelyony, Вы писали:

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


НАЕ>>Преобразовать указатель на функцию — метод объекта (не статическую) в указатель на обычную функцию. Возможно ли это?


Z>В VC++ 6 насколько я помню в функциях-членах this передаётся в регистре EAX, а дальше


На моем VC 6 this всегда передается в ECX.
Преобразование указателей
От: Нартов Андрей Евгеньевич  
Дата: 17.07.01 06:52
Оценка:
Преобразовать указатель на функцию — метод объекта (не статическую) в указатель на обычную функцию. Возможно ли это?
Re: Преобразование указателей
От: adontz Грузия http://adontz.wordpress.com/
Дата: 17.07.01 10:41
Оценка:
Здравствуйте Нартов Андрей Евгеньевич, вы писали:

НАЕ>Преобразовать указатель на функцию — метод объекта (не статическую) в указатель на обычную функцию. Возможно ли это?


уточните зачем это надо. А так пиши (GERERIC_FUCTION_TYPE)MyClass.MyStrangeMethod();
A journey of a thousand miles must begin with a single step © Lau Tsu
Re[2]: Преобразование указателей
От: Нартов Андрей Евгеньевич  
Дата: 17.07.01 12:02
Оценка:
A>уточните зачем это надо.
например, сделать нить (CreateThread()) из нестатической member function

А> так пиши (GERERIC_FUCTION_TYPE)MyClass.MyStrangeMethod();

(GENERIC_FUCTION_TYPE) — как я понимаю выглядит так:
DWORD (WINAPI* MyStrangeFunction)( LPVOID )
и компилятор совершенно справедливо говорит на это вот что:

[C++ Error] Unit1.cpp(30): E2031 Cannot cast from 'unsigned long (__stdcall * (_closure )(void *))(void *)' to 'unsigned long (__stdcall *)(void *)'

Но ведь должен быть какой-то способ сделать такое преобразование указателей?
Re[3]: Преобразование указателей
От: wvk Россия  
Дата: 17.07.01 12:58
Оценка:
НАЕ>Но ведь должен быть какой-то способ сделать такое преобразование указателей?
Способ конечно есть, только не нужно это...
Вообще, посмотрите тему "Адрес метода" в этом форуме,
там обсуждается разница между статическими и нестатическими функциями.
Re: Преобразование указателей
От: zelyony  
Дата: 27.07.01 05:01
Оценка:
Здравствуйте Нартов Андрей Евгеньевич, вы писали:

НАЕ>Преобразовать указатель на функцию — метод объекта (не статическую) в указатель на обычную функцию. Возможно ли это?


В VC++ 6 насколько я помню в функциях-членах this передаётся в регистре EAX, а дальше
идут остальные параметры. Тут нужны будут какие-то извращения с АСМом.
P.S. Зачем всё это надо, наверное ФСБ грохать? :)
Re[2]: Преобразование указателей
От: Igor Soukhov  
Дата: 27.07.01 12:35
Оценка:
Здравствуйте zelyony, вы писали:

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


НАЕ>>Преобразовать указатель на функцию — метод объекта (не статическую) в указатель на обычную функцию. Возможно ли это?


Z>В VC++ 6 насколько я помню в функциях-членах this передаётся в регистре EAX, а дальше

Z>идут остальные параметры. Тут нужны будут какие-то извращения с АСМом.
Z>P.S. Зачем всё это надо, наверное ФСБ грохать? :)
ты что-ли ? =)

Igor
* thriving in a production environment *
Re: Преобразование указателей
От: Андрей Тарасевич Беларусь  
Дата: 28.07.01 18:11
Оценка:
Здравствуйте Нартов Андрей Евгеньевич, вы писали:

НАЕ>Преобразовать указатель на функцию — метод объекта (не статическую) в указатель на обычную функцию. Возможно ли это?


Нет. Указатель на функцию и указатель на метод класса — объекты совершенно разной природы в С++ и никакого преобразования между ними быть не может. И даже на уровне бинарного представления в общем случае внутреннее устройство этих указателей может сильно различаться (здесь уже речь идет о конкретных платформах/компиляторах, разумеется).

Best regards,
Андрей.
Best regards,
Андрей Тарасевич
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, чтобы не оставались потоки-зомби. Но я не стал этого делать чтобы не перегружать пример особенностями поточной библиотеки.
Re[3]: Преобразование указателей
От: ArtDenis Россия  
Дата: 17.03.03 07:12
Оценка:
Здравствуйте, <Аноним>, Вы писали:

А>точно такой же вызов, только дополнительно ECX = this


Во первых, что С++ не для Интел процессоров кто-то отменял??? А во вторых, какой практический смысл иакого преобразования?

Денис.
... << RSDN@Home 1.0 beta 6a >>
[ 🎯 Дартс-лига Уфы | 🌙 Программа для сложения астрофото ]
Re[3]: Преобразование указателей
От: MaximE Великобритания  
Дата: 17.03.03 10:09
Оценка:
Здравствуйте, Нартов Андрей Евгеньевич, Вы писали:

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

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

А>> так пиши (GERERIC_FUCTION_TYPE)MyClass.MyStrangeMethod();

НАЕ>(GENERIC_FUCTION_TYPE) — как я понимаю выглядит так:
НАЕ>DWORD (WINAPI* MyStrangeFunction)( LPVOID )
НАЕ>и компилятор совершенно справедливо говорит на это вот что:

Используй thunk. Смотри здесь
Автор: MaximE
Дата: 21.11.02
Re[4]: Преобразование указателей
От: Павел Кузнецов  
Дата: 14.11.03 07:40
Оценка:
Здравствуйте, Андрей, Вы писали:

АТ> (Я не понимаю, что происходим с форумом и что здесь делает это замшелое

АТ> сообщение, но на всякий случай отвечу).

Это кто-то, читая старые сообщения, решил на них поотвечать. При этом соответствующая
ветка автоматически "всплывает" наверх. Как я понимаю, тебе придется объяснять все заново.
Эдакое "назад в будущее-2"
Posted via RSDN NNTP Server 1.7 "Bedlam"
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[3]: Преобразование указателей
От: MaximE Великобритания  
Дата: 14.11.03 08:55
Оценка:
shura_mam wrote:

Z>> В VC++ 6 насколько я помню в функциях-членах this передаётся в регистре EAX, а дальше


sm> На моем VC 6 this всегда передается в ECX.


struct some
{
    void __stdcall foo();
};


Даже для some::foo?
Posted via RSDN NNTP Server 1.8 beta
Re[5]: Преобразование указателей
От: Андрей Тарасевич Беларусь  
Дата: 14.11.03 11:08
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

АТ>> (Я не понимаю, что происходим с форумом и что здесь делает это замшелое

АТ>> сообщение, но на всякий случай отвечу).

ПК>Это кто-то, читая старые сообщения, решил на них поотвечать. При этом соответствующая

ПК>ветка автоматически "всплывает" наверх. Как я понимаю, тебе придется объяснять все заново.
ПК>Эдакое "назад в будущее-2"

Да, но похоже что дело не в этом. У меня со вчерашнего вечера до сегодняшенго утра форум показывал только какие-то совершенно старинные сообщения. Я понимаю, что старые ветки всплывают наверх, если в них что-то написать, но внимательное изучение верхних веток показало, что ни одного свежего сообщения в них нет. Я так и не понял, что происходило: то ли база сообщений временно "обрубилась" где-то в районе 2001 года и все новые сообщения временно пропали, то ли просто что-то нехорошее произошло с сортировкой и 2001 год почему-то всплыл наверх. К позднему утру все вдруг неожиданно стало на свои места.
Best regards,
Андрей Тарасевич
Re[6]: Преобразование указателей
От: Павел Кузнецов  
Дата: 14.11.03 11:30
Оценка:
Здравствуйте, Андрей, Вы писали:

АТ> Да, но похоже что дело не в этом. У меня со вчерашнего вечера до сегодняшенго

АТ> утра форум показывал только какие-то совершенно старинные сообщения.

А... Это другое дело. Я, просто, через web почти сайт не смотрю. Похоже, действительно,
была какая-то ошибка с сортировкой. Во всяком случае во внутрикомандном форуме что-то
такое упоминалось.
Posted via RSDN NNTP Server 1.7 "Bedlam"
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[7]: Преобразование указателей
От: dupamid Россия  
Дата: 15.11.03 17:46
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

АТ>> Да, но похоже что дело не в этом. У меня со вчерашнего вечера до сегодняшенго

АТ>> утра форум показывал только какие-то совершенно старинные сообщения.

ПК>А... Это другое дело. Я, просто, через web почти сайт не смотрю. Похоже, действительно,

ПК>была какая-то ошибка с сортировкой. Во всяком случае во внутрикомандном форуме что-то
ПК>такое упоминалось.

К сожалению, ошибка еще есть, но теперь в другую сторону
Это сообщение не всплыло вверх, хотя на него последнее ответили.
Re[8]: Преобразование указателей
От: dupamid Россия  
Дата: 15.11.03 17:47
Оценка:
А теперь всплыло, странное что-то твориться...
Re[3]: Преобразование указателей
От: Кодт Россия  
Дата: 16.11.03 15:51
Оценка:
Здравствуйте, Нартов Андрей Евгеньевич, Вы писали:

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

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

<>

НАЕ>Но ведь должен быть какой-то способ сделать такое преобразование указателей?


Преобразование указателей — нет такого способа (в общем виде). И вот почему.

Указатель на функцию или статический метод — это указатель в сегменте кода. Его размер для intel/win32 равен 4.
Указатель на метод — это совершенно иное явление. Он содержит разнообразную дополнительную информацию — о виртуальном методе, о виртуальной базе — и достигает размера 20 байт.

Чтобы передать в нить связку (closure) объекта и метода, нужно воспользоваться шлюзом (thunk).
Пример
template<class V>
V* assert_non_null(V* v) { assert(v != NULL); return v; }

template<class T>
struct Thunk
{
  typedef T Type;
  typedef Thunk<T> This;
  typedef void (*Type::Method)();

  Type* obj_;
  Method mtd_;

  Thunk(Type* obj, Method mtd) : obj_(assert_non_null(obj)), mtd_(assert_non_null(mtd)) {}

  static DWORD CALLBACK thunk_proc(LPVOID param)
  {
    This* thunk = assert_non_null(reinterpret_cast<This*>(param));
    thunk->play();
    delete thunk;
    return 0;
  }

  void play()
  {
    (obj_->*mtd_)();
  }
};

///

class Greeting
{
public:
  void hello();
  void goodbye();
};

///

Greeting a, b;

CreateThread(Thunk<Greeting>::thunk_proc, new Thunk<Greeting>(&a, &Greeting::hello));
CreateThread(Thunk<Greeting>::thunk_proc, new Thunk<Greeting>(&b, &Greeting::goodbye));

В более общем виде — можно делать шлюз для boost::function
DWORD CALLBACK boost_function_thunk(LPVOID param)
{
  boost::function0<void>* pfn = reinterpret_cast<boost::function0<void>*>(param);
  (*pfn)();
  delete pfn;
  return 0;
}

///

CreateThread(boost_function_thunk, new boost::function0<void>(&a, &Greeting::hello));
Перекуём баги на фичи!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.