[comp.lang.c++] C++ Limitation

allgood.ingr.com (Greg Allgood) (05/14/91)

Hello,

   I apologize if you have seen multiples of this article - you know how 
things can happen.

   I would like to have the capability of telling some object A, to send
message M to an object B where M and B are not specifically known to A.
For example, I would like to create a ScrollBar and tell it "Hey, when 
you get scrolled to a new location, send a 'move_to' message to this
instance of class View".  I can do this in C++ if the ScrollBar class knows
about the View class, but I want to create a generic ScrollBar which
can send messages to classes which will be defined later.  

   I believe this is a necessary capability to capture the full power of 
re-use.  As another example, consider the items found in a dialog box. In
some toolkits whenever an item (such as a radio button) is activated, there
would be some common routine called to handle the event.  The id of the
item would be passed to the routine and a switch statement would be
inside to handle events for that specific item.  I would like to get away
from this approach because there is a loss of information taking place.
When an item is activated in some way, it knows about it and should know
what to do by itself.  By calling some common routine with a switch 
statement, I have to figure out what I knew already.  I would like to create 
the dialog box and tell all of the items what they should do. Then all I 
have to do is display the dialog box and everything just works.  This gives
me some "slick" re-use opportunities as long as the arguments match 
between the objects which I am hooking up.


   Now obviously I would like to modify C++ to allow me to do this, but I
have not come up with the way yet.  Note that this does not require
dynamic type checking although that would be a way to do it.  I think that
all I really need is some generic types for Objects and Messages.  I have
found a rather un-ellegant way using pointer-to-member functions.  However,
I am not sure how portable this method is.  Basically, all objects which
need to be hooked to something would have to derive off of an empty base
class.  Unfortunately, I have to do a cast.  Here is a simplistic example:

--------------

#include <stdio.h>
#include <iostream.h>

class Recipient
{
   virtual void VirtualTableKludge() {}
};

class View : public Recipient
{
   public:
      View() {}
      void moved(int dp)
	 {cout << "View moved by " << dp << endl;}
      virtual void moved_to(int p)
	 {cout << "View moved to " << p << endl;}
};

class ScrollBar
{
   protected:
      int t;
      Recipient *obj;
      void (Recipient::*moved_to)(int t);
      void (Recipient::*moved)(int dt);

   public:
      ScrollBar(int _t) { t = _t; }

      void when_moved(Recipient *_obj, void (Recipient::*_moved_to)(int t),
		      void (Recipient::*_moved)(int dt))
	{ obj = _obj; moved_to = _moved_to; moved = _moved; }

      void move(int dt)
	{ t += dt;
	  if (obj != NULL) (obj->*moved)(dt);
	}
      void move_to(int _t)
	{ t = _t;
	  if (obj != NULL) (obj->*moved_to)(t);
	}
};

main()
{
   ScrollBar sb(5);
   View v;

   sb.when_moved(&v, (void (Recipient::*)(int)) &View::moved_to,
   		 (void (Recipient::*)(int)) &View::moved);

   
   sb.move(-2);
   sb.move_to(10);
}

--------------

   With AT&T C++ release 2.1, this works and prints out:

View moved by -2
View moved to 10

   I would greatly appreciate any comments or reasons why the above method 
is non-portable.  I know that I am depending on the way pointer-to-member
functions are done with AT&T, and I wonder how safe that is.

--
______________________________________________________________________________
                                |
    Greg Allgood                | John 14:6
    Intergraph Corporation      |
    Huntsville, Alabama         |    Jesus saith unto him, I am the way,
    Mail Stop IW17A2            |  the truth, and the life: no man cometh
                                |  unto the Father, but by me.
    (205) 730-7052              |  
    allgood@greg.b17a.ingr.com  |  
				|  
_______________________________/ \____________________________________________

jgro@lia (Jeremy Grodberg) (05/20/91)

In article <1991May14.155950.4336@infonode.ingr.com> allgood@greg.b17a.ingr.com writes:
>Hello,
>
>   I apologize if you have seen multiples of this article - you know how 
>things can happen.
>
>   I would like to have the capability of telling some object A, to send
>message M to an object B where M and B are not specifically known to A.
>For example, I would like to create a ScrollBar and tell it "Hey, when 
>you get scrolled to a new location, send a 'move_to' message to this
>instance of class View".  I can do this in C++ if the ScrollBar class knows
>about the View class, but I want to create a generic ScrollBar which
>can send messages to classes which will be defined later.  

This is what multiple inheritance is for.  After you write your ScrollBar
class without any specific recipient class in mind, the class will end
up with a bunch of messages it wants to send.  In order to be sure that
those messages are all understood by the recipient, you must ensure that
the recipient has member functions for all of them.  The way to do
this in C++ is to create a class, say ScrollBarReceiver, which has
virtual member functions for all the messages that ScrollBar sends.  Then
any class that wants to be controlled by a ScrollBar can inherit from
that class, in addition to any other classes it wants, and override
the functions for the messages it wants to handle specially.  ScrollBar then
operates on objects of class ScrollBarReceiver.   This is the only way
to ensure at compile time (without an extremely intelligent compiler)
that all the messages ScrollBar sends will be understood by the recipient.

