typecasting on derived SEvents

You are an experienced programmer and have a problem with the engine, shaders, or advanced effects? Here you'll get answers.
No questions about C++ programming or topics which are answered in the tutorials!
Post Reply
psychophoniac
Posts: 101
Joined: Wed Dec 03, 2008 5:33 pm
Location: ger

typecasting on derived SEvents

Post by psychophoniac »

hey everyone.
im trying to write an graphics engine, that should be event based.
that means, everything that is done, is done by an event wich is sent out and interpreted.
i use SEvent as base class, derive an tCustomEvent from it and so on.
now i have a problem. these are the important functions:

Code To Send Out Event, a simple run function wich does only 20 frames, and the OnEvent function from IEventReceiver, wich should be used to interpret all the incoming events.

Code: Select all


void app::sendOutEvent(const tCustomEvent& ev)
{
    device->postEventFromUser(ev);
};

void app::run(void)
{
    s32 frames = 0;
    while(device->run() && ++frames < 20)
    {
      printf("FRAME %i\n", frames);

      sendOutEvent(tCustomEvent());
      sendOutEvent(tPrintStringEvent("TEST YEAH"));
    };
};

bool app::OnEvent(const SEvent& event)
{
    switch(event.EventType)
    {
        case EET_USER_EVENT:
                            {
                                tCustomEvent cEvent = (tCustomEvent&)event;
                                printf("CET = %i\n", cEvent.type);

                                switch(cEvent.type)
                                {
                                    case CET_BASE:
                                                  {
                                                      printf("CET_BASE event received!\n");

                                                      return true;
                                                  };break;
                                    case CET_PRINT_STRING:
                                                  {
                                                      tPrintStringEvent psEvent = (tPrintStringEvent&)cEvent;
                                                      printf("CET_PRINT_STRING event received!\n");
                                                      printf("String Received : ");
                                                      psEvent.printString();
                                                      printf("\n");

                                                      return true;
                                                  };break;
                                    default:
                                             return false;
                                };
                            };break;
        default : return false;
    };
    return false;
};


this is how i declare the tCustomEvent etc.:

Code: Select all

#ifndef CUSTOMEVENTS_H
#define CUSTOMEVENTS_H

#include "irrlicht.h"

using namespace irr;

enum customEventType
{
    CET_BASE = 1,
    CET_PRINT_STRING
};

class tCustomEvent : public SEvent
{
    public:
            tCustomEvent()
            {
                SEvent::EventType = EET_USER_EVENT;
                type = CET_BASE;
            };
            virtual ~tCustomEvent()
            {
            };

            customEventType type;
};

class tPrintStringEvent : public tCustomEvent
{
    public:
            tPrintStringEvent(const c8* string) : array(string)
            {
                tCustomEvent::type = CET_PRINT_STRING;
            };
            virtual ~tPrintStringEvent()
            {
                delete array;
            };

            inline const c8* getString(void)
            {
                return array;
            };

            inline void printString(void) const
            {
                printf(array);
            };

    private:
            const c8* array;
};

#endif //CUSTOMEVENTS_H

at least, i can see wether the incoming event is one of mine or some gui or sth.
i can even see, wich type of tCustomEvent it is (this is a simplified piece of the whole code :P ), but the problem is, somehow the typecast fails.
the string wich is printed is all 20 frames the same, but not the one i gave to the sended event.

i have an 64-bit vista, maybe the problem has something to do with that?
please help me out peolpe i'm relly stuck on this one and it is important to me!!
i love skateboarding!
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

When the OnEvent method is called, a temporary object is created, which is of type SEvent. AFAIK there is no way to recover your data, only the part that fits into the SEvent type will be used.
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

There are a few issues with your code. The following line makes a temporary tCustomEvent, which slices off the additional data members of the derived class.

Code: Select all

tCustomEvent cEvent = (tCustomEvent&)event;
This is fine and dandy until you later try to cast that value to a derived class.

Code: Select all

tPrintStringEvent psEvent = (tPrintStringEvent&)cEvent;
Since cEvent is actually of type tCustomEvent and not tPrintStringEvent, the cast is actually illegal. You're lying to the compiler, and it will exact revenge. The simple solution would be to avoid making copies of the event, and just cast to and keep the appropriate reference type...

Code: Select all

const tCustomEvent& cEvent = (const tCustomEvent&)event;
//...
const tPrintStringEvent& psEvent = (const tPrintStringEvent&)event;
An even better solution would be to avoid the big switch block with all of the casting. You can usually do this pretty easily with a virtual function...

Code: Select all

class tCustomEvent : public SEvent
{
public:
  tCustomEvent() { EventType = EET_USER_EVENT; }
  // virtual destructor should not be necessary because nobody holds
  // a base pointer to a drived event. all events have automatic storage.
  virtual bool doSomethingCustom(IrrlichtDevice*) const {
    return false;
  }
};

class tPrintStringEvent : public tCustomEvent
{
public:
  tPrintStringEvent(const char* s) : str_(s) {}
  // no destructor needed, lifetime of s is longer than the instance
  // that uses it.
  virtual bool doSomethingCustom(IrrlichtDevice*) const {
    printf ("%s\n", s);
    return true;
  }
private:
  cont char* str_;
};


bool app::OnEvent(const SEvent& ev)
{
  if (event.EventType == EET_USER_EVENT)
  {
    const tCustomEvent& custom = (const tCustomEvent&)ev;
    return custom.doSomethingCustom(device);
  }

  return false;
}
Another problem is that your tPrintStringEvent destructor is calling delete on a pointer that wasn't allocated with new.

The final problem is that you call printf() passing an argument string directly. This is dangerous. It is best to supply the format specifier explicitly to prevent the function from trying to read arguments that aren't provided.

Travis
psychophoniac
Posts: 101
Joined: Wed Dec 03, 2008 5:33 pm
Location: ger

Post by psychophoniac »

goddamn.
casting from the original event did the trick :

Code: Select all

tPrintStringEvent psEvent = (tPrintStringEvent&)event;
now the string is printed correctly.
thanks for your help on this !

another question, can i solve the printf() problem by simply using the irrlicht logger?

and i used your code example for now, seems quite usable to me, thanks !
but i left the virtual destructors, since my compilers keeps on complaining that i have virtual functions, but no virtual destructors... (gcc32 + code::blocks).
i love skateboarding!
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

psychophoniac wrote:goddamn.
casting from the original event did the trick :

Code: Select all

tPrintStringEvent psEvent = (tPrintStringEvent&)event;
Yes, this will work, but you have to be careful to not use psEvent to cast to another derived type again. This is why it is best to make psEvent a reference.

Travis
psychophoniac
Posts: 101
Joined: Wed Dec 03, 2008 5:33 pm
Location: ger

Post by psychophoniac »

okay, i tried the method without the switch block and it works even better, i can now easily set up what i want, i now have a header for every event class, so it is also easy to handle (also include) and extend.
thanks again guys!
i love skateboarding!
Post Reply