Указатель на метод объекта класса. Возможно?
От: MTimur  
Дата: 29.03.10 21:20
Оценка:
class A
{
public:
    void m (void)    
    {
        //...
    };
};

typedef void     (*pa)(void);
typedef void (A::*pma)(void);

void someforeignfunc(pa)           // <-- вот это менять нельзя
{
    //...
}

A a;
void main ()
{
    someforeignfunc(/*???*/);         // <-- как вот здесь подсунуть указатель на a.m?
}


Как в someforeignfunc отдать указатель на a.m не используя интерфейсов вроде
void f()
{
    a.m();
}


Очевидное решение — изменить someforeignfunc так, чтобы принимала pma, но эта функция "не моя".
Понимаю, что никак, но надеюсь, что ошибаюсь
Re: Указатель на метод объекта класса. Возможно?
От: Кодт Россия  
Дата: 29.03.10 22:56
Оценка: 2 (1)
Здравствуйте, MTimur, Вы писали:

MT>Очевидное решение — изменить someforeignfunc так, чтобы принимала pma, но эта функция "не моя".

MT>Понимаю, что никак, но надеюсь, что ошибаюсь

Можно с рядом ограничений.

1) Синглетон
// присвой нужные значения перед вызовом
static A* object;
static pma method;
void invoke() { (object->*method)(); }

Нельзя одновременно создавать произвольное количество РАЗНЫХ замыканий.
Хотя, можно завести пул функций invoke1, invoke2, ... — и для каждой свой комплект статических данных.

2) Санки
struct Thunk
{
  char bytecode[XZ];
  void generate(A* object, pma method)
  {
    // здесь рожаем байткод для функции invoke()
    // в который подставляем значения object и method
  }
  pa get_invoke() { return (pa)bytecode; }
};

Это платформенно-зависимый подход. Для разных процессоров — разная реализация.
Используется в ATL, кстати. Можно попробовать оттуда выдрать.
Перекуём баги на фичи!
Re[2]: Указатель на метод объекта класса. Возможно?
От: remark Россия http://www.1024cores.net/
Дата: 30.03.10 06:17
Оценка: 1 (1)
Здравствуйте, Кодт, Вы писали:

К>Нельзя одновременно создавать произвольное количество РАЗНЫХ замыканий.

К>Хотя, можно завести пул функций invoke1, invoke2, ... — и для каждой свой комплект статических данных.

Добавлю только, что такой пул можно сгенерировать с помощью шаблонов "для любого конечного N".
Сгенерировать функций заведомо больше, чем должно понадобиться (например, больше, чем кол-во потоков). Дальше брать калбеки из пула и возвращать, ну а если пул оказывается пустой, то кидать исключение, это типа как out-of-memory, ну точнее out-of-resources.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[2]: Указатель на метод объекта класса. Возможно?
От: sidorov18 США  
Дата: 30.03.10 06:41
Оценка:
Здравствуйте, Кодт, Вы писали:

К>2) Санки

К>
К>struct Thunk
К>{
К>  char bytecode[XZ];
К>  void generate(A* object, pma method)
К>  {
К>    // здесь рожаем байткод для функции invoke()
К>    // в который подставляем значения object и method
К>  }
К>  pa get_invoke() { return (pa)bytecode; }
К>};
К>

К>Это платформенно-зависимый подход. Для разных процессоров — разная реализация.
К>Используется в ATL, кстати. Можно попробовать оттуда выдрать.

а в каком классе/функции такое в ATL не подскажите?
Re[2]: Указатель на метод объекта класса. Возможно?
От: MTimur  
Дата: 30.03.10 08:21
Оценка:
Здравствуйте, Кодт, Вы писали:

К>
К>static A* object;
К>static pma method;
К>void invoke() { (object->*method)(); }
К>


В данном случае это аналогично:
MT>
MT>void f()
MT>{
MT>    a.m();
MT>}
MT>


А вот в том то и дело, что не хотелось делать такой обвес.
На самом деле мне нужно передать во внешнюю библиотеку набор callback функций:

typedef void (*pa)(void);
typedef int  (*pb)(void);
typedef void (*pc)(int);
typedef void (A::*pma)(void);
struct cb
{
    pa a1;
    pa a2;
    pb b;
    pc c;
//  и еще пару десятков строк
//  ....
}

// cb.a = ...
// cb.b = ...
// ..........
someforeignfunc(cb);