Note that this is one of the most important areas of divergence between
C++ and Objective C.  In Objective C, you could send messages to the
objects without a compiler-given guarantee that the object will know what
to do with them.  Obviously there are trade-offs between the two approaches,
and some people prefer one over the other, but I am all for keeping 
strong saftey checking in C++.  If you don't like it, use Objective C.

-- 
Jeremy Grodberg      "Show me a new widget that's bug-free, and I'll show
jgro@lia.com         you something that's been through several releases."

jimad@microsoft.UUCP (Jim ADCOCK) (05/24/91)

In article <1991May14.155950.4336@infonode.ingr.com> allgood@greg.b17a.ingr.com writes:
>   I would like to have the capability of telling some object A, to send
>message M to an object B where M and B are not specifically known to A.
>For example, I would like to create a ScrollBar and tell it "Hey, when 
>you get scrolled to a new location, send a 'move_to' message to this
>instance of class View".  I can do this in C++ if the ScrollBar class knows
>about the View class, but I want to create a generic ScrollBar which
>can send messages to classes which will be defined later.  


You might want to consider an approach similar to the below.  The basic
idea is to define an abstract notion of the "Action" [or whatever you want
to call it] that needs to be taken to keep the Scrollbar happy in 
certain situations.  An "Action" being typically invoking some method
on some object -- BUT -- the concept of an "Action" doesn't have
to explicitly define an Action as being such.  The concept of an "Action"
doesn't have to be defined to be "anything."  Rather, we can wait till
later and specialize Actions to meet our particular needs.  In this
case we're simply saying an Action looks something like a function of
one int parameter:

	Action doSomething;

	....

	doSomething(-2);

A ScrollBar then just needs to take a couple Action& parameters to specify
what needs to be done in certain situations.   Later, one can specialize
Actions by derivation to specify the way those Actions are actually
performed.  In our case, sometime after ScrollBars are defined, we decide
we need ViewActions to be the action of invoking a particular View method
on a View object.  Fine, ViewActions are Actions, so now a ScrollBar is
able to take the Action of invoking a View method on a View object.  Even
though ScrollBars know nothing about View objects.

/**********************************/

#include <stdio.h>
#include <iostream.h>

class Action
{
public: 
	virtual void operator()(int) = 0;
};

class ScrollBar
{
   protected:
      int t;
      Action& moved_to;
      Action& moved;

   public:
      ScrollBar(int _t, Action& _moved_to, Action& _moved) 
      : t(_t), moved_to(_moved_to), moved(_moved) { }

      void move(int dt)
	{ t += dt;
	  moved(dt);
	}
      void move_to(int _t)
	{ t = _t;
	  moved_to(t);
	}
};

/**************************************************/

// now, at some later point in time you can choose ScrollBar Actions to be
// actions related to Views:

class View
{
   public:
      View() {}
      void moved(int dp)
	 {cout << "View moved by " << dp << endl;}
      void moved_to(int p)
	 {cout << "View moved to " << p << endl;}
};

typedef void (View::*pViewMethod)(int);
class ViewAction : public Action
{
	View* obj;
	pViewMethod method;
public:
	ViewAction(View* _obj, pViewMethod _method)
	: obj(_obj), method(_method) { }

	virtual void operator()(int parm) { (obj->*method)(parm); }
};

main()
{
   View v;

   ViewAction moved_action(&v, &View::moved);
   ViewAction moved_to_action(&v, &View::moved_to);

   ScrollBar sb(5, moved_to_action, moved_action);

   sb.move(-2);
   sb.move_to(10);

   return 0;
}

gyro@kestrel.edu (Scott Layson Burson) (05/25/91)

In article <1991May14.155950.4336@infonode.ingr.com> allgood@greg.b17a.ingr.com writes:
>   I would like to have the capability of telling some object A, to send
>message M to an object B where M and B are not specifically known to A.
>For example, I would like to create a ScrollBar and tell it "Hey, when 
>you get scrolled to a new location, send a 'move_to' message to this
>instance of class View".  I can do this in C++ if the ScrollBar class knows
>about the View class, but I want to create a generic ScrollBar which
>can send messages to classes which will be defined later.  

A proposal that either has or will soon come before the committee
addresses this concern.  The idea is to generalize the notion of a
pointer to function so that it can carry a `this' pointer along with
it.  Then given an object `x', with member function `f', the value of
`x.f' (or `&x.f') is a functional object which, when invoked, performs
the `f' operation on that particular `x'.  (In some circles this is
called a "closure".)

The reason this is relevant is that the *type* of such a pointer to
function is determined only by the signature of the member function
from which it was created; it is *independent* of the class that
created it (if any).  Therefore it forms a mechanism for allowing
communication between objects that do not know each other's type; all
that is required is for one or more of these generalized pointers to
functions to be passed from one object to the other at runtime.

-- Scott
Gyro@Reasoning.COM