Topic: Generic Object Callbacks


Author: "ian (i.) willmott" <willmott@bnr.ca>
Date: 1996/03/04
Raw View
References: <4h9ii0$3mlq@news-s01.ny.us.ibm.net>

hickeyr@ibm.net (Rich Hickey) wrote:

>Having previously considered the suggestion you proposed and forced to solve
>the problem in current language, and with a lot of experience using a
>current-language solution, I feel it is actually preferable to the extension
>you propose in the following areas:
>
>A)It is more type-flexible. By that I mean it is possible to produce a
>callback system that is type-safe, yet tolerant of type differences in the
>caller/callee signatures that are either implicitly convertible or can be
>ignored. For example if the caller requires a function to which it can pass
>an int and from which it expects nothing, using my library it is possible to
>bind the callback to a function that takes an int and returns an int (return
>value is ignored), or takes a long and returns void (compiler implicitly
>converts int->long). Once you get involved with polymorphism this becomes
>critical: caller passes ptr-to-derived, callee accepts ptr-to-base. Any
>language extension approach would have the rigidity normally associated with
>pointers-to-functions, i.e. exactly matching signatures required.

I'm not sure what you mean by this. I've never heard of any requirement
for "exactly matching signatures" for calls through pointers-to-function.
As far as I know the matching rules are exactly the same as for ordinary
function calls. The following code is accepted by Cfront v3.0
without complaint:

class A {
    int x;
};

class B: public A {
    int y;
};

int (*fp)(long,A *);

long func(int i,B *p)
{
    return(fp(i,p));
}

The proposed language extension would work the same way. It's true
that this is illegal (I assume this is what you were referring to):

fp=&func;  // never mind the infinite recursion here

However, I don't see why this is a problem. You just declare your
callback functions to take the most general parameters; in this
example, (long,A *) instead of (int,B *). The proposed type
"pointer-to-bound-member-function" is the natural analog for class
member functions of the existing pointer-to-function type, and will
be useful in the same contexts.

>B)It is easier to extend; to things like callbacks with stored arguments
>etc.

How so? Here's the PBMF solution:

class MyCallback {
    int (class::*pbmf)(int,A *);  // language extension
    int i;
    A *p;
public:
    MyCallback((class::*pb)(int,A *),int ii,A *pp):
        pbmf(pb),i(ii),p(pp) {}
    int operator()
        { return(pbmf(i,p)); }
};

No dynamic allocation, virtual functions, casts to void *, or templates
(although you could use them if you wanted to) needed. Of course, if this
extension were adopted, this would not prevent other solutions from being
used anywhere they were appropriate. For most cases, the built-in datatype
is more convenient and type-safe.