Все эти callback'и реализованы как методы класса. В программе создается один экземпляр этого класса и на момент заполнения структуры cb и вызова someforeignfunc этот экземпляр уже существует. Кроме того он не имеет наследников и сам не является наследником. Т.о. методы этого объекта не отличаются от обычной функции. Верно? Или я ошибаюсь?
Так вот я хотел в структуру cb затолкать указатели на его методы, а не на функции-обвязки указанные выше (f() и invoke()). Для компилятора pa и pma совсем не одно и то же, хотя кажется мне, что разницы в данном случае как бы нет.
Очевидно обмануть компилятор не получится, придется делать внешние функции.
Спасибо.
Re[3]: Указатель на метод объекта класса. Возможно?
От: Кодт Россия  
Дата: 30.03.10 09:33
Оценка: 2 (1)
Здравствуйте, sidorov18, Вы писали:

S>а в каком классе/функции такое в ATL не подскажите?


Файл %путь-до-студии%/VC/atlmfc/include/atlstdthunk.h
Перекуём баги на фичи!
Re[3]: Указатель на метод объекта класса. Возможно?
От: unreg_flex  
Дата: 30.03.10 09:42
Оценка:
Здравствуйте, MTimur, Вы писали:

MT>Все эти callback'и реализованы как методы класса. В программе создается один экземпляр этого класса и на момент заполнения структуры cb и вызова someforeignfunc этот экземпляр уже существует. Кроме того он не имеет наследников и сам не является наследником. Т.о. методы этого объекта не отличаются от обычной функции. Верно? Или я ошибаюсь?


В общем случае ошибаетесь.
Ибо метод объекта для работы хочет this.
А передача этого this осуществляется через какой нить __thiscall calling convention.
Если например рассматривать компилятор от MS и допустить что ваш метод не обращается к полям своего объекта, то ваше предположение будет верно,
в противном случае нет.
Передать указатель то очень просто, даже асм вставок ненужно =)
Но это будет грязнючий хак.

Самое лучшее решение имхо с переходником, как написал Кодт.

П.С. Почемуто вспомнилась милая задачка о получении адреса конструктора произвольного объекта =)
Re[3]: Указатель на метод объекта класса. Возможно?
От: rus blood Россия  
Дата: 30.03.10 09:44
Оценка:
Здравствуйте, MTimur, Вы писали:

MT>А вот в том то и дело, что не хотелось делать такой обвес.

MT>На самом деле мне нужно передать во внешнюю библиотеку набор callback функций:
MT>Все эти callback'и реализованы как методы класса.

Это твоя реализация, или так сделана внешняя библиотека?

MT>В программе создается один экземпляр этого класса и на момент заполнения структуры cb и вызова someforeignfunc этот экземпляр уже существует. Кроме того он не имеет наследников и сам не является наследником. Т.о. методы этого объекта не отличаются от обычной функции. Верно? Или я ошибаюсь?


Ошибаешься. Методы классов имеют неявный параметр this (или const this).
Что по твоему должна передать внешняя библиотека в качестве этого параметра?

MT>Так вот я хотел в структуру cb затолкать указатели на его методы, а не на функции-обвязки указанные выше (f() и invoke()). Для компилятора pa и pma совсем не одно и то же, хотя кажется мне, что разницы в данном случае как бы нет.

MT>Очевидно обмануть компилятор не получится, придется делать внешние функции.

Ты, кажется, пытаешься изобрести велосипед, под названием интерфейс.
Имею скафандр — готов путешествовать!
Re[4]: Указатель на метод объекта класса. Возможно?
От: remark Россия http://www.1024cores.net/
Дата: 30.03.10 09:56
Оценка: 1 (1)
Здравствуйте, unreg_flex, Вы писали:

_>В общем случае ошибаетесь.

_>Ибо метод объекта для работы хочет this.
_>А передача этого this осуществляется через какой нить __thiscall calling convention.
_>Если например рассматривать компилятор от MS и допустить что ваш метод не обращается к полям своего объекта, то ваше предположение будет верно,
_>в противном случае нет.
_>Передать указатель то очень просто, даже асм вставок ненужно =)
_>Но это будет грязнючий хак.

АФАИР, если добавить в функцию-метод ellipsis последним параметром, то и студия и гцц переделывают соглашение о вызове в __cdecl, и тогда её можно передавать как каллбек в С код — this будет выглядеть как первый параметр типа void*.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[4]: Указатель на метод объекта класса. Возможно?
От: unreg_flex  
Дата: 30.03.10 09:57
Оценка:
Если интересно хаком можно например так:

class A {
public:
  void m() { }
};

typedef void     (*FuncPtr)(void);
typedef void (A::*MemberPtr)(void);

struct { MemberPtr p; } Ptr;

Ptr.p=&A::m;

TCL_ASSERT(sizeof(Ptr)==sizeof(FuncPtr));

