[comp.lang.c++] Address of member function

delft@fwi.uva.nl (Andre van Delft) (10/24/90)

For a call-back mechanism, I want to store the address of a non-static
class member function. Turbo-C++ does not allow this ("member function
pointers are not true pointer types, and do not refer to any particular
instance of a class"). 

What does ANSI-C++ say about this? Are C++ implementations free to 
decide whether addresses of member functions are normal addresses?
If so, what do other C++ implementations decide? 

Anyway, could ANSI-C++ be extended with a new kind of "access specifier"
for a member function so that one can take the address of it?

Andre van Delft
DELFT@fwi.uva.nl

vaughan@mcc.com (Paul Vaughan) (10/24/90)

   From: delft@fwi.uva.nl (Andre van Delft)

   For a call-back mechanism, I want to store the address of a non-static
   class member function. Turbo-C++ does not allow this ("member function
   pointers are not true pointer types, and do not refer to any particular
   instance of a class"). 

   What does ANSI-C++ say about this? Are C++ implementations free to 
   decide whether addresses of member functions are normal addresses?
   If so, what do other C++ implementations decide? 

   Anyway, could ANSI-C++ be extended with a new kind of "access specifier"
   for a member function so that one can take the address of it?

Both Cfront and g++ accept this usage of member functions

class Foo {
public:
  int foo() {}
};

int (Foo::*f)() = Foo::foo;

Foo aFoo;

int bar = (aFoo.*f)();

However, pointers to member functions are not true pointer types in
the sense that they occupy more storage than normal pointers--about
three words worth.  You will need to store or otherwise obtain the
object that you intend to call the function on.  Also note that
storing pointers to member functions is not very flexible--you
wouldn't be able to vary the arguments or the type of the object to
call it on, for instance.  I recommend an approach like this.


class Foo {                        // a class that would want to post a callback
public:
  void bar(int, float);
};


Foo aFoo;

class CallBack {                  // the base class for CallBacks
public:
  virtual void operator()() = 0;
};


class MyCallBack : public CallBack {        // a callback class for Foo
public:
  MyCallBack(Foo& f, int a1, float a2) : owner(f), arg1(a1), arg2(a2) {};
  void operator()() { owner.bar(arg1, arg2); }
private:
  Foo& owner;
  int arg1;
  float arg2;
};

main() {
  CallBack& cb = *new MyCallBack(aFoo, 3, 3.141);   // a posted callback
  cb();                                             // the invocation
}

If you then find that that you could use pointers to member functions
in many cases (for instance, for functions declared on a certain base
type that all took no arguments and returned void), you could create a
special PMFCallBack class to avoid having to make a class definition
for each sort of CallBack.



 Paul Vaughan, MCC CAD Program | ARPA: vaughan@mcc.com | Phone: [512] 338-3639
 Box 200195, Austin, TX 78720  | UUCP: ...!cs.utexas.edu!milano!cadillac!vaughan

mike@taumet.com (Michael S. Ball) (10/25/90)

In article <12422@cadillac.CAD.MCC.COM> vaughan@mcc.com (Paul Vaughan) writes:
>   For a call-back mechanism, I want to store the address of a non-static
>   class member function. Turbo-C++ does not allow this ("member function
>   pointers are not true pointer types, and do not refer to any particular
>   instance of a class"). 

>Both Cfront and g++ accept this usage of member functions
>
>int (Foo::*f)() = Foo::foo;

So does TC++, but this is not what he asked for.  He wants the actual
address of the function, which is not available in C++ except as an
anachronism.  Few compilers support this anachronism, and we hope that
still fewer do in the future. 

The reason is obvious, and stated in the original postings, the functions
must be bound to a class instance before they can be called, and there
is no way to do this within the C++ language.  For example, it's not
acceptable to pass a pointer to an object as the first parameter, since
the language gives no guarantees of how "this" is implemented.

If you are dealing with an existing callback mechanism which insists
on a pointer to an ordinary function, you must write such an ordinary
function and have it call the member function (possibly through a pointer)
bound to an actual object.


-Mike-

delft@fwi.uva.nl (Andre van Delft) (10/28/90)

A few days ago I had a question on comp.lang.c++ about taking the address
of a member function:

>>For a call-back mechanism, I want to store the address of a non-static
>>class member function. Turbo-C++ does not allow this ("member function
>>pointers are not true pointer types, and do not refer to any particular
>>instance of a class"). 
>>
>>What does ANSI-C++ say about this? Are C++ implementations free to 
>>decide whether addresses of member functions are normal addresses?
>>If so, what do other C++ implementations decide? 

("ANSI-C++" should have been "AT&T C++, 2.0", of course.) Thanks those
who responded. However, my question was not well understood by some,
e.g.,

>It doesn't make sense to call a class member function without an instance of
>that class.  It *should* be illegal.

I meant the following: why is it allowed to take the address of:

a GLOBAL VARIABLE
a GLOBAL FUNCTION 
a MEMBER VARIABLE, i.e. a variable local to an instance of a class

but why is it NOT allowed to take the address of

a MEMBER FUNCTION, i.e. a function local to an instance of a class

Example:


int i;
int f(int k) {printf("%d\n",i+k);}

class c {
  public:
    int j;
    int g(int k) {printf("%d\n",j+k);}
}

main() {
  int*ip,*jp;
  int(*fp)(int);
  int(*gp)(int);
  c anObject;

  ip  = &         i;        // OK
  jp  = &anObject.j;        // OK
  fp  = &         f;        // OK
  gp  = &anObject.g;        // NOT OK
  *ip = 1;
  *jp = 2;
  fp(3);                     // prints 4
  gp(4);                     // would print 6, if not NOT OK ...
}

I want the address of g() as belonging to anObject, and this address should
have the same size of any address. Current C++ implementations do not allow
this: it is reasonable that any instance of a class has its own space for its
instance variables, but it would be inefficient when it also had the complete
code for its member functions for itself. Instead, most C++ versions implement a
member function as a global function with an extra parameter that points tothe
object to which the message is directed. Something like:

                  int cg(c*,int)    for   c::g
with actual use   cg(&anObject,5)   for   anObject.g(5)).

However, it would not be too inefficient to put part of the member function
code in the space of each instance: in 'anObject', this "present" 'g' function

would just call 'cg' with 'this' as extra parameter: 'cg(this,5)'. With a new
"access modifier" programmers could force such present functions, so that
taking their address would be legal, without overhead for nonpresent functions:

class c {
  public:
    int j;
    present int g(int k) {printf("%d\n",j+k);}
}


Andre van Delft
DELFT@fwi.uva.nl

steve@taumet.com (Stephen Clamage) (10/29/90)

delft@fwi.uva.nl (Andre van Delft) writes:

>I meant the following: why is it allowed to take the address of:

>a GLOBAL VARIABLE
>a GLOBAL FUNCTION 
>a MEMBER VARIABLE, i.e. a variable local to an instance of a class

>but why is it NOT allowed to take the address of

>a [nonstatic] MEMBER FUNCTION, i.e. a function local to an instance of a class

The problem comes with virtual functions.  Suppose that you could take
the address of a virtual member function.  You would still have to call
it in conjuction with some object (so "this" could refer to something).
Suppose we had something like
	class C { ... public: virtual f(); g();... };
	class D : public C { ... public: virtual f(); ... };
	D d;

	void (*fp)() = c.f;	// presently illegal
	C* cp = &d;

1.	cp->f();	// virtual call, in this case calls dp->D::f()
2.	cp->fp();	// invented sytax -- what do we do here?
3.	fp();		// ok if fp had the address of an ordinary function

As you can see from 2 & 3, we still must distinguish between a pointer to
a non-static member function and a pointer to an ordinary function.
The compiler would otherwise not be able to tell whether it should
add a "this" pointer and thus whether the call was even legal.

In 1, the C* variable really holds a D*, and so D::f() gets called -- this
is the definition of a virtual function.  What do we do with 2?  We only
have the address of C::f(), so we can't call D::f(), and we have lost
polymorphism.  That is, the behavior of a class object depends on whether
a certain kind of pointer is used to access it.  Ugh.

Also note that fp could also be made to point to C::g(), since it has the
same type as C::f(), but is not virtual.  So we have a new kind of
pointer type, a pointer-to-member-function, which may point to a
virtual or non-virtual member function.  Such an object is not the same
kind of object as other pointers because it must deal with different
kinds of issues.
-- 

Steve Clamage, TauMetric Corp, steve@taumet.com

delft@fwi.uva.nl (Andre van Delft) (10/29/90)

steve@taumet.com (Steve Clamage) writes:

> >delft@fwi.uva.nl (Andre van Delft) writes:
>
> >I meant the following: why is it allowed to take the address of:
>
> >a GLOBAL VARIABLE
> >a GLOBAL FUNCTION
> >a MEMBER VARIABLE, i.e. a variable local to an instance of a class
>
> >but why is it NOT allowed to take the address of
>
> >a [nonstatic] MEMBER FUNCTION, i.e. a function local to an instance of a
class
>
>The problem comes with virtual functions.  Suppose that you could take
>the address of a virtual member function.  You would still have to call
>it in conjuction with some object (so "this" could refer to something).
>Suppose we had something like
>        class C { ... public: virtual f(); g();... };
>        class D : public C { ... public: virtual f(); ... };
>        D d;
>
>        void (*fp)() = c.f;     // presently illegal
>        C* cp = &d;
>
>1.      cp->f();        // virtual call, in this case calls dp->D::f()
>2.      cp->fp();       // invented sytax -- what do we do here?
>3.      fp();           // ok if fp had the address of an ordinary function

I would *never* propose the "invented syntax" cp->fp() since fp is a name of
a global variable instead of a name of a member in class C.
I do not see why virtual functions would be a problem for my proposal:
a new "access modifier", e.g., 'present', for member functions so that you
can take their addresses (which you can also do with member variables), as in

class C {present void f(void);}
...
C anObject;
void (*fp)(void);
...
fp = anObject.f;
...
fp();

The preprocessor/compiler would

1. place in *each* instance 'anObject' of a class C, and for each of its
'present'
   functions 'f(parms)'  simple code that calls the code C::f(&anObject,parms)
   (which is shared among all objects of the class for space efficiency). 

2. transform a call to a 'present' function f(parms) in anObject into
   something like ( (void(*)(parms) (&anObject.f) ) (parms) instead of
   something like C::f(anObject,parms)

No problem with virtual functions: each object instance will know itself to
what class it belongs, and implement the 'present' function accordingly.

Andre van Delft
DELFT@fwi.uva.nl

marc@dumbcat.sf.ca.us (Marco S Hyman) (10/29/90)

In article <1399@carol.fwi.uva.nl> delft@fwi.uva.nl (Andre van Delft) writes:
    
    A few days ago I had a question on comp.lang.c++ about taking the address
    of a member function:

    [[...]]

    I meant the following: why is it allowed to take the address of:
    
    a GLOBAL VARIABLE
    a GLOBAL FUNCTION 
    a MEMBER VARIABLE, i.e. a variable local to an instance of a class
    
    but why is it NOT allowed to take the address of
    
    a MEMBER FUNCTION, i.e. a function local to an instance of a class

I think the problem here is that member functions are not "local to an
instance of a class," but are part of the class itself.  Given the class

    class c {
      public:
        int j;
        int g(int k) {printf("%d\n",j+k);}
    };

    c aC;

there is a j for every instance, therefore you can take the address of 
aC.j or you can get its relative offset in the class by taking the address
of c::j.  There is only one g for the class used by all instances and you
can only get its offset (&c::g).  See section 8.1c of the ARM (pg 155).

Note: by making minor changes to your example you can get what you want,
albeit by special casing pointers to member functions.

#include <stdio.h>

int i;
int f(int k) {printf("%d\n",i+k);return 0;}
	// return 0 added to get rid of warning

class c {
  public:
    int j;
    int g(int k) {printf("%d\n",j+k); return 0;}
	// return 0 added to get rid of warning
};

main() {
  int*ip,*jp;
  int(*fp)(int);
//int(*gp)(int);	// replaced gp with the next two lines
  int (c::*gp)(int);
  c * gpObject;

  c anObject;

  ip  = &         i;        // OK
  jp  = &anObject.j;        // OK
  fp  = &         f;        // OK
//gp  = &anObject.g;        // NOT OK but see the next two lines
  gpObject = &anObject;
  gp  = &c::g;
  *ip = 1;
  *jp = 2;
  fp(3);                     // prints 4
//gp(4);                     // would print 6, if not NOT OK ...
  (gpObject->*gp)(4);	     // this is OK and prints 6 with CC 2.0
}

It's more work than you want to do but gets the job done.

// marc
-- 
// marc@dumbcat.sf.ca.us
// {ames,decwrl,sun}!pacbell!dumbcat!marc

jbuck@galileo.berkeley.edu (Joe Buck) (10/30/90)

In article <1399@carol.fwi.uva.nl>, delft@fwi.uva.nl (Andre van Delft) writes:
> ("ANSI-C++" should have been "AT&T C++, 2.0", of course.) 

No, it should be "Ellis and Stroustrup"; their book, "The Annotated C++
Reference Manual", has been adopted by the ANSI C++ committee as the base
document for the standard.  It specifies many things not supported by AT&T
C++ 2.0, and even a couple of minor things that AT&T C++ 2.1 doesn't support
(though it is roughly 2.1).  When I see someone use the phrase "ANSI-C++"
here that is my understanding of their meaning, although the question "What
does the E&S book say?" is more accurate.

The behavior of AT&T 2.0 (cfront) is definitely not the standard, even
according to AT&T (especially since 2.1 is out now).
-- 
Joe Buck
jbuck@galileo.berkeley.edu	 {uunet,ucbvax}!galileo.berkeley.edu!jbuck	

jamiller@hpcupt1.cup.hp.com (Jim Miller) (10/31/90)

>The behavior of AT&T 2.0 (cfront) is definitely not the standard, even
>according to AT&T (especially since 2.1 is out now).
>-- 
>Joe Buck
>jbuck@galileo.berkeley.edu	 {uunet,ucbvax}!galileo.berkeley.edu!jbuck	
>----------

Just read that AT&T's 3.0 is out (in ComputerWorld),
they implimented templates.


   jim miller
   jamiller@hpmpeb7.cup.hp.com
   (a.k.a James A. Miller; Jim the JAM; stupid; @!?$$!; ... )
   Anything I say will be used against me ...
   But my company doesn't know or approve or condone anything of mine here.

jamiller@hpcupt1.cup.hp.com (Jim Miller) (11/02/90)

Ahem:  AT&T informs me that 3.0 is *NOT* released.

The following is the ComputerWorld article:

Page 156 of ComputerWorld on  October29,1990  

   ADVANCES IN C++

   Hewlett-Packard Co. and AT&T's Unix System Laboratories
   have implemented new features in their respective versions of
   the C++ programming language.  AT&T has adopted tech-
   nology from Object Design,Inc. in Burlington, Mass., which in-
   cludes Release 3.0 of AT&T's C++ "parameterized types"
   or templates.  This allows programmers to build libraries of
   structures without having to know about the objects manipu-
   lated by the structures, the company said.  HP now has a C++
   compiler for its HP-UX system that generates native code for
   the HP9000 computer.  Also, C++ Developer, a class con-
   struction and browsing tool, is now available for the HP Do-
   main/OS system.

Under careful reading it does not say that 3.0 is released, but that is
the impression I got from reading it.  On the other hand ComputerWorld is
perfectly able to get information completely FUBAR'ed.

   jim miller
   jamiller@hpmpeb7.cup.hp.com
   (a.k.a James A. Miller; Jim the JAM; stupid; @!?$$!; ... )
   Anything I say will be used against me ...
   But my company doesn't know or approve or condone anything of mine here.