Затирается адрес возврата в колбеке
От: ak239  
Дата: 15.12.13 11:06
Оценка:
Добрый день.

Существует библиотека (AntTweakBar), которая позволяет передавать указатель на функцию, которая должна вызываться для получения значения.
Функция имеет следующий typedef:
typedef void (__stdcall * TwGetVarCallback)(void *value, void *clientData);

Внутри своей dll я передаю внутрь этой функции — соответствующую статическую функцию класса:
static void TW_CALL GetData(void* val, void* clientdata);


Все работает, если реализовать функцию следующим образом:
void TW_CALL ATBVar::GetData(void* val, void* clientdata)
{
    *static_cast<GLfloat*>(val) = 1.0;
}

Все не работает, если реализовать эту функцию:
void TW_CALL ATBVar::GetData(void* val, void* clientdata)
{
    static_cast<ATBVar*>(clientdata)->get(clientdata);
}

В данном случае в clientdata сохранен указатель на производный класс, а функция get является виртуальной функцией базового класса.
Внутри библиотеки вызов функции осуществляется следующим образом:
if( UseGet ){
  std::cout << "ValueToDouble() 0.0" << std::endl;
  m_GetCallback(&Val, m_ClientData);
  std::cout << "ValueToDouble() 0.1" << std::endl;
}

Без вызова виртуальной функции — все работает нормально, т.е. выводятся обе отладочные строки.
С вызовом виртуальной функции — выводится только первая строка и работа AntTweakBar становится абсолютно некорректной.
Я предполагаю, что затирается код возврата из статической функции при вызове виртуальной функции. Если это действительно так, то как быть в этой ситуации? Либо в чем может быть дело?

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

17.12.13 15:38: Перенесено из 'C/C++'
Re: Затирается адрес возврата в колбеке
От: ak239  
Дата: 15.12.13 11:26
Оценка:
Ошибка была в:
void TW_CALL ATBVar::GetData(void* val, void* clientdata)
{
    static_cast<ATBVar*>(clientdata)->get(clientdata);
}

Из-за невнимательности
Тему можно закрывать.
Re: Затирается адрес возврата в колбеке
От: Кодт Россия  
Дата: 16.12.13 09:07
Оценка: +2
Здравствуйте, ak239, Вы писали:

A>В данном случае в clientdata сохранен указатель на производный класс, а функция get является виртуальной функцией базового класса.


Несмотря на то, что ошибка найдена и исправлена, — хочу обратить внимание на одну грабельку, которая здесь не сработала, но, в принципе, может сработать.
Дело в возможном смещении баз.
struct A { int x; };
struct B { int y; virtual void f() {} };
struct C { int z; virtual void g() {} };
struct D : A, B, C { int t; };

int main()
{
    D d;
    D* pd = &d;
    C* pc = pd;
    std::cout << static_cast <void*>(pd) << std::endl; // 0x22fd50
    std::cout << dynamic_cast<void*>(pc) << std::endl; // 0x22fd50 -- отдаёт указатель на первую базу с виртуальными функциями, даже из указателя на другую
    std::cout << static_cast <A*>   (pd) << std::endl; // 0x22fd5c -- базы A и B переставлены местами
    std::cout << static_cast <B*>   (pd) << std::endl; // 0x22fd50
    std::cout << static_cas t<C*>   (pd) << std::endl; // 0x22fd60 -- база C смещена
}

Поэтому
void foo(void* param)
{
    C* pc = static_cast<C*>(param);
}

void bar(D* pd)
{
    // неправильно (приведение по умолчанию, между прочим!)
    foo(pd); // D* -> void* -> C*

    // неправильно (всякие рукодельные способы)
    foo(static_cast<void*>(pd)); // D* -> void* -> C* (то же, что и неявное)
    foo(dynamic_cast<void*>(pd)); // D* -> B* -> void* -> C*

    // правильно
    foo(static_cast<C*>(pd)); // D* -> C* -> void* -> C*
}

либо, если в искомой базе заведомо есть виртуальные функции,
void foo(void* param)
{
    C* pc = dynamic_cast<C*>(param);
}

void bar(D* pd)
{
    foo(pd); // D* -> void* -> C*
    foo(dynamic_cast<void*>(pd)); // D* -> B* -> void* -> C*
    foo(static_cast<C*>(pd)); // D* -> C* -> void* -> C*
}
http://files.rsdn.org/4783/catsmiley.gif Перекуём баги на фичи!
Re[2]: Затирается адрес возврата в колбеке
От: uzhas Ниоткуда  
Дата: 16.12.13 11:15
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Поэтому


я бы еще в качестве правильного варианта предложил такое:

void bar(D* pd)
{
  foo(pd); // D* -> void*
}

void foo(void* param)
{
  C* c = static_cast<D*>(param); // void* -> D* -> (implicit static_cast) -> C*
}


руководствуясь правилом: делай static_cast из void* только на тот тип, который реально передается (сам придумал, много не бейте)
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.