Topic: properties in C++


Author: wkdugan@ix.netcom.com (Bill Dugan)
Date: 1997/08/08
Raw View
On 07 Aug 1997 15:26:44 PDT, Don Griffin
<dgriffin@no.spam.farallon.com> wrote:

>>I don't understand why you say this can't be done in C++. If I
>>understand what you're describing, it can be done with templates and
>>pointers to member functions.
>
>I don't think so.  My goal is to create class A and allow users of that
>class to be called when something interresting happens (ie, an "event"
>is fired).  Class A wants to pass itself and an int (for example) as
>parameters to the event.  One way to do this is:

snip

But that isn't the best way. Take a look at the simple example posted
elsewhere on this thread by Gerard Weatherby <gerardw@alum.mit.edu>.
This basic pattern can be elaborated in several ways.

>Adding a template will not solve this problem.  As a matter of fact, you
>would need a matrix of template classes for different function
>signatures!  You would need a template for 0 parameters, another for 1
>parameter, etc..  Then multiply the number of templates by 2, because
>the return type may be void or non-void.

For reasonable kinds of uses, you wouldn't need more than about half a
dozen templates.

>Even if templates could easily
>be created, class A wants to keep one of these pointers as a member, so
>it would have to instantiate one of the templates and we'd be back at
>square one.

A doesn't have to know the user's type. In order for A to call its
user's method, they do have to agree on the signature of that method.
A just instantiates a pointer to the base class template which matches
the signature, while the user, which knows its own type, instantiates
an instance of the derived class template which matches both the
user's type and the signature.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: "Nat Pryce" <np2@doc.ic.ac.uk>
Date: 1997/08/11
Raw View
Colin Rafferty <craffert@ml.com> wrote in article <ocr67tg37gr.fsf@ml.com>...
> You are thinking like a C style callback rather than a C++ style object.
> Rather than having SetEventHandler() specifying a member function of a
> base class, you would have SetEventHandler() take a Function Object of
> the appropriate type.
>
> If you are not familiar with Function Objects, see the section
> [lib.function.objects] in the draft standard for a longer description.

But function objects cannot be used polymorphically, can they?
I'd suggest defining an abstract interface to receive updates and
passing instances to the SetEventHandler operation.  You could write
templates which wrapped the abstract callback interface around function
objects if necessary -- I have written code like this for a GUI framework
and it works very well.

Cheers,
 Nat.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: wkdugan@ix.netcom.com (Bill Dugan)
Date: 1997/07/31
Raw View
On 31 Jul 97 04:21:37 GMT, Don Griffin <NOSPAMdgriffin@farallon.com>
wrote:

snip

>The right wat to do this is a "closure", which Borland C++ Builder added
>to their C++ implementation.  With a closure, class A declares just what
>we want: "a method of any class taking X and returning Y".  Class A does
>not worry about the object and the method as separate pieces, but rather
>as a single entity.  If a B object wants to pass one of its methods to
>the A object, it must form the closure of its "this" pointer and a
>method with an appropriate signature.
>
>Closures are very powerful, with many applications.  I have yet to see a
>way to use pure C++ to achieve the same elegance.  Templates cannot do
>it.  While BC++B is a Windows product, the concept of events and event
>driven programming extends to most platforms.

I don't understand why you say this can't be done in C++. If I
understand what you're describing, it can be done with templates and
pointers to member functions.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Colin Rafferty <craffert@ml.com>
Date: 1997/08/08
Raw View
Don Griffin writes:

>> I don't understand why you say this can't be done in C++. If I
>> understand what you're describing, it can be done with templates and
>> pointers to member functions.

> I don't think so.  My goal is to create class A and allow users of that
> class to be called when something interresting happens (ie, an "event"
> is fired).  Class A wants to pass itself and an int (for example) as
> parameters to the event.  One way to do this is:

>     class A
>     {
>     public:
>         void (* eventhandler) (A *, int);
>         void SetEventHandler (eventhandler ev) { ev = _ev; }
>     };

> All well and good, but I really want "eventhandler" to be a class
> method, so I must ammend class A like so:

