Re[4]: Указатель на функцию
От: Андрей Якубовский  
Дата: 22.12.04 10:08
Оценка:
Здравствуйте, Аноним, Вы писали:

Еще раз прошу прощения у общественности. Но на самом деле мне было важно не приведение типа к войд*, А приведение указателей на функции с неизвестнвм количеством парметров к некой одному типу, чего я и добился. Я всякие терки по поводу размера адреса функции эт пожалуйста на ваше усмотрение.
Re[5]: Указатель на функцию
От: Хитрик Денис Россия RSDN
Дата: 22.12.04 10:20
Оценка: +2
Здравствуйте, Андрей Якубовский, Вы писали:

АЯ>Еще раз прошу прощения у общественности. Но на самом деле мне было важно не приведение типа к войд*, А приведение указателей на функции с неизвестнвм количеством парметров к некой одному типу, чего я и добился. Я всякие терки по поводу размера адреса функции эт пожалуйста на ваше усмотрение.


Андрей, заводя новые ники вы еще раз попадаете под несоблюдение правил форума, в частности под спор с модератором. Вам запрещено писать в форум. Извольте соблюдать. В противном случае будет заблокирован ваш IP адрес и отключена возможность читать форум.
Новый ник заблокирован.
Обсуждения — только через moderator@rsdn.ru
Правила нашего с вами форума.
Как правильно задавать вопросы. © 2001 by Eric S. Raymond; перевод: © 2002 Валерий Кравчук.
Re[4]: Указатель на функцию
От: Андрей Тарасевич Беларусь  
Дата: 22.12.04 10:24
Оценка: 12 (1)
Здравствуйте, Аноним, Вы писали:

А>Обратите пожалуйста внимание на __stdcall. Я готов пообсуждать без эмоций в чем я не прав. Токо пжалуйста не надо стучать себя кулоком в грудь и говорить что тут полный бред.

А>
А>struct Method {
А>    typedef long (__stdcall Method::*Function)();
А>    void*        mthdOwner;
А>    Function    mthdFunction;
А>    Method(void* Owner,...)
А>    {
А>        va_list mthd;
А>        va_start(mthd,Owner);
А>        mthdOwner        = Owner;
А>        mthdFunction    = static_cast<Function>(va_arg(mthd,Function));
А>        va_end(mthd);
А>    }
А>    long Run(int iCount,...)
А>    {
А>        void* pArgs[10];

А>        void* Owner = mthdOwner;
А>        void* Fn = mthdFunction;
А>        long Result;

А>        va_list marker;
А>        va_start( marker, iCount );

А>        int i=0;
А>        while(iCount --){
А>            pArgs[i ++] = va_arg( marker, void*);
А>        }
А>        va_end(marker);

А>        while(i--){
А>            __asm mov eax,i
А>            __asm mov eax,dword ptr pArgs[eax*4]
А>            __asm push eax

А>        }

А>        __asm push Owner
А>        __asm mov eax,Fn
А>        __asm call eax

А>        return Result;
А>    }
А>};
А>


Прекрасно. Без эмоций.

Указатели на методы классов в С++ обладают рядом уникальных свойств. На них стандартом языка накладываются определенные требования, удовлетворить которым можно только путем помещения в указатель дополнительной информации, наряду с банальным адресом точки входа в метод. По этой причине, если на некоторой платформа размер адреса равен 32-м битам, то размер корректно реализованного указателя на метод класса всегда заведомо больше 32-х бит (64 бита, например). По этой причине в общем случае невозможно сконвертировать указатель на метод класса в указатель типа 'void*' без потери информации. Какая-то информация будет потеряна. И это приведет к тому, что корректно вызвать метод через такой указатель будет невозможно.

