----- Original Message ----- From: Ashley Roll To: Sent: Sunday, December 15, 2002 12:22 AM Subject: Re: [EE]: function pointer syntax in C/C++ > Hi Sergio, > > I think I understand why you think it is too convoluted.. It does look like > it, but bear with me.. :) > > In the end there is a trade off between complexity and flexibility. In your > case you chose one way. I would have chosen a different. It really depends > on the amount of flexibility needed later (and sometimes this is not easy to > determine when you are writing the code). > > Please Note, I don't think you are wrong in using pointers like this. I'm > just trying to demonstrate an alternative that may be useful in another > project or just as a mental exercise ;) > > I would agree for the simple Light/Switch case that the pointer solution you > use is simpler. However, lets extend the example to handle an apparently > simple extra requirement: Hi Ash, Ok keeping this friendly and just to throw a little more light on pointers to methods. We should firstly show the onlooker one of the aspects that was bothering me when I said I thought it was too convoluted. That is, your switch can handle either an ON condition or an OFF condition. It cannot handle both. To do so you would need to add another intermediate messaging class LightOffCommand (if we follow your previous naming convention). Your lamp would need to know whether it is currently on and ignore requests to turn on or whether it is currently off and ignore requests to turn off. A real switch cannot be turned on if it is already on, or off if it is already off. So to model the switch accurately you would have to build the logic into the switch not the lamp (as I have in fact done). > > Staying with your electrical analogue, Lets say that we now have a motor > that moves something. It has a "position" that can be set. > > Now say we want to have 3 switches, "left", "mid", and "right". > > With the pointer method, we would need to implement one of two things: > > 1. Add functions to the motor class for "left" "mid" and "right". > 2. Allow the Switch to store a parameter to pass to a general "move" > function. > > Option 1 is doable, but really is adding extraneous stuff to the motor > class, do you keep extending this interface each time a new position is > needed. This is not very elegant, and arguably will cause a maintenance > nightmare in the future. If we have a motor with three input levels on it I would want it to be a different motor to one that only has an off/on input. I would only have a problem with the motor class if I tried to keep all my options open by adding on, off, left, mid, right, up, down, sideways, insideout blah blah blah to a motor base class. By using pointers to methods you can point to a method in one class that does not exist in another class and visa versa. Provided you apply the method to the right type of object there is no conflict, and you certainly don't have to worry about propagating virtual methods all over the place. e.g. class MOTOR : public MTASK { // NOTE the absense of ON OFF or HIGH methods } class MOTOR_ONE_SPEED : public MOTOR { void on(void); void off(void); } class MOTOR_TWO_SPEED : public MOTOR { void high(void); void on(void); void off(void); } Then if we continue with the simple switch which you first introduced class SWITCH { MTASK *obj; void (MTASK::*event_func)(void)); void attach(MTASK *, void (MTASK::*)(void)); void execute(int); } void SWITCH::attach(MTASK *obj, void (MTASK::*event_func)(void)) { SWITCH::obj = obj; SWITCH::event_func = event_func; } void SWITCH::execute(int new_val) { (obj->*event_func)(); } MOTOR_ONE_SPEED *m1 = new MOTOR_ONE_SPEED MOTOR_TWO_SPEED *m2 = new MOTOR_TWO_SPEED; SWITCH *sw1 = new SWITCH, *sw2 = new SWITCH, *sw3 = new SWITCH; sw1->attach(m1, &MOTOR_ONE_SPEED::on); sw2->attach(m2, &MOTOR_TWO_SPEED::on); sw3->attach(m2, &MOTOR_TWO_SPEED::high); > > Option 2 means that a special version of a switch is needed just for motors. > Alternatively switches could be extended to take an argument and return it > when invoking the function pointers. However then all the code needs to be > changes on all the object currently using the switch mechanism. Also this > would probable need to be handled as a void* so arbitrary data can be > passed. Now the issue is you have designed out type safety in your target > class interfaces, not a beneficial thing. There are also going to be issues > with memory ownership and freeing. No this is not the case. Yes I could easily have multiple types of switches with the pointer to method stuff and I would not need to use void * as an arbitrary data passing mechanism. I think the problem here is that you are locked into the virtual method mode of thinking. In which you have a pointer to a base class and you want to apply a method to an object of a derived class. You see the only way to access the method is by having a virtual instance in the base. Naturally all overloaded methods must take the same parameters. BUT if you scrub the virtual method and point directly to the real method, then the name becomes just a label AND two methods with the same name in two different classes CAN have different parameters and be accessed via a pointer to method without conflict. > > With the command pattern, to implement this new functionality is simple. > There are no changes to the Switch class.. > > class Motor > { > void Move(int position); > // etc. > }; > > class MoveMotorCommand : public Command > { > public: > // when constructed, we will need a target > MoveMotorCommand ( Motor *pTarget, int NewPosition ); > > virtual void Execute(); > private: > Motor *m_pTarget; > int m_NewPosition; > > }; > > MoveMotorCommand::MoveMotorCommand( Motor *pTarget, int NewPosition ) > { > m_pTarget = pTarget; > m_NewPosition = NewPosition; > } > > MoveMotorCommand::Execute() > { > pTarget->Move( m_NewPosition ); > } > > Then you can construct as many different switches with different motor > positions as you like: > > Motor TheMotor; > Switch MoveLeftSwitch; > Switch MoveRightSwitch; > Switch MoveMidSwitch; > Command *pCmd; > > pCmd = new MoveMotorCommand( &TheMotor, 0 ); > MoveLeftSwitch.AttachCommand( pCmd ); > > pCmd = new MoveMotorCommand( &TheMotor, 128 ); > MoveRightSwitch.AttachCommand( pCmd ); > > pCmd = new MoveMotorCommand( &TheMotor, 256 ); > MoveMidSwitch.AttachCommand( pCmd ); > > MoveMidSwitch.Invoke(); // when pressed > > Or perhaps you want to implement a switch that turns on a number of lights.. > or turns on a light and moves a motor.. You could implement a "macro" > command. You would attach arbitrary other commands to this macro command. It > would add them to an ordered list then when executed, it would execute each > one in turn. > > It really does depend on your needs. A GUI is a prime example of where this > can really be helpful. The same mechanism can be used for menus, tool > buttons, and a command parser. As an added benefit, it is possible to > implement special commands that don't really have a "target" object but > alter global state or create a new window or document etc.. All with the > same Command infrastructure. Ok, if we're going to talk about GUIs have a look at the XC GUI which forms the heart of the IPAD-Pro meta CASE tool (see http://www.xcprod.com ). This tool allows you to build fully interactive visual programming CASE tools for niche markets or in-house use. The interface that allows you to build these tools is itself also a fully interactive visual programming tool built using IPAD-Pro. It allows you to quickly and easily build dialogs and controls using the same kind of drag and drop approach as visual basic. ZMech (an interactive state machine development system) is built using IPAD-Pro (a PIC version of which is also available). And guess what, all this uses the power and flexibility of pointers to methods. > > Basically, it only looks convoluted until you need to implement some extra > functionality, then it makes it really easy and neat. > > Should we move this to [OT]? > I don't know that there is much more I can really say about this. > Cheers, > Ash. Best Regards Sergio http://www.xcprod.com -- http://www.piclist.com hint: The PICList is archived three different ways. See http://www.piclist.com/#archives for details.