>     class A
>     {
>     public:
>         class EventClass  { };
>         void (EventClass::*eventhandler) (A *, int);
>         void SetEventHandler (eventhandler ev) { ev = _ev; }
>     };

You are thinking like a C style callback rather than a C++ style object.
Rather than having SetEventHandler() specifying a member function of a
base class, you would have SetEventHandler() take a Function Object of
the appropriate type.

If you are not familiar with Function Objects, see the section
[lib.function.objects] in the draft standard for a longer description.

--
Colin
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Alexandre Oliva <oliva@dcc.unicamp.br>
Date: 1997/07/29
Raw View
loic tregan writes:

> I've read that it would be easy to include properties in C++. It's not true
> for properties which type is function (instead of integer or float). Such a
> property is in fact an event ( in C++ terminology).

> it implies that C++ must support virtual pointer, wich is in fact a
> [instance+method pointer] and when I use "OnClick( this )" in fact
> "(OnClick->Instance).Method(self)" is called.

Why can't you do that with functors?

A functor is an object that stores a reference to another object, a
pointer to a method, and provides an operator() that accepts arguments
to be passed on to the method.

--
Alexandre Oliva
mailto:oliva@dcc.unicamp.br mailto:aoliva@acm.org
Universidade Estadual de Campinas, SP, Brasil
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Volker Hetzer <hetzer.abg@sni.de>
Date: 1997/07/29
Raw View
Alexandre Oliva wrote:
>
> loic tregan writes:
>
> > I've read that it would be easy to include properties in C++. It's not true
> > for properties which type is function (instead of integer or float). Such a
> > property is in fact an event ( in C++ terminology).
>
> > it implies that C++ must support virtual pointer, wich is in fact a
> > [instance+method pointer] and when I use "OnClick( this )" in fact
> > "(OnClick->Instance).Method(self)" is called.
>
> Why can't you do that with functors?
>
> A functor is an object that stores a reference to another object, a
> pointer to a method, and provides an operator() that accepts arguments
> to be passed on to the method.
Can you give me a code example of that?
Sounds interesting.

Volker
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Alexandre Oliva <oliva@dcc.unicamp.br>
Date: 1997/07/30
Raw View
Volker Hetzer writes:

> Alexandre Oliva wrote:

>> loic tregan writes:

>>> it implies that C++ must support virtual pointer, wich is in fact
>>> a [instance+method pointer] and when I use "OnClick( this )" in
>>> fact "(OnClick->Instance).Method(self)" is called.

>> Why can't you do that with functors?

> Can you give me a code example of that?

Just take a look at [lib.member.pointer.adaptors] to get the idea.

I'd define:

template <class S, class T>
class mem_fun_prop_t {
  T& Obj;
  S (T::*P)();
public:
  explicit mem_fun_prop_t(T& obj, S (T::*p)()) : Obj(obj), P(p) {}
  S operator()() { return Obj.*P(); }
};

template <class S, class T>
mem_fun_prop_t<S, T> mem_fun_prop(T& obj, S (T::*p)())
{ return mem_fun_prop_t<S, T>(obj, p); }


template <class S, class T, class A1>
class mem_fun_prop_1arg_t {
  T& Obj;
  S (T::*P)(A1);
public:
  explicit mem_fun_prop_t(T& obj, S (T::*p)()) : Obj(obj), P(p) {}
  S operator()(A1 a1) { return Obj.*P(a1); }
};

template <class S, class T, class A1>
mem_fun_prop_1arg_t<S, T, A1> mem_fun_prop1(T& obj, S (T::*p)(A1))
{ return mem_fun_prop_1arg_t<S, T, A1>(obj, p); }


... and so on

--
Alexandre Oliva
mailto:oliva@dcc.unicamp.br mailto:aoliva@acm.org
Universidade Estadual de Campinas, SP, Brasil
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: wkdugan@ix.netcom.com (Bill Dugan)
Date: 1997/07/30
Raw View
On 28 Jul 1997 10:44:12 PDT, "loic tregan" <loic.tregan@hol.fr> wrote:

>I've read that it would be easy to include properties in C++. It's not true
>for properties which type is function (instead of integer or float). Such a
>property is in fact an event ( in C++ terminology).
>
>it implies that C++ must support virtual pointer, wich is in fact a
>[instance+method pointer] and when I use "OnClick( this )" in fact
>"(OnClick->Instance).Method(self)" is called.
>
>example (it's what we have today in Delphi) :

I don't know Delphi, so I may be misunderstanding you, but what you
describe as a "virtual pointer" sounds like what C++ calls a "pointer
to member function". This encapsulates a combination of an instance
pointer and a method pointer.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Don Griffin <NOSPAMdgriffin@farallon.com>
Date: 1997/07/31
Raw View
A functor might be declared like so:

template <class T>
class functor
{
    public:
        functor (T *o, void (T::*m) ())
            : object(o), method(m) { }

        void operator() ()
            { (object->*method) (); }

    private:
        void (T::*method) ();
        T * object;
};

The major problem with using a functor in an event model is that...

[Deep breath] The object holding the functor (call it class A) needs to
have the right type of functor object.  The "right type" being
determined by the type of object that wants to _use_ it (say class B).
This means class A needs to know about class B.  One might think it
would be possible for B to derive (or mixin) some other class C of which
A is familiar.  The problem here is that method pointers don't work that
way.  The user (class B) has a method of type "void (B::*)()" which is
not compatible with "void (C::*)()" which is the type of functor that A
contains. [exhale]

I may be using outdated information here; perhaps the Standard has
changed the behavior of method pointers and I have not heard about it.

What we really need is the ability for class A to specify a method
signature sans class, eg: "typedef void (class::* method_type)()".  This
would mean "a pointer to a method of any class taking void and returning
void".  The other problems that arise from this are:  class A needs a
way to refer to the object _and_ method (but, remember that A does not
know the type of the object); class A needs a way to invoke the method
on the object.

The right wat to do this is a "closure", which Borland C++ Builder added
to their C++ implementation.  With a closure, class A declares just what
we want: "a method of any class taking X and returning Y".  Class A does
not worry about the object and the method as separate pieces, but rather
as a single entity.  If a B object wants to pass one of its methods to
the A object, it must form the closure of its "this" pointer and a
method with an appropriate signature.

Closures are very powerful, with many applications.  I have yet to see a
way to use pure C++ to achieve the same elegance.  Templates cannot do
it.  While BC++B is a Windows product, the concept of events and event
driven programming extends to most platforms.

The concept of properties is the same.  It is a universal concept that
would be useful in any environment.  Properties have been discussed at
length in another thread, so I won't bother to go over them again here.

I hope this rant wasn't too far off the track of this thread... :)

Don Griffin
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Alexandre Oliva <oliva@dcc.unicamp.br>
Date: 1997/07/31
Raw View
Don Griffin writes:

> The major problem with using a functor in an event model is that...

> [Deep breath] The object holding the functor (call it class A) needs to
> have the right type of functor object.  The "right type" being
> determined by the type of object that wants to _use_ it (say class B).
> This means class A needs to know about class B.  One might think it
> would be possible for B to derive (or mixin) some other class C of which
> A is familiar.  The problem here is that method pointers don't work that
> way.  The user (class B) has a method of type "void (B::*)()" which is
> not compatible with "void (C::*)()" which is the type of functor that A
> contains. [exhale]

A method pointer needs not be used at all.  A non-member function or
static member function may take whatever arguments it needs, including
a pointer to a base class that can be down-casted to the appropriate
type before delivering.

template <class T> class functor;

template <class ret_type>
class functor_base {
protected:
  functor_base() {}
  virtual ~functor_base() {}
public:
  virtual ret_type operator() () = 0;
};

template <class T, class ret_type>
class nonstatic_functor : public functor_base<ret_type>
{
/* your implementation follows, but operator()() returns ret_type */
public:
};

template <class T, class ret_type>
class static_functor : public functor_base<ret_type> {
  T* obj;
  ret_type (*foo)(T*);
public:
  static_functor(T* o, ret_type (*m)(T*)) : obj(o), m(foo) {}
  ret_type operator() () { return foo(obj); }
};

template <class ret_type>
class functor
: auto_ptr<functor_base>,
  public nonstatic_functor<functor_base, ret_type> {
public:
  functor(functor_base* o)
  : auto_ptr<functor_base>(o),
    nonstatic_functor<functor_base>(o, &functor_base<ret_type>::operator())
  {};
}

Usage:

functor<RET_TYPE> foo() {
  return functor<RET_TYPE>
           (new nonstatic_functor<TYPE, RET_TYPE>(OBJ, &TYPE::METHOD));
}

main() {
  functor<RET_TYPE> myevent = foo();
  // main does not even know what type the actual functor is
  myevent();
}

> The right wat to do this is a "closure", which Borland C++ Builder added
> to their C++ implementation.  With a closure, class A declares just what
> we want: "a method of any class taking X and returning Y".

I think I've just proved such closures are not necessary for events.
I agree they might be useful for other purposes, though.

--
Alexandre Oliva
mailto:oliva@dcc.unicamp.br mailto:aoliva@acm.org
Universidade Estadual de Campinas, SP, Brasil
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Richard See <Richard.See@vega.co.uk>
Date: 1997/07/31
Raw View
Don Griffin wrote:
>
> The major problem with using a functor in an event model is that...
>
> [Deep breath] The object holding the functor (call it class A) needs to
> have the right type of functor object.  The "right type" being
> determined by the type of object that wants to _use_ it (say class B).
> This means class A needs to know about class B.  One might think it
> would be possible for B to derive (or mixin) some other class C of which
> A is familiar.  The problem here is that method pointers don't work that
> way.  The user (class B) has a method of type "void (B::*)()" which is
> not compatible with "void (C::*)()" which is the type of functor that A
> contains. [exhale]
>

This can sometimes be fixed by using an ABC.

class functor_base {
public:
   virtual ~functor_base() {}
   virtual void operator() () = 0;
};

template <class T> class typed_functor : public functor_base {
public:
   functor (T* o, void (T::*m)()) : object(o), method(m) {}
   virtual void operator() () { (object->*method)(); }
private:
   void (T::*method)();
   T* object;
};

If desired, these classes could be modified for use in a handler object,
say functor_handler, by adding reference counting or clone functions.
Objects of class A could then store and use functor_handlers without
needing to know about class B.

Richard
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: "loic tregan" <loic.tregan@hol.fr>
Date: 1997/07/28
Raw View
I've read that it would be easy to include properties in C++. It's not true
for properties which type is function (instead of integer or float). Such a
property is in fact an event ( in C++ terminology).

it implies that C++ must support virtual pointer, wich is in fact a
[instance+method pointer] and when I use "OnClick( this )" in fact
"(OnClick->Instance).Method(self)" is called.

example (it's what we have today in Delphi) :

TNotifyEvent = procedure ( Sender : TObject) of object; // note : OF OBJECT

TForm1 = class( TForm )
 procedure OnClicked( Sender : TObject); virtual;
 constructor Create;
     begin
        button1.OnClicked := OnClicked: // (this+method) is copied, not
only the ptr
    end;
end;


TButton = class
  FOnClicked : TNotifyEvent;
 property OnClicked : TNotifyEvent read FOnClicked write FOnClicked;
 procedure WindowsMEssage( msg : word);
 begin
    if msg = WM_CLICKED then
       OnClicked( this );   // the instance is extracted then the method
called
                                  // eq
(OnClicked->Instance)->Methode(this)
  end;
end;


=> for properties of type scalar, properies are easy to implement
=> for properties of type function(=Event), the C++ must augmented ( a
pointer to a method should be different than a method to a simple
function).


another consecquence is the following :
TForm2 = class(TForm1)
 procedure OnClicked( Sender : TObject); override;
end;

the line 'button1.OnClicked := OnClicked' will dynamically return
TForm2::OnClicked instead of TForm1::OnClicked ( whereas in C++, you have
to override in each class a "getOnClicked" method, very boring and sensible
to errors).
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]