Ian Willmott
Bell-Northern Research
Ottawa, Ontario
(613)-763-9688
willmott@bnr.ca
---
[ 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: "brian (b.c.) white" <bcwhite@bnr.ca>
Date: 1996/02/26
Raw View
In article <pgpmoose.199602221531.14635@isolde.mti.sgi.com>,
ian (i.) willmott <willmott@bnr.ca> wrote:

>In an article entitled "Q: Generic Callbacks -- Object->*func(...)"
>(reference <4fti32$p3p@bcarh8ab.bnr.ca>), bcwhite@bnr.ca asks

>"Does the C++ standard allow for a generic callback to be specified?
>Basically, I'd like to be able to pass an arbitrary object and
>function of that object to be called at some later time."

>What is needed is the ability to use member functions as callbacks
>without any constraint on the type of the object they are invoked on.
>This is possible in C++ only by using various implementation-dependent
>hacks to escape the type system, because the language does not provide
>any way to express such a construct.

Of course, what we're really trying to with all this is to pass _code_
(along with its context) as an object.

                                        Brian
                                 ( bcwhite@bnr.ca )

-------------------------------------------------------------------------------
    In theory, theory and practice are the same.  In practice, they're not.
[ To submit articles: Try just posting with your newsreader.
        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: alindbac@sw.seisy.abb.se (Anders Lindback)
Date: 1996/02/27
Raw View
In article <4gsi2p$905@bcarh8ab.bnr.ca>,
brian (b.c.) white <bcwhite@bnr.ca> wrote:
>In article <pgpmoose.199602221531.14635@isolde.mti.sgi.com>,
>ian (i.) willmott <willmott@bnr.ca> wrote:
>
>>In an article entitled "Q: Generic Callbacks -- Object->*func(...)"
>>(reference <4fti32$p3p@bcarh8ab.bnr.ca>), bcwhite@bnr.ca asks
>
>>"Does the C++ standard allow for a generic callback to be specified?
>>Basically, I'd like to be able to pass an arbitrary object and
>>function of that object to be called at some later time."
>
>>What is needed is the ability to use member functions as callbacks
>>without any constraint on the type of the object they are invoked on.
>>This is possible in C++ only by using various implementation-dependent
>>hacks to escape the type system, because the language does not provide
>>any way to express such a construct.
>
>Of course, what we're really trying to with all this is to pass _code_
>(along with its context) as an object.

There exist a rather simple solution using a static member function.
Instead of having the callback call a normal member function it can
call a static callback member function with the object as a
argument. Then this callback function can call the real callback function.
This will involve an extra callback function for each callback one has,
but on the other hand it involves no changes to the base class if
one want to override the real callback function in a derived class.

class X
{
public:
  static void callback(void* to)
  virtual void real_callback();
};

void X::callback(void* to)
{
  X* me = (X*)to;
  me ->real_callback()
}

Motif widget callbacks and event handlers are examples where this pattern
works without problems.

If the desired class Y can not be modified it is possible that X is a helper
class that knows which member function on the real class Y it shall call.
I.e. this solution is portable and can be used for all possible classes.

Anders



[ 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: David Byrden <100101.2547@compuserve.com>
Date: 1996/02/27
Raw View
>> There exist a rather simple solution using a static member function.
>> Instead of having the callback call a normal member function it can
>> call a static callback member function with the object as a
>> argument

  This would require to caller to store and pass the address of the
object. Since a different static function would exist for each
callbackable member of the class, the caller would have to keep and use 2
items of data. He would presumably wrap them in a class for convenience,
at which point he would have reinvented most of the work done by Richard
Hickey's callback class.

                                David
---
[ 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: "ian (i.) willmott" <willmott@bnr.ca>
Date: 1996/02/29
Raw View
Shalom Reich <sqr1874@acf4.nyu.edu> wrote:

>I don't seem to understand the discussion in this thread.  Yet it seems to generate
>some strong opinions.  So, . . .
>
>The routine that will issue the callback (let us call it the driver) needs to use a
>stored pointer.  The argument is that the stored pointer should be to a member function.
>Why isn't it good enough for the stored pointer to be a pointer to the object and then
>the driver will only need to say "object->callback();" in order to call the proper routine.
>The usual virtual function semantics would take care of any inheritance hierarchy.

The point is that the "driver" shouldn't have to know the type of the object the
callback is directed at. This is particularly important when the "driver" issuing
the callback is a library API, such as Motif; you do not want the library to have
to know about application data types and you do not want to constrain the application
to use a single callback object type that is defined by the library. The point of the
proposal is that there is a simple extension to the language which would accomplish
this in a type-safe way.

>If the driver could deal directly with the address of a non-static member function
>how would the "this" parameter be supplied by the caller?

The original article (this has expired on my site and possibly others as well;
maybe I should repost it) suggests a new type "pointer-to-bound-member-function"
which would encapsulate an object pointer and a pointer to a member function
of that class.

>The X example seems to be showing an inteface between a C environment and a C++
>environment.  Is that the real problem?

The real problem is that it's difficult to write an object-oriented API for a
system like X because C++ as currently defined doesn't provide type-independent
object callbacks. The proposed language extension would greatly facilitate the
creation of object-oriented, event-driven API's.

>Am I missing something here?

See my original article for an extended discussion of this issue. I will repost it
or email it if necessary.

Ian Willmott
Bell-Northern Research
Ottawa, Ontario
(613)-763-9688
willmott@bnr.ca
---
[ 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: Shalom Reich <sqr1874@acf4.nyu.edu>
Date: 1996/02/29
Raw View
Ben Jones wrote:
>
> Anders Lindback (alindbac@sw.seisy.abb.se) wrote:
> : In article <4gsi2p$905@bcarh8ab.bnr.ca>,
> : brian (b.c.) white <bcwhite@bnr.ca> wrote:
> : >
> : >>What is needed is the ability to use member functions as callbacks
> : >>without any constraint on the type of the object they are invoked on.
> : >>This is possible in C++ only by using various implementation-dependent
> : >>hacks to escape the type system, because the language does not provide
> : >>any way to express such a construct.
> : >
>
> The point we have been making is that there is a trivial way to extend
> the C++ language to provide for completely typesafe callbacks which
> are simple to use and which will dramatically improve the clarity
> of programs.
>

I don't seem to understand the discussion in this thread.  Yet it seems to generate
some strong opinions.  So, . . .

The routine that will issue the callback (let us call it the driver) needs to use a
stored pointer.  The argument is that the stored pointer should be to a member function.
Why isn't it good enough for the stored pointer to be a pointer to the object and then
the driver will only need to say "object->callback();" in order to call the proper routine.
The usual virtual function semantics would take care of any inheritance hierarchy.

If the driver could deal directly with the address of a non-static member function
how would the "this" parameter be supplied by the caller?

The X example seems to be showing an inteface between a C environment and a C++
environment.  Is that the real problem?

Am I missing something here?

Shalom Reich


[ 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: Marco_Dalla-Gasperina@vcd.hp.com (Marco Dalla Gasperina)
Date: 1996/03/01
Raw View
In article <3135C73D.5570@acf4.nyu.edu>, Shalom Reich <sqr1874@acf4.nyu.edu>
wrote:
>Ben Jones wrote:
>>
>> Anders Lindback (alindbac@sw.seisy.abb.se) wrote:
>> : In article <4gsi2p$905@bcarh8ab.bnr.ca>,
>> : brian (b.c.) white <bcwhite@bnr.ca> wrote:
>> : >
>> : >>What is needed is the ability to use member functions as callbacks
>> : >>without any constraint on the type of the object they are invoked on.
>> : >>This is possible in C++ only by using various implementation-dependent
>> : >>hacks to escape the type system, because the language does not provide
>> : >>any way to express such a construct.
>> : >

You really don't need hacks at all.  It is completely possible to do it
with the way the language is defined.  See below.

>I don't seem to understand the discussion in this thread.  Yet it seems
>to generate some strong opinions.  So, . . .
>
>The routine that will issue the callback (let us call it the driver)
>needs to use a stored pointer.  The argument is that the stored pointer
>should be to a member function.
>Why isn't it good enough for the stored pointer to be a pointer to the
>object and then the driver will only need to say "object->callback();" in
>order to call the proper routine.

The problem here is probably that you are constraining all "callbackable"
objects to be derived from a common base class which may not be a natural
attribute of the system.

>The usual virtual function semantics would take care of any inheritance
>hierarchy.

Here's one way to do it (excuse any syntax errors as I haven't compiled
this) ...

class CallbackBase {
    public:
    virtual doit();
};

template<class T> class Callback : public CallbackBase {
    private:
    // the callback object saves a pointer to a member function
    // and a pointer to the target object.
    void (T::*callbackFunc)();
    T* target;

    public:
    // save the pointer-to-member and target object
    Callback( T* obj, void (T::*cbf)() ) : target(obj), callbackFunc(cbf) {}
    // doit issues the callback
    void doit() { target->*callbackFunc)(); }
};

Heres the driver code...
DriverFunc( CallbackBase* cbb )
{
    ...
    cbb->doit();
    ...
    delete cbb;
}

Heres the client code:

class MyCleck {
    void Cleck() { ... }
};

class MyBlurp {
    void Blurp() { ... }
};

main()
{
    ...
    MyCleck   mc;
    MyBlurp   mb;

    DriverFunc( new Callback<MyCleck>(&mc,&MyCleck::Cleck) );
    DriverFunc( new Callback<MyBlurp>(&mb,&MyBlurp::Blurp) );
    ...
}

I think this should work, as I have implemented the publisher/
subscriber (observer/observed) pattern in an almost identical
way.

The problem is a little trickier trying to generalize any
parameters that the callback methods require and still keep
them typesafe.

>Shalom Reich

marco
---
[ 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: "ian (i.) willmott" <willmott@bnr.ca>
Date: 1996/02/22
Raw View
Subject: Generic Object Callbacks

In an article entitled "Q: Generic Callbacks -- Object->*func(...)"
(reference <4fti32$p3p@bcarh8ab.bnr.ca>), bcwhite@bnr.ca asks

"Does the C++ standard allow for a generic callback to be specified?
Basically, I'd like to be able to pass an arbitrary object and
function of that object to be called at some later time."

What is needed is the ability to use member functions as callbacks
without any constraint on the type of the object they are invoked on.
This is possible in C++ only by using various implementation-dependent
hacks to escape the type system, because the language does not provide
any way to express such a construct.

This is strange, because the use of callbacks is critical to
event-driven programming for applications such as embedded systems and
graphical interfaces; C++, which aims to support object-oriented
programming, ought to allow member functions as well as ordinary
functions to be the target of a callback. As yet, it doesn't.

I suggest that a new data type needs to be added to the language. We
could call it "pointer-to-bound-member-function". A small example will
clarify the requirement:

int *pi;
int (*pf)(int);

void f1(int i)
{
    *pi=i;
    (*pf)(i);
}

// somewhere else:

class A {
public:
    int x;
    int f(int);
};

void f2(A *pa)
{
    pi=&pa->x;  // this is legal
    pf=&pa->f;  // this isn't
}

The desired semantics here are perfectly clear. Assuming f2() has been
executed first, the second line in f1() should invoke A::f() on the
object referenced by f2's argument, without having to know anything
about the object type. This is exactly analogous with the integer
pointer pi, which is why I included it.

The reason this isn't legal is because of the way function pointers
are implemented, as a single machine pointer into code space. To make
the example work, a pointer into data space, to identify the object,
would have to be saved as well. It would be a bad idea to burden the
implementation of function pointers with an extra word of storage just
to make this work, so a new type is needed to provide this facility.

The name "pointer to bound member function" is suggested by the error
message Cfront produces when you feed it the code above. Specifically,
the type I am proposing will encapsulate the idea of a member function
callback in a completely typesafe way, and will be trivially easy to
implement. Conceptually, it is a two-element structure consisting of
pointer into data space, identifying an object, and a pointer into
code space, identifying a member function on that object. The static
type of such a variable is the signature (return type and arguments)
of the member functions it is compatible with, just as the type of a
regular function pointer is the signature of the functions it can
reference. The only operations defined for this type are assignment,
equality, and callthrough. Static typechecking is done at the point
where a value of this type is created, and where a call is made
through it.

Notice that this type is already implicit in the language. In the
expression

    (pa->f)(17)

what is the type of the subexpression (pa->f) ? The parentheses are
not even necessary; the operator associativity produces the same
binding. This means that C++ contains legal expressions whose type
cannot be expressed in the language. I propose that the language be
extended so that this type can be expressed, variables of this type
can be declared, and the obvious operations on them provided.

This is what is needed:
1 - declaration syntax for objects of this type
2 - expressions such as the one above are values of this type
3 - assignment operator for variables of this type
4 - call through
5 - implicit conversion from zero
6 - implicit conversion from signature-compatible pointers-to-function
7 - equality and inequality operators for values of this type


Declaration Syntax

No new keywords are required. A slight modification of the existing
pointer-to-member-function declarator syntax will suffice.

return-type (class::*pbmf)(arg-decl-list)

declares the identifier pbmf to be a pointer-to-bound-member-function
with the specified signature. "struct" should probably be allowed in
place of "class". Similar syntax will be used to declare pointers and
references to objects of this type, arrays of this type, functions
returning this type, function arguments of this type, etc.

If this syntax is inappropriate for some reason, something similar
could be devised instead.


Values of Type Pointer-to-Bound-Member-Function

Given the declaration of class A above, and the following declarations

A obj,&ref,*ptr;
int (A::*pmf)(int);

the following expressions all have type
"pointer-to-bound-member-function of one int parameter returning int":

obj.f
ref.f
ptr->f
obj.*pmf
ref.*pmf
ptr->*pmf

Anywhere it is possible to call a non-static class member function on
an object, it is possible to compute two things: the entry point of
the actual function to be executed, and the object pointer to be
passed to that function. Computing the function entry point may
involve virtual function table lookup, and in the presence of multiple
inheritance the object pointer passed to the function may not be the
same as the address of the object on which the function was called.
The pair of these values constitute a value of type
pointer-to-bound-member-function. This value can be used immediately
to specify a member function call, or it can be saved for later use.
If this happens, information about the type of the object need not be
preserved, because all typechecking except of the actual parameters
used in the call has already been done.

If an expression of one of the first three forms above refers to a
static member function, the data-pointer component of the resulting
pointer-to-bound-member-function will have the value used by the
implementation to represent a null pointer.


Assignment

Assignment to an lvalue of type pointer-to-bound-member-function is
defined to mean copying of the two components of the value on the
right-hand-side of the assignment expression into the corresponding
elements of the lvalue.


Call Through

If the identifier pbmf denotes a pointer-to-bound-member-function, and
the arguments supplied in an expression of the form

(*pbmf)(arg-list)

match the function signature of pbmf, then the expression specifies a
function call through the value contained in pbmf, the type of the
expression is the return type of pbmf, and the value of the expression
is the value returned by the called function.

If the data-pointer component of the value of pbmf is non-null, it
will be passed to the called function using the usual mechanism for
object-pointers in member function calls; if it is null, it will not
be. In this most implementations, this will mean that it will be
pushed onto the stack after all the other parameters only if it is
non-zero. The code-pointer component of the value of pbmf specifies
the exact entry point of the function to be executed. If it is null,
the resulting behaviour is undefined.

In order to match the syntax for calls through regular
pointers-to-function, the expression

pbmf(arg-list)

should be allowed and have the same meaning.


Implicit Conversions

A value of type pointer-to-function can be implicitly converted to a
pointer-to-bound-member-function of the same signature. The
code-pointer component of the resulting PBMF has the same value as the
pointer-to-function, and the data-pointer component of the PBMF has
whatever value the implementation uses to represent null data
pointers. This implies that there is an implicit conversion to PBMF
from zero, since zero can be implicitly converted to
pointer-to-function.


Equality and Inequality Operators

Two values of the same pointer-to-bound-member-function type may be
compared to each other using the == and != operators. This is defined
to mean component-wise comparison of the two values, which are
considered to be equal only if the corresponding components are both
equal, and not equal otherwise.


General Comments

For reasons of syntactic consistency, declarations such as the
following should probably be allowed:

int class::*pbmd;

Objects declared this way will have the same semantics and
implementation as normal data pointers and there should be implicit
conversions in both directions between them.

PBMFs are upward compatible with regular pointers-to-function;
anywhere a callback might have to refer to member function on an
object, it should be declared as a PBMF; either type can then be
assigned to it, and the callthrough will behave appropriately. Where
this facility is not needed, pointers-to-function will suffice.

The proposed type pointer-to-bound-member-function introduces to C++
no new keywords, only one new piece of syntax, the declarator, and one
new run-time operation, the callthrough, consisting of a conditional
stack push and a function call. It is statically type-checked in
exactly the same way as a regular member function call; it merely
separates member function lookup and invocation. Objects of this type
are opaque; there is no way to access the two components independently
and consequently it is completely type-safe, something that cannot be
said for the kludges currently necessary to implement the
functionality it provides. This datatype can be implemented in two
machine words on linear address-space architectures and requires no
additional run-time support. It appears to be completely orthogonal to
the rest of the C++ language.


Liabilities

I can think of only one possible problem. If a PBMF referring to a
virtual function is exported from a base class constructor invoked for
a derived class object, then when a call through that PBMF is made,
the member function invoked will be the one defined for the base class
and not the derived class. No simple implementation can prevent this
possibility. It's pretty hard to think of any good reason why you
would want to do this. This is analogous to the situation of calling a
pure virtual function from a base class constructor, although at least
that can be detected at runtime. There is the corresponding
possibility of importing a PBMF into a base class destructor, but
that's even more unlikely. Any function pointers used in code called
from constructors and destructors would be much better expressed as
pointers-to-member-function.


Possible Objections


"You can do this with an object pointer and a pointer-to-member-function."

Only if all of your callbacks are to objects in the same class
hierarchy (a severe restriction), and to members declared in exactly
the same class.  It's not even good enough that they be declared in
classes derived from a common base type, since if D is derived from B,
you can assign an value of type (B::*)() to an object of type
(D::*)(), but not the other way around. There's a good reason for this
rule: if it didn't exist it would be possible to call a member
function on an object of a class for which it was not defined.

This points out the need for a type which encapsulates an object
reference and a member function reference so that there is no
possibility of applying the function reference to an object of
inappropriate type.


"There are other ways to do this."

There are two that I know of.

class pbmf {
    void *ptr;
    int (*func)(void *,int)
public:
    pbmf(void *p,int (*f)(void *,int)):
        ptr(p),func(f) {}
    int operator()(int x)
        { return(func(ptr,x)); }
};

int Af(void *p,int x)
{
    return(((A *) p)->f(x));
}

A obj;
pbmf pf(&obj,&Af);

pf(17);

This isn't typesafe, it requires you to rewrite the class declaration
for every new function signature, and to write a wrapper function
around every member function you want to use this way, and imposes the
overhead of an extra function call.

class pbmfbase {
public:
    virtual int operator()(int);
};

template<class T> class pbmf: public pbmfbase {
    T *ptr;
    int (T::*func)(int);
public:
    pbmf(T *p,int (T::*f)(int))
        ptr(p),func(f) {}
    int operator()(int x)
        { return(ptr->*func(x)); }
};

pbmfbase *pf=new pbmf<A>(&obj,&A::f);

(*pf)(17);

This requires a new template for every different function signature
(you might be able to do something with member function templates), it
costs an extra virtual function call, and because the callback has to
be done through a pointer or reference, objects of this type are
probably going to end up on the heap, although you might be able to
avoid this with some other type-unsafe tricks. Using templates is
really overkill for an application this simple.

These are really ugly hacks to accomplish something that should be
directly supported by the language. The fact that the type
pointer-to-bound-member-function is implicit but unexpressable in the
language shows that this is a real deficiency.


"Any built-in type should fit in a single processor register."

According to the discussion in the ARM and the D&E, pointers to member
function are represented by structures with at least three elements.
PBMFs are much easier to implement than pointers-to-member-function.

Also, floating point numbers often do not fit into a single register on
machines without hardware floating point support.


"What will happen if a PBMF is called through after the object it
refers to has been destroyed?"

This is the same situation as calling a member function through a
regular pointer which does not reference a valid object, either
because the pointer is invalid, or the object has been destroyed. The
language does not prevent you from doing this.


"It's not type-safe."

PBMFs are statically type-checked in exactly the same way that regular
member function calls are. They simply separate the object
type-checking from function invocation. Once member function lookup
and pointer adjustment are done, there is no further need for object
type information.


I realize that the probability of any more extensions to the standard
being accepted at this point is small, but I thought that it would be
worth discussing this one anyway. I would not be at all surprised to
hear that something similar to this has been proposed before, although
I saw no mention of any such in the D&E. If so, I would like to know
why it was rejected.

I would like to know how many people think that this would be a useful
language feature. It seems to me that it would make object-oriented,
event-driven programming much easier. I have been working on the
embedded control software for a network switch for the last two years
and the need for this facility has arisen over and over again, but I
have had to make do with the sort of kludges listed above. When the
GNU C++ compiler becomes more stable, I will look into adding this
datatype to it. All comments are welcome.


Ian Willmott
Bell-Northern Research
Ottawa, Ontario
(613)-763-9688
willmott@bnr.ca
---
[ To submit articles: Try just posting with your newsreader.  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: David Byrden <100101.2547@compuserve.com>
Date: 1996/02/23
Raw View
>>>
What is needed is the ability to use member functions as callbacks
without any constraint on the type of the object they are invoked on.
This is possible in C++ only by using various implementation-dependent
hacks to escape the type system, because the language does not provide
any way to express such a construct.
<<<

  The scheme presented by Richard Hickey in the C++ Report, Feb 95,
achieved this in what seemed to me an implementation-independent way.
Briefly; the creator of a functor would pass an object's and a function's
address to a template factory function; this would cause the
instantiation of various code, and return an object of a class Callback,
which would internally hold the addresses, the object address being
cast to void*.

 The Callback object had an operator() which would take the various
parameters expected for the callback, and pass them to another function
generated by the Factory specialisation. This function would cast the
void pointer to the appropriate type for the object, and complete the
call.

  No type information was shared between the caller and the called
modules, other than the declaration of various Callback classes and
associated functions. In fact, different Callback classes were required
to deal with functions having different numbers of parameters.

                                     David



[ To submit articles: Try just posting with your newsreader.
        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: phalpern@truffle.ultranet.com (Pablo Halpern)
Date: 1996/02/23
Raw View
"ian (i.) willmott" <willmott@bnr.ca> wrote:

>I suggest that a new data type needs to be added to the language. We
>could call it "pointer-to-bound-member-function". A small example will
>clarify the requirement:
>
> [ long proposal deleted ]

Bravo!

As you might have noticed, I came up with exactly the same concept (even
the same name) in a related thread, but without the full analysis that
Ian provided.

Ian, PLEASE, PLEASE, PLEASE submit this to the ISO/ANSI committee in
their comment format. One suggestion if you do this, write all of the
implementation descriptions as parenthetic asides to illustrate that
this is easy to implement. The standard does not mandate any specific
implementation of features, it just describes semantics.
-------------------------------------------------------------
Pablo Halpern                   phalpern@truffle.ultranet.com

I am self-employed. Therefore, my opinions *do* represent
those of my employer.

[ To submit articles: Try just posting with your newsreader.
        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
]