I'm creating a set of classes that mimic standard VC controls (such as command buttons, etc.) on the console. My base class, ConsoleObject, works beautifully. I added a few function pointers that can be set to handle events:
Users could set the function pointers to their own function. However, when a derived class, WindowTitleBar, tries to set the function pointers, I get the following compile error:Code:void (*onFocusEventHandler)(void); void (*onLoseFocusEventHandler)(void); void (*onClickEventHandler)(unsigned __int32 mouseX,unsigned __int32 mouseY,unsigned __int8 button); void (*onDoubleClickEventHandler)(unsigned __int32 mouseX,unsigned __int32 mouseY,unsigned __int8 button); void (*onMouseOverEventHandler)(unsigned __int32 mouseX,unsigned __int32 mouseY); void (*onMouseOutEventHandler)(unsigned __int32 mouseX,unsigned __int32 mouseY); void (*onDestroyEventHandler)(void);
cannot convert from 'void (__thiscall WindowTitleBar::*)(unsigned __int32,unsigned __int32,unsigned __int8)' to
'void (__cdecl *)unsigned __int32,unsigned __int32,unsigned __int8)'
Typecasting didn't work. I tried using template classes, but then the WindowTitleBar can't set the handler functions (which belong to WindowTitleBar) to the command buttons (also derived from ConsoleObject)that they're for. I get the following error:
cannot convert from 'void (__thiscall WindowTitleBar::* )(unsigned __int32,unsigned __int32,unsigned __8)' to
'void (__thiscall CommandButton::* )(unsigned __int32,unsigned __int32,unsigned __8)'
In addition, when a derived class Window tries to instantiate a variable of type WindowEventHandler which is not derived from ConsoleObject, I get illogical compile errors:
Causes the error:Code:eventHandler = new WindowEventHandler(objects,this); /* Note that WindowEventHandler has a constructor: WindowEventHandler(ConsoleObject<Object> **objList,ConsoleObject<Window> *parent) */
'WindowEventHandler::WindowEventHandler(ConsoleObj ect<objClass> **,ConsoleObject<Window> *)' :
cannot convert parameter 1 from 'ConsoleObject<objClass> **' to 'ConsoleObject<objClass> **'
Last time I checked, ConsoleObject<objClass> ** and ConsoleObject <objClass> ** are the same thing.
Can someone please help me? I know this is confusing, and real GUIs are much more efficient, but I just like doing things myself and this is a major obstacle for me. I'm attaching the source code to this post. (It's a header (.h) file, but I had to change the extension to .txt to upload it.)
I have a bad habit of not documenting my code very well, so if you need further clarification, just let me know.)
Thanks in advance.
Last edited by dargueta; 10-17-2007 at 07:08 PM.
Had a quick look at your code. Correct me if I'm wrong, but function members can only point to function members of the same base class or the same concrete class, and minimize and titlebar are different concrete classes, therefore this is invalid as minimize and titlebar point to different classes.
minimize->onClickEventHandler = &TitleBar::minimizeWindow;
That's what I was trying to work around. Is there any way to typecast the function to force it to work? I read something somewhere that there's a way to force the __thiscall declarator to disappear by using something like this.this->foo(). It didn't seem to help.
I don't think you can. Even if it does work, it's not a safe cast. The behaviour would be unpredictable depending on the compiler. And that's bad! Best way to approach this is to use a different design. Polymorphic functions would work better.
I want the user to be able to specify their own functions for the handlers, so __declspec(__stdcall) __cdecl pointers would be ideal. The problem is that classes derived from ConsoleObject use the __thiscall calling convention. I'm guessing I'd use a template for the argument types...? Perhaps something like this is what you meant?
Code:template <typename funcPtrType> void onMouseOver(funcPtrType (*p)(void)) { if(p != NULL) (*p)(); else //default handler goes here }
In my last assignment I encountered something like your discussing.
I ended up creating a virtual base class that contained only public event functions; Basically, an interface class. Just remember to use a virtual destructor. If a class needed to handle those events, it would have to inherit the interface. For the class that called the event functions, I created a pointer (of the Interface type) and assigned it to an event handler object (instance of a derived class of the Interface Class). Then I called (raised) the event through the pointer. Like:
You can assign an InterfaceClass * to any class that inherits the Interface class. You can used a linked list of Interface class pointers if you need add eventhandlers during run-time.Code:InterfaceClass * ptrInterfaceClass = new EventHandlingClass; ptrInterfaceClass->SomeEvent();
I should note that I never added asynchronous execution to the assignment, and I'm not sure how that would effect the design I used.
I hope that helps.
Do you mean an abstract class? I've never done that; could you show me a simple example, please? I'm a more visual person.
You'd think after programming in C++ for four years I'd have come across this before...
Sure. I wrote this for a scenario that includes a button. When the button is pressed, it raises an event.
The abstract class is called IButtonEvents. It is the interface between the Button class and, in this instance, the SomeEventHandler class. Here is the header for IButtonEvents:
Since I used a virtual destructor, I must provide implementation for it. Here is IButtonEvents.cpp:Code:#pragma once class IButtonEvents { public: virtual ~IButtonEvents(); // Virtual Destructor. // Purpose: Function declaration for function thats represents an event. Must be overridden in // a derived class. //--------------------------------------------------------------------------------------------------- virtual void _OnPress() = 0; // Is raised when a button is pressed. };
I just created a class called SomeEventHandler that inherits the interface, overrides the _OnPress function and provides implementation. Here is the header for SomeEventHandler:Code:#include "IButtonEvents.h" IButtonEvents::~IButtonEvents() { }
The implementation for _OnPress just cout's the memory address of the object and identifies the function. Here is the source for SomeEventHandler:Code:#pragma once #include <iostream> #include "IButtonEvents.h" using namespace std; class SomeEventHandler : public IButtonEvents { private: void _OnPress(); // The overidden function of the inherited virtual function. // AKA, the EventHandler. };
For the Button class, I used a linked list of pointers, like I was talking about in the previous post. You can ignore that code. The only thing worth noting is the function AddEventHandler. Notice that an implicit conversion takes place in the parameters. Here is the header for the Button class:Code:#include "SomeEventHandler.h" void SomeEventHandler::_OnPress() { // Display a response message identifying the object and the event. cout << "Object at " << this << " responding to _OnPress event." << endl; }
There isn't much worth noting in the source for this class, just that it calls the _OnPress function from the IButtonEvents class. Here is the source for the Button class:Code:#pragma once #include "IButtonEvents.h" class Button { private: struct EventHandlerRecord // The record structure for the table of EventHandlers { IButtonEvents * Object; // Pointer to an EventHandling object EventHandlerRecord * NextRecord; // Pointer to the next record }; EventHandlerRecord * FirstRecord; // The pointer to the first record EventHandlerRecord * LastRecord; // The pointer to the last record public: Button::Button(); // Default Constructor Button::~Button(); // Default Destructor // Parameters: A pointer to an object derived from the IButtonEvent class. // Purpose: Adds a record the EventHandler table. //----------------------------------------------------------------------------------- void AddEventHandler(IButtonEvents * EventHandlerObject); // Purpose: Raises the _OnPress event function //----------------------------------------------------------------------------------- void Press(); };
Finally, here is main.cpp:Code:#include "Button.h" Button::Button() { /* ===Initialize pointers=== */ Button::FirstRecord = 0; Button::LastRecord = 0; } Button::~Button() { /* ===Destroy the EventHandler table=== */ // create a cursor Button::EventHandlerRecord * cursor; // while a record exists . . . while (Button::FirstRecord != 0) { // . . . point the cursor to the first record cursor = Button::FirstRecord; // move the FirstRecord to the next record Button::FirstRecord = Button::FirstRecord->NextRecord; // delete the record at the cursor delete cursor; } } void Button::AddEventHandler(IButtonEvents * EventHandlerObject) { // Create a cursor Button::EventHandlerRecord * cursor; // Create a new record cursor = new Button::EventHandlerRecord; // Set the Object field cursor->Object = EventHandlerObject; // If the first record hasn't been assigned yet . . . if (FirstRecord == 0) { // . . . make the new record the first record Button::FirstRecord = cursor; } // Else . . . else { // . . . append the record to the last record Button::LastRecord->NextRecord = cursor; } // Make the new record the last record cursor->NextRecord = 0; Button::LastRecord = cursor; } void Button::Press() { /* ===Call the _OnPress function for each object in the EventHandler table=== */ // Create a cursor Button::EventHandlerRecord * cursor; // Start at the first record cursor = Button::FirstRecord; // While the cursor points to a record . . . while (cursor != 0) { // . . . Call the _OnPress function for that object cursor->Object->_OnPress(); // Move to the next record cursor = cursor->NextRecord; } }
That's it. I believe it is a fail-safe design, excluding multiple inheritance. To support aysnchronous calls wouldn't take much work. The only drawback is that an EventHandling class has to inherit the interface, which means they must be designed to handle the events, and can't be bound during run-time. Another layer of abstraction could solve that.Code:#include <iostream> #include "Button.h" #include "SomeEventHandler.h" using namespace std; int main() { Button myButton; // Button object SomeEventHandler * EventHandler1 = new SomeEventHandler; // An event handler object SomeEventHandler * EventHandler2 = new SomeEventHandler; // Another event handler object // Add the EventHandlerObjects to myButton's EventHandler table myButton.AddEventHandler(EventHandler1); myButton.AddEventHandler(EventHandler2); // Press the button myButton.Press(); // Wait for the new line character cin.get(); // Destroy the EventHandler objects delete EventHandler2; delete EventHandler1; return 0; }
I went with some of your suggestions, and it seems to work all right. Now I have a new problem. In my base class, ConsoleObject, there is a pointer variable called parent that is of type Window, which is declared later on in the file. The compiler chokes because it doesn't recognize the type. How can I work around this?
Is Window a derived class of ConsoleObject? Could you use an implicit cast here?
There are currently 1 users browsing this thread. (0 members and 1 guests)
Bookmarks