DalekSupreme
DalekSupreme

Reputation: 1513

Event handling using union hack c++

I have hot two classes. Assume that one of them is an Image and the other is a Page. The Image object receives an OnPointerPressed event and When it happens I would like to call a function form the Page calss. The problem is that this function is not defined until I pass the address of the member function to the image using union hack. When this method is called from the Image class it works fine but inside the method I can't see the Page Object and its members.

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

Can anybody help a little?

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

Upvotes: 0

Views: 367

Answers (2)

Captain Obvlious
Captain Obvlious

Reputation: 20063

In both the assembly language version and the non-assembly language version of your code you are calling the member function incorrectly. There are specific requirements for member functions that do not apply to calling a free function (and static member functions) which is exactly what both versions of your code are doing. When calling non-static member functions the this pointer is implicitly passed as the first parameter. This can be done by either pushing the pointer value onto the stack, placing it in a register, or both - it really depends on how the compiler generates the calling code.

The current version of your code attempts to call the member function as if it was a free function but passes the object pointer on the stack. This is a very bad idea and is the reason why the value of this is incorrect. It also may not work as you expect if ARG is an object being passed by value instead of by reference or pointer. This is because the entire object needs to be copied onto the stack - don't even try doing this yourself. Instead you should use the features provided by the language that are specifically intended to allow you to call member functions by pointer.

The syntax for a pointer to member function is returntype (ClassType::*)(parameters). By using the correct syntax you can rely on the compiler to tell you when you use it incorrectly instead of trying to hunt down obscure bugs like you are currently doing. Not only should you use this when calling the member function but you should use it to store the pointer in your list. VC++ allows you to convert a function pointer to a void pointer but this is a Microsoft extension to the language and is not standard C++. I recommend you avoid the void whenever possible.

The one change you will need to make is how you are calling the member function. This is different than using a pointer to a free function and the following shows the correct syntax.

(objectpointer->*functionpointer)(arguments);

The extra set of parenthesis is required due to operator precedence and the ->* is the "pointer to member" operator. The code below shows the changes you will need to make to your code to correctly call the member function.

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

You can make this more generic by adding an additional template parameter to specify the type of the target object.

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

.

As a side note the function add() contains undefined behavior. From $9.5/1 of the C++ Language Standard

In a union, at most one of the data members can be active at any time, that is, the value of at most one of the data members can be stored in a union at any time.

In add() you set the value of t then read the value of v_ptr.

Upvotes: 1

dev_nut
dev_nut

Reputation: 2542

You need to pass in the 'this' pointer as well to make this work, otherwise it would instantiate a separate instance of the class:

You have to modify the imageMousePressed like this:

// 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;
    }

Now, this would mean the add function of OnPointerPressedHandler should also be modified to accomodate this:

    image1->OnPointerPressedHandler->add(&MainPage::imageMousePressed, this);

And internally it should pass call the pointed function passing the 'this' value. The reason this does not work now, if the function is instantiated like a static function which would then be a part of a new object, so the state of your old object would not be preserved.

PS: You should paste more code to give a better idea to folks who answer this

Upvotes: 0

Related Questions