*((FuncPtr*)&Ptr); // <<<--- НАЛЕТАЙ!
Re[5]: Указатель на метод объекта класса. Возможно?
От: unreg_flex  
Дата: 30.03.10 10:03
Оценка:
Здравствуйте, remark, Вы писали:

R>АФАИР, если добавить в функцию-метод ellipsis последним параметром, то и студия и гцц переделывают соглашение о вызове в __cdecl, и тогда её можно передавать как каллбек в С код — this будет выглядеть как первый параметр типа void*.


Красиво
Re[5]: Указатель на метод объекта класса. Возможно?
От: remark Россия http://www.1024cores.net/
Дата: 30.03.10 10:17
Оценка:
Здравствуйте, unreg_flex, Вы писали:

_>
_>class A {
_>public:
_>  void m() { }
_>};

_>typedef void     (*FuncPtr)(void);
_>typedef void (A::*MemberPtr)(void);

_>struct { MemberPtr p; } Ptr;

_>Ptr.p=&A::m;

_>TCL_ASSERT(sizeof(Ptr)==sizeof(FuncPtr));

_>*((FuncPtr*)&Ptr); // <<<--- НАЛЕТАЙ!
_>


Подожди, а this-то кто будет передавать в эту функцию? И зачем Ptr, чем это отличается от простого каста одной функции в другую? И размеры их зачастую не равны...


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[6]: Указатель на метод объекта класса. Возможно?
От: unreg_flex  
Дата: 30.03.10 10:24
Оценка:
Здравствуйте, remark, Вы писали:

R>Подожди, а this-то кто будет передавать в эту функцию? И зачем Ptr, чем это отличается от простого каста одной функции в другую? И размеры их зачастую не равны...


Я же написал выше, что только если методы не обращаются к полям объекта.
Простой каст вроде как не канает.
Про размеры я в курсе поетому и влепил ассерт, автор поста же написал что объект простой без наследования и как бы прочей хрени.
Я сам бы такое в коде низачто не оставил, просто как я понял у автора скастить как раз и не получалось, но передать хоть как нибудь очень хотелось.
Re[7]: Указатель на метод объекта класса. Возможно?
От: remark Россия http://www.1024cores.net/
Дата: 30.03.10 10:30
Оценка:
Здравствуйте, unreg_flex, Вы писали:

_>Здравствуйте, remark, Вы писали:


R>>Подожди, а this-то кто будет передавать в эту функцию? И зачем Ptr, чем это отличается от простого каста одной функции в другую? И размеры их зачастую не равны...


_>Я же написал выше, что только если методы не обращаются к полям объекта.

_>Простой каст вроде как не канает.
_>Про размеры я в курсе поетому и влепил ассерт, автор поста же написал что объект простой без наследования и как бы прочей хрени.
_>Я сам бы такое в коде низачто не оставил, просто как я понял у автора скастить как раз и не получалось, но передать хоть как нибудь очень хотелось.

Всё-таки простой объект без наследования и вообще не обращаться к полям — это разные вещи. А если уж он не обращается к полям, то надо не это городить, а пометить методы как static.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[8]: Указатель на метод объекта класса. Возможно?
От: unreg_flex  
Дата: 30.03.10 10:35
Оценка:
Здравствуйте, remark, Вы писали:

R>... а пометить методы как static.


Это уже изменение условий исходной задачи
Re: Указатель на метод объекта класса. Возможно?
От: vpchelko  
Дата: 30.03.10 11:59
Оценка:
Я немного поиздевался, поизвращался, плз бейте меня больно. Извращался как мог.

class A
{
public:
void m (void)
{
//...
};
void xD (void)
{
//...
};
};

typedef void (*pa)(void);

typedef void (A::*PMA)(void);

void someforeignfunc(pa a) // <-- вот это менять нельзя
{
//...
a();
}

template <class T>
class B
{
public:
typedef void (A::*TMA)(void);

static pa getPA(T* _a, TMA _pma)
{
static B* b = NULL;
if(b != NULL)
delete b;
b = new B(_a, _pma);

return b->m;
}

private:
B(T* _a, TMA _pma)
{
b(this);
a = _a;
pma = _pma;
}

static void m(void)
{
(b()->a->*(b()->pma))();

}

T* a;
TMA pma;

static B* b(B* _b = NULL)
{
static B* b;
if (_b != NULL)
b = _b;
return b;
};
};


void main ()
{
A a;

someforeignfunc( B<A>::getPA(&a, &A::xD) );
someforeignfunc( B<A>::getPA(&a, &A::m) );

}
Сало Украине, Героям Сала
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.