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.nlvaughan@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!vaughanmike@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.