事件处理使用联合hack c++
Event handling using union hack c++
我有两个类。假设其中一个是Image,另一个是Page。图像对象接收一个OnPointerPressed事件,当它发生时,我想调用Page类的函数。问题是,直到我使用union hack将成员函数的地址传递给图像时,才定义该函数。当这个方法从Image类调用时,它工作得很好,但在方法内部,我看不到Page Object和它的成员。
MainPage::MainPage(void)
{
//.......
image1->OnPointerPressedHandler->add(&MainPage::imageMousePressed);
}
void MainPage::imageMousePressed(STObject* sender, STPointerEventArg * args)
{
void * dsda = this; //the address of this is something wierd
pres = true;
}
有谁能帮帮我吗?
template<class T>
void add(T const & x)
{
union hlp_t{
void * v_ptr;
T t;
};
hlp_t a;
a.t = x;
functions->addLast(a.v_ptr);
}
template<class ARG>
void trigger(SENDERTYPE sender, ARG arg)
{
STLinkedListIterator<void *>* it = functions->createIteator();
if(it != nullptr)
{
do
{
void (*callback)(SENDERTYPE,ARG) =static_cast<void (*)(SENDERTYPE,ARG)>(it->getData());
callback(sender,arg);
/*
void * myVoid = it->getData();
__asm
{
push [arg];
push [sender];
call [myVoid];
}
*/
it->next();
}while(!it->EOL());
}
}
在代码的汇编语言版本和非汇编语言版本中,您都错误地调用了成员函数。成员函数有一些特定的要求,这些要求不适用于调用自由函数(和静态成员函数),而这正是两个版本的代码所做的。当调用非静态成员函数时,this
指针隐式地作为第一个参数传递。这可以通过将指针值压入堆栈,将其放入寄存器,或两者兼而有之来实现——这实际上取决于编译器如何生成调用代码。
当前版本的代码试图调用成员函数,就像它是一个自由函数一样,但在堆栈上传递对象指针。这是一个非常坏主意,也是this
值不正确的原因。如果ARG
是一个通过值而不是通过引用或指针传递的对象,它也可能不像您期望的那样工作。这是因为需要将整个对象复制到堆栈中——甚至不要自己尝试这样做。相反,您应该使用语言提供的特性,特别是旨在允许您通过指针调用成员函数。
指向成员函数的指针的语法是returntype (ClassType::*)(parameters)
。通过使用正确的语法,您可以依赖编译器告诉您何时使用错误,而不是像您目前所做的那样试图寻找模糊的错误。你不仅应该在调用成员函数时使用它,还应该使用它来将指针存储在列表中。vc++允许您将函数指针转换为void指针,但这是该语言的Microsoft扩展,并且是而不是标准c++。我建议你尽可能避免使用void
。
需要做的一个更改是如何调用成员函数。这与使用指向自由函数的指针不同,下面显示了正确的语法。
(objectpointer->*functionpointer)(arguments);
由于操作符优先级和->*
是"指向成员的指针"操作符,需要额外的括号。下面的代码显示了您需要对代码进行的更改,以便正确地调用成员函数。
template<class ARG>
void trigger(STObject* sender, ARG arg)
{
STLinkedListIterator<void *>* it = functions->createIteator();
if(it != nullptr)
{
do
{
// cast to a pointer to member function so the compiler knows
// the correct type of the function call.
void (STObject::*callback)(ARG)
= static_cast<void (STObject::*)(ARG)>(it->getData());
// call it using the correct syntax and let the compiler
// handle all the gory details.
(sender->*callback)(arg);
it->next();
}while(!it->EOL());
}
}
您可以通过添加一个额外的模板参数来指定目标对象的类型,从而使其更通用。
template<class SENDER, class ARG>
void trigger(SENDER* sender, ARG arg)
{
STLinkedListIterator<void *>* it = functions->createIteator();
if(it != nullptr)
{
do
{
void (SENDER::*callback)(ARG)
= static_cast<void (SENDER::*)(ARG)>(it->getData());
(sender->*callback)(arg);
it->next();
}while(!it->EOL());
}
}
。
作为旁注,函数add()
包含未定义行为。c++语言标准$9.5/1起
在union中,在任何时候最多只能有一个数据成员处于活动状态,即在任何时候最多只能有一个数据成员的值存储在union中。
在add()
中设置t
的值,然后读取v_ptr
的值。
您还需要传入'this'指针以使此工作,否则它将实例化类的单独实例:
你必须像这样修改imageMousePressed:
// add in a void* argument
void MainPage::imageMousePressed(STObject* sender, STPointerEventArg * args, void* mainPageObject)
{
MainPage* dsda = (MainPage*) mainPageObject; // now, you should have a pointer to the valid object
pres = true;
}
现在,这将意味着OnPointerPressedHandler的add函数也应该被修改以适应:
image1->OnPointerPressedHandler->add(&MainPage::imageMousePressed, this);
在内部,它应该传递调用传递'this'值的指向函数。现在不能工作的原因是,如果函数像静态函数一样被实例化,那么它将成为新对象的一部分,因此旧对象的状态将不会被保留。
PS:你应该粘贴更多的代码来给回答这个问题的人一个更好的想法