事件处理使用联合hack c++

Event handling using union hack c++

本文关键字:hack c++ 事件处理      更新时间:2023-10-16

我有两个类。假设其中一个是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:你应该粘贴更多的代码来给回答这个问题的人一个更好的想法