使用union hack c ++进行事件处理

时间:2013-06-02 07:12:21

标签: c++ function events pointers unions

我有两门课。假设其中一个是Image,另一个是Page。 Image对象接收一个OnPointerPressed事件,当它发生时,我想从Page calss调用一个函数。问题是直到我使用union hack将成员函数的地址传递给图像时才定义此函数。当从Image类调用此方法时,它工作正常但在方法内部我看不到页面对象及其成员。

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());
    }
}

2 个答案:

答案 0 :(得分:1)

在汇编语言版本和代码的非汇编语言版本中,您正在调用成员函数 。成员函数的特定要求不适用于调用自由函数(和静态成员函数),这正是代码的两个版本正在执行的操作。调用非静态成员函数时,this指针将作为第一个参数隐式传递。这可以通过将指针值推入堆栈,将其放入寄存器或两者来完成 - 这实际上取决于编译器如何生成调用代码。

当前版本的代码尝试调用成员函数,就像它是一个自由函数一样,但是传递堆栈上的对象指针。这是一个非常错误的想法,也是this的值不正确的原因。如果ARG是通过值而不是通过引用或指针传递的对象,它也可能无法正常工作。这是因为需要将整个对象复制到堆栈中 - 甚至不要自己尝试这样做。相反,您应该使用特定语言提供的功能,以允许您通过指针调用成员函数。

指向成员函数的指针的语法是returntype (ClassType::*)(parameters)。通过使用正确的语法,您可以依赖编译器在错误地使用它时告诉您,而不是像您当前那样尝试查找模糊的错误。调用成员函数时不仅应该使用它,还应该使用它来将指针存储在列表中。 VC ++允许您将函数指针转换为void指针,但这是该语言的Microsoft扩展,并且不是标准C ++。我建议您尽可能避免使用void

您将需要进行的一项更改是您调用成员函数的方式。这与使用指向自由函数的指针不同,以下显示正确的语法。

(objectpointer->*functionpointer)(arguments);

由于operator precedence而需要额外的括号集,而->*是“指向成员的指针”运算符。下面的代码显示了您需要对正确调用成员函数的代码所做的更改。

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()包含undefined behavior。从$ 9.5 / 1的C ++语言标准

  

在联盟中,最多其中一个数据成员可以随时处于活动状态,也就是说,最多一个数据成员的值可以随时存储在一个联合中

add()中设置t的值,然后读取v_ptr的值。

答案 1 :(得分:0)

你需要传递&#39;这个&#39;指针也可以使这个工作,否则它会实例化一个单独的类实例:

您必须像这样修改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);

在内部它应该通过调用指向函数传递&#39;这个&#39;值。这个现在不起作用的原因,如果函数被实例化为静态函数,然后它将成为新对象的一部分,那么旧对象的状态将不会被保留。

PS:您应该粘贴更多代码,以便更好地了解回答此问题的人