Дополнительная информация, хранимая в указателе на метод, предназначена для вычисления корректного значения указателя 'this' объекта при вызове метода. В условиях обычного одиночного наследования эта информация не нужна. Но вот в условиях множественного наследования (не говоря уже о виртуальном) эта информация играет существенную роль. Я уже приводил пример с множественным наследованием, который демонстрирует (на уровне твоего любимого асма, сгенерированного MSVC++ 6) наличие в указателе на метод класса дополнительной информации (размер указателя в этом примере будет 64 бита) и то, как эта информация используется в процессе вызова. Я уже писал об этом более подробно, можно почитать здесь
Автор: Андрей Тарасевич
Дата: 16.02.02
, если есть такое желание.
Best regards,
Андрей Тарасевич
Re[5]: Указатель на функцию
От: Аноним  
Дата: 22.12.04 10:39
Оценка:
Здравствуйте, Андрей Тарасевич, Вы писали:

Спасибо, что ответели. Все что вы сказали является истинной, я об этом не спорю. Я привел конкретный пример который нужен мне в моей реализации, и который работает на 100%. Возможно в других реализациях он работать не будет, в частности я проверил действительно при перегруженных виртуальных функциях этот код не работает. Но мне это и не надо. А что касаемомо размера функции отмечу что правы как Вы так и я. Вы парвы с точки зрения синтаксиса С++, я прав в постановке вопроса, на который в ответах была приведена куча кода, не отвечающая моим требованиям. Веди изначально вопрос ставился в возможности приведения типов, а не в последующем вызове функции.

Буду рад услышать Ваш ответ. В чем моя ошибка.
Re[4]: Указатель на функцию
От: Андрей Тарасевич Беларусь  
Дата: 22.12.04 10:43
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Обратите пожалуйста внимание на __stdcall. Я готов пообсуждать без эмоций в чем я не прав. Токо пжалуйста не надо стучать себя кулоком в грудь и говорить что тут полный бред.

А>
А>struct Method {
А>    typedef long (__stdcall Method::*Function)();
А>    void*        mthdOwner;
А>    Function    mthdFunction;
А>    Method(void* Owner,...)
А>    {
А>        va_list mthd;
А>        va_start(mthd,Owner);
А>        mthdOwner        = Owner;
А>        mthdFunction    = static_cast<Function>(va_arg(mthd,Function));
А>        va_end(mthd);
А>    }
А>    long Run(int iCount,...)
А>    {
А>        void* pArgs[10];

А>        void* Owner = mthdOwner;
А>        void* Fn = mthdFunction;
А>        long Result;

А>        va_list marker;
А>        va_start( marker, iCount );

А>        int i=0;
А>        while(iCount --){
А>            pArgs[i ++] = va_arg( marker, void*);
А>        }
А>        va_end(marker);

А>        while(i--){
А>            __asm mov eax,i
А>            __asm mov eax,dword ptr pArgs[eax*4]
А>            __asm push eax

А>        }

А>        __asm push Owner
А>        __asm mov eax,Fn
А>        __asm call eax

А>        return Result;
А>    }
А>};
А>


Что же касается работоспособности это кода — я не знаю, чем Вы его компилировали, но у меня ни один компилятор не пропускает строчку

void* Fn = mthdFunction;


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

struct A {
  int i;
  A() : i(1) {}
  void __stdcall print() { std::cout << i << std::endl; }
};

struct B {
  int i;
  B() : i(2) {}
  void __stdcall print() { std::cout << i << std::endl; }
};

struct C : A, B {
  int i;
  C() : i(3) {}
};

void call(C* pc, void (__stdcall C::*pm)()) {
  (pc->*pm)();
  Method p(pc, pm);
  p.Run(0);
}

int main() {
  C c;
  call(&c, &A::print);
  call(&c, &B::print);
}


и посмотреть на результаты его работы (надеюсь я правильно воспользовался Вашим указателем внутри метода 'call'?). Предлагаю Вам сравнить то, что печатает "стандартный" вызов метода через указатель и то, что печатает вызов через Ваш "указатель".

Я все-таки заставил MSVC++ 6 SP6 компилировать вышеприведенную строчку и вот, к примеру, вывод, который поучается при использовании этого компилятора

1
1
2
1


Как видите, Ваши вызовы всегда печатали '1', в то время как второй "стандартный" вызов напечатал '2', как и должно быть. Надеюсь, это наведет Вас на размышления (все еще надеясь, что я правильно воспользовался Вашим указателем).

Если Вы получаете иные результаты или у Вас имеются какие-то другие вопросы — то, учитывая сложившейся ситуации в форуме, мы можем продолжить эту дискуссию мылом (при условии отсутствия эмоций, разумеется).
Best regards,
Андрей Тарасевич
Re[6]: Указатель на функцию
От: Аноним  
Дата: 22.12.04 10:44
Оценка: +1
Здравствуйте, Аноним, Вы писали:

>>Буду рад услышать Ваш ответ. В чем моя ошибка.


Ошибка в том, что ты выдаешь очень частный случай за верное решение.
И еще и упорствуешь.
Это примерно так же, как и реализовывать функцию сложения двух int
таким вот образом:

int Sum(int a, int b)
{
  return 4;
}


В частном случае Sum(2,2); функция работает замечательно и очень эффективно.
Но кому нужно такое решение?
Re[5]: Указатель на функцию
От: Аноним  
Дата: 22.12.04 10:46
Оценка:
Здравствуйте, Андрей Тарасевич, Вы писали:

Это моя оплошность в публикации кода.

вместо
void* Fn = mthdFunction;
надо
Function Fn = mthdFunction;

все компилируется, готов выслать проект, запускается все без проблем.
Re[6]: Указатель на функцию
От: Андрей Тарасевич Беларусь  
Дата: 22.12.04 10:49
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Спасибо, что ответели. Все что вы сказали является истинной, я об этом не спорю. Я привел конкретный пример который нужен мне в моей реализации, и который работает на 100%. Возможно в других реализациях он работать не будет, в частности я проверил действительно при перегруженных виртуальных функциях этот код не работает. Но мне это и не надо. А что касаемомо размера функции отмечу что правы как Вы так и я. Вы парвы с точки зрения синтаксиса С++, я прав в постановке вопроса, на который в ответах была приведена куча кода, не отвечающая моим требованиям. Веди изначально вопрос ставился в возможности приведения типов, а не в последующем вызове функции.


А>Буду рад услышать Ваш ответ. В чем моя ошибка.


Для начала стоило бы разобраться с тем, как Вам удалось скомпилировать строчку

void* Fn = mthdFunction;


в Вашем коде. Не один из имеющихся у меня под рукой компиляторов эту строчку в таком виде не пропускает. Вот, например, что сообщает MSVC++ 6 SP 6

error C2440: 'initializing' : cannot convert from 'long (__stdcall Method::*)(void)' to 'void *' 
             There is no context in which this conversion is possible
Best regards,
Андрей Тарасевич
Re[6]: Указатель на функцию
От: Андрей Тарасевич Беларусь  
Дата: 22.12.04 10:55
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Это моя оплошность в публикации кода.


А>вместо

А>void* Fn = mthdFunction;
А>надо
А>Function Fn = mthdFunction;

А>все компилируется, готов выслать проект, запускается все без проблем.


Да, но в таком случае в Вашем коде вообще не будет приведения указателя на метод класса к типу 'void*'!!! А это, напомню, и было основным камнем преткновения. Зачем же Вы, спрашивается, сообщали, что у Вас в коде прекрасно работает такое приведение, если на самом деле его у Вас в коде нет совсем? Это во-первых.

А во-вторых, Ваш код по прежнему использует только 32 бита указателя на метод — собственно адрес точки входа в метод

...
        __asm mov eax,Fn
        __asm call eax
...


Этого недостаточно для того, чтобы, например, корректно выполнить приведенный в моем предыдущем сообщении пример. Не затруднит ли Вас попробовать скомпилировать и выполнить этот пример и сообщить сюда, какой-же результат Вы получили?
Best regards,
Андрей Тарасевич
Re[6]: Указатель на функцию
От: DangerDen  
Дата: 22.12.04 11:26
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, Андрей Тарасевич, Вы писали:


АТ>>Век живи — век учись. На 32-хбитной платформе битовый размер легального указателя на метод класса всегда строго больше 32-х бит. Например, на платформе MSVC++ указатель на метод класса, корректно работающий в условиях множественного наследования имеет размер 64 бита. Это и есть та причина, по котрой вопросы приведения указателя на метод класса к типу 'void*' не заслуживают никакого серъезного рассмотрения.


К>Во! Точно! Я не мог вспомнить, в каких случаях получается 128-битный указатель... Виртуальное наследование + множественное наследование + виртуальные функции. Вуаля!


Можно поподробнее?
Что находится в каждых 32 битах? и как осуществляется вызов по этом 128 битному адресу?
Re[3]: Указатель на функцию
От: jazzer Россия Skype: enerjazzer
Дата: 22.12.04 11:51
Оценка:
Здравствуйте, Андрей Тарасевич, Вы писали:

АТ>Здравствуйте, jazzer, Вы писали:


J>>Боюсь, что мы сейчас увидим решение с ..., thunks в стиле ATL или еще что-нть непереносимое.


АТ>Решение, как видишь, уже опубликовано и включает банальное силовое (и, разумеется, некорректное) приведение указателя на метод к типу 'void*'. Гора родила мышь. Как и следовало ожидать, автор решения наивно полагает, что указатель на метод класса состоит только из адреса точки входа в метод


да.
я ожидал непереносимости между разными платформами (что-нть в стиле анализа стека, понимания, что же перед нами за указатель, и т.д.), а тут непереносимость уже между по-разному спроектированными классами и по-разному объявленными функциями даже в рамках одной платформы.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[7]: Указатель на функцию
От: Кодт Россия  
Дата: 22.12.04 12:16
Оценка:
Здравствуйте, DangerDen, Вы писали:

К>>Во! Точно! Я не мог вспомнить, в каких случаях получается 128-битный указатель... Виртуальное наследование + множественное наследование + виртуальные функции. Вуаля!


DD>Можно поподробнее?

DD>Что находится в каждых 32 битах? и как осуществляется вызов по этом 128 битному адресу?

Это внутреннее дело компилятора, вообще-то. И, честно сказать, я плохо представляю устройство. Могу только предположить.

Множественное наследование: требуется корректировка this для приведения к базовому типу.
struct A { int x; void a(); };
struct B { int y; void b(); };
struct C : A, B { int z; void c(); }
typedef void (C::*CF)();

int main()
{
  CF cf = &B::b;

  C* pc = new C;
  B* pb = pc; // явное приведение типа, с коррекцией адреса
  pb->b(); // коррекция не нужна
  pc->b(); // коррекция на стадии компиляции: неявное приведение к B*
  (pc->*cf)(); // откуда взять коррекцию? - только из cf
}

В принципе, компилятор мог бы завести прокси-метод
struct C: A, B
{
  int z;
  void call_b() { b(); } // коррекция указателя - здесь
};

int main()
{
  CF fb = &B::b; // компилятор подставит &C::call_b
}

Но дело в том, что этот шаг может быть опосредован:
typedef void (B::*BF)();

int main()
{
  BF bf = &B::b;
  .....
  CF cf = bf; // какой прокси подставить сюда?
}

С виртуальными функциями — аналогичная история. Необходимо различать методы, вызываемые статически и динамически.
struct A { int x; void a() {} };
struct B: A { int y; virtual void b() {} };
struct C: B { int z; virtual void b() {} };

typedef void (A::*FA)();
typedef void (B::*FB)();
typedef void (C::*FC)();

int main()
{
  B* pb = new C;

  FB fa = &A::a;
  FB fb = &B::b; // заметьте! мы здесь ничего не знаем про C

  pb->a(); // статический вызов
  pb->b(); // динамический вызов C::b

  (pb->*fa)(); // статический вызов
  (pb->*fb)(); // динамический вызов - обращаемся к vfptr/vtbl
}

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

Размер указателя будет зависеть от того, как устроены базовые классы.
Про 16 байт я, возможно, напутал (во всяком случае, сейчас не могу родить комбинацию, в которой это было бы так). Но 12 байт — стопудов.
Перекуём баги на фичи!
Re[2]: Указатель на функцию
От: Кодёнок  
Дата: 22.12.04 12:41
Оценка:
I>Для тех кто в танке Получить адресс функции метода класса одназначно можно, без всяких хаков. Стандартным синтакисом С++. Проверяно в VC7.1 код совместим работает как, в релиз так, и в дебаг версии с уникодом и без.

Как бывший танкист замечу, что только указатель на метод простого класса можно привести к void*, потому что они имеют одинаковый размер. Через union например, но это хак. А вот указатели сложных классов с множественным наследованием, виртуальными функциями имеют размер 8 или 12 байт. Соответственно, если старшие один-два дворда не равны нулю, то твой хак не будет работать.

Попробуй посмотреть на sizeof
#include <stdio.h>

struct simple_a { void A() { } };
struct simple_b : simple_a { void B() { } };

struct vt_a { virtual void vA() { } };
struct vt_b : vt_a { virtual void vB() { } };
struct vt_c : vt_a { virtual void vC() { } };

struct mi_a : vt_b, vt_c { void mA() { } };
struct mi_b : virtual vt_b, virtual vt_c { void mB() { } };

void print_dw(void *p, int sz)
{
    for (int i = sz/4 - 1; i >= 0; --i)
        printf("%08X ", (unsigned*)p + i);
    printf("\n");
}

template <class P>
union hack_cast
{
    P p_;
    void *v_;
    hack_cast(const P& p) : p_(p) { }
    operator void * () { return v_; }
};

template <class P>
void ptm(P p)
{
    print_dw(hack_cast<P>(p), sizeof(p) );
}

void main()
{
    typedef void (simple_b::*simple_p)();
    typedef void (vt_c::*vt_p)();
    typedef void (mi_a::*multiple_p)();
    typedef void (mi_b::*multiple_vt_p)();
    printf("%d\n", sizeof(simple_p));
    printf("%d\n", sizeof(vt_p));
    printf("%d\n", sizeof(multiple_p));
    printf("%d\n", sizeof(multiple_vt_p));

    ptm(&mi_a::mA);
    ptm(&mi_b::mB);
}


4
4
8
12
00401104 00401100
00401108 00401104 00401100
Re[8]: Указатель на функцию
От: _Winnie Россия C++.freerun
Дата: 22.12.04 12:41
Оценка: 2 (2)
К>Это внутреннее дело компилятора, вообще-то. И, честно сказать, я плохо представляю устройство. Могу только предположить.

Очень хорошая статья по поводу внутреннего устройства в разных компиляторах, никак времени не хватит ее полностью освоить
http://www.codeproject.com/cpp/FastDelegate.asp
Правильно работающая программа — просто частный случай Undefined Behavior
Re[3]: Указатель на функцию
От: Stepkh  
Дата: 22.12.04 13:20
Оценка:
Здравствуйте, Кодт, Вы писали:


К>
К>owner = 0012FF78, tag1 = FEEDFACE, func = 004011A0, tag2 = 00000000 <--- а где же дохлая корова?
К>Ellipsis is ...
К>... FEEDFACE
К>... 004011A0
К>... 00000000
К>... 00000004
К>... DEADBEEF         <--- а вот она!
К>


Не обижайте 0xDEADBEEFа
Re[3]: Указатель на функцию
От: Plague Россия  
Дата: 22.12.04 13:23
Оценка:
Помоему где-то на этом форуме (если мне не изменяет память, то господин Кодт) говорилось, что указатель на данные и указатель на код могут быт разной ширины. Так интересно, какой размер быдет у void*?
Re[4]: Указатель на функцию
От: Кодт Россия  
Дата: 22.12.04 14:35
Оценка: +1
Здравствуйте, Plague, Вы писали:

P>Помоему где-то на этом форуме (если мне не изменяет память, то господин Кодт) говорилось, что указатель на данные и указатель на код могут быт разной ширины. Так интересно, какой размер быдет у void*?


Дело в том, что код и данные обычно размещаются в разных местах, и необязательно одинаково адресуемых.

На линейке интеловских процессоров, начиная с i8086 (и далее, включая 16-, 24-, 32- и 64-битные) адрес состоит из пары селектор:смещение.
Если весь код или все данные умещаются в сегмент с одним селектором, то можно единожды загрузить его значение в соответствующий сегментный регистр, и далее оперировать только смещениями.
Указатель, содержащий смещение и "подразумевающий" селектора — называется ближним, near. Указатель, содержащий и селектор, и смещение — называется дальним, far.
Причём, для кода и для данных селекторы могут различаться. Отсюда возникли модели памяти
Но и это ещё не всё! Рабочий диапазон значений смещения тоже может варьироваться. Скажем, для IA-64 с плоской моделью можно ограничиться 32-битными смещениями для кода (из расчёта, что его объём — менее 4ГБ) и 64-битными — для кода.
Перекуём баги на фичи!
Re[4]: Указатель на функцию
От: Tom Россия http://www.RSDN.ru
Дата: 22.12.04 16:04
Оценка:
Вообще решаемая Вами задача давно решена. см boost::bind, boost::function
Народная мудрось
всем все никому ничего(с).
Re[3]: Указатель на функцию
От: Андрей Тарасевич Беларусь  
Дата: 22.12.04 17:10
Оценка: 4 (2)
Здравствуйте, Кодёнок, Вы писали:

I>>Для тех кто в танке Получить адресс функции метода класса одназначно можно, без всяких хаков. Стандартным синтакисом С++. Проверяно в VC7.1 код совместим работает как, в релиз так, и в дебаг версии с уникодом и без.


Кё>Как бывший танкист замечу, что только указатель на метод простого класса можно привести к void*, потому что они имеют одинаковый размер.


Нет, в компияоторе, который корректно реализует функциональность указателей на методы классов указатель на метод является "составным" и имеет по этой причне размер, больший чем 'void*'.

Кё>Через union например, но это хак. А вот указатели сложных классов с множественным наследованием, виртуальными функциями имеют размер 8 или 12 байт. Соответственно, если старшие один-два дворда не равны нулю, то твой хак не будет работать.


Но при этом надо заметить, что язык С++ позволяет использовать указатели "базовых" классов (путь они будут "простыми", т.е. без множественных предков) для указания на методы производных классов (пусть там появилось множественное наследование). По этой причине компиляторы всегда вынуждены использовать большие указатели на методы классов. Компиляторы, которые используют 32-хбитные указатели для указания на методы "простых" классов, работают с такими указателями некорректно. Примером является MSVC++, который в режиме "Best-case alwys" действиельно использует 32-битные указатели для методов "простых" классов. Не составляет никакого труда привести пример кода, который из-за этого будет работать некорректно. Для того, чтобы заставить MSVC++ работать с указателями корректно, надо лезть в установки компиляции и выставлять режим 'Any class' (или как его там). При этом указатели на методы сразу вырастут в размере , независимо от того, есть там множественное наследование или нет.
Best regards,
Андрей Тарасевич
Re: Указатель на функцию
От: Аноним  
Дата: 22.12.04 17:41
Оценка: :)
А немогли бы вы ответить на вопрос в топике (Начнем все сначала. Указатель на функцию) в чем там проблемы.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.