garya@Solbourne.COM (Gary Aitken) (04/22/89)
Consider the following: class A : public { public: virtual void f() ; } ; class B::A : public { public: void f() ; } ; It is not unusual to need to take the address of a function, and pass it as an argument. The following possibilities arise: A *ap ; B *bp ; A *objp ; void (*fp)() ; ap = new A ; bp = new B ; objp = (A*) bp ; fp = &A::f ; // static reference to a known function fp = &B::f ; // static reference to a known function fp = &objp->f ; // dynamic ref to unknown (at compile time) virtual funct The intent is to get the pointer to the proper function via the virtual table. This does, in fact, work with cfront, although you get a warning telling you to use :: (which won't compile, since it expects a class name in front of it instead of a pointer to a class object). Now consider a function which has been passed two arguments, one a pointer to an object, and the other a pointer to a member function for that object. How do you invoke the member function? The only way I know to do it is by using straight C code, relying on the fact that cfront passes "this" as the first argument. But what about other implementations? Example of what works in cfront: // void dispatch(A *ap, void (*A::fp)()) How would one write this??? void dispatch(A *ap, void (*fp)(A*)) { // ap->(*fp)() ; // This is what I really want... (*fp)(ap) ; // This is what works return ; } Can anyone help me out on this (ooooooo is that a pun?) ? Here's a complete program to play with: ============================= Cut Here ================================ void printf(...) ; class A { public: virtual void f() ; } ; void A::f() {printf("A.f\n");} class B : public A { public: void f() ; } ; void B::f() {printf("B.f\n");} //void dispatch(A *ap, void (*A::fp)()) void dispatch(A *ap, void (*fp)(A*)) { // ap->(*fp)() ; /* how do we do this???? */ (*fp)(ap) ; /* this works for cfront, but what about others?? */ return ; } main() { A *ap ; B *bp ; A *objp ; auto void (*fp)() ; ap = new A ; bp = new B ; objp = (A*) bp ; fp = &A::f ; // static reference to a known function fp = &B::f ; // static reference to a known function fp = &objp->f ; // dynamic ref to unknown (at compile time) virtual function // dispatch(objp, (void (*)()) fp) ; dispatch(objp, (void (*)(A*)) fp) ; } -- Gary Aitken Solbourne Computer Inc. ARPA: garya@Solbourne.COM Longmont, CO UUCP: ...!{boulder,sun}!stan!garya
shopiro@alice.UUCP (Jonathan Shopiro) (04/23/89)
In article <856@garya.Solbourne.COM>, garya@Solbourne.COM (Gary Aitken) writes: (with condensation and a few syntax errors fixed) > > Consider the following: > > class A { > public: > virtual void f() ; void g(); // not virtual > } ; > > class B : public A { > public: > void f() ; void g(); // not virtual > } ; > > A* ap = new A; > B* bp = new B; > A* objp = bp; // no cast necessary > > It is not unusual to need to take the address of a function, and pass it > as an argument. The following possibilities arise: > > fp = &A::f ; // static reference to a known function > fp = &B::f ; // static reference to a known function > fp = &objp->f ; // dynamic ref to unknown (at compile time) virtual funct For virtual functions, only dynamic (time of call) binding is supported. Do it this way: typedef void A::MEMF(); // MEMF is the type of a member function of A MEMF* fp = &A::f; (There is a way to do it without a typedef, but this is easier). Then the calls go like this: (ap->*fp)() // A::f() (bp->*fp)() // B::f() (objp->*fp)() // B::f() fp = &A::g; // note the variable can refer // to a virtual or non-virtual function (ap->*fp)() // A::g() (bp->*fp)() // A::g() (objp->*fp)() // A::g() There is no way to apply A::f() to a B object (but you're not supposed to want to :-)). > Now consider a function which has been passed two arguments, one a pointer > to an object, and the other a pointer to a member function for that object. > How do you invoke the member function? void dispatch(A *ap, MEMF* fp) // this is how { (ap->*fp)(); // This is what I really want... // you got it } -- Jonathan Shopiro AT&T Bell Laboratories, Warren, NJ 07060-0908 research!shopiro (201) 580-4229
dan@oresoft.uu.net (Daniel Elbaum) (04/25/89)
[ When we last left our heros, Gary Aitken (garya@Solbourne.COM) [ was striving mightily to take the address of a virtual function [ and pass it to a subroutine. Using the secret code which the [ evil baron Bjarne had hidden deep within page 153 of The C++ [ Programming Language, he smuggled 'this' to his waiting subroutine [ disguised as the mysterious first argument to a member function. [ Thus he evaded the watchful eyes of the great alchemist Cfront. [ But alas! another sorceror, rumored to be the vicious Orc, saw [ through the ploy. It looked as though he might never achieve [ his objective! [ [ Just in the nick of time, Jonathan Shopiro (shopiro@alice.UUCP) [ arrived bearing the steely typedef! With deft strokes, he [ freed the construct from its magical bindings and delivered the [ valuable pointers safely. [ [ But what of the Orc and those other alchemists who followed not [ the dark path of Cfront? Would this method remain forever fraught [ with peril and uncertainty? [ [ Stay tuned for the thrilling conclusion! The basic problem is to obtain a pointer to a function which is a member of a class, and to pass an object of that class and the function pointer in such a way that the called routine can access the function as a member of the class of the object which was passed. In other words, pass a pointer to an object to a function along with (essentially) the name of a function to call. In the course of doing so, be able to handle properly the cases of virtual and static functions. Unfortunately, an approach which works with Cfront may not work with other implementations, and vice versa. The first sample below is a technique which works with Oregon C++ but not Cfront. The second, derived from Jonathan's response to the original posting of Gary's, compiles under Cfront 1.2.1 but does not seem to handle virtual functions properly. Excerpts from the original postings are at the end of this article. /*------A method which works with Oregon C++-----------*/ /* This means of passing pointers to member functions along with pointers to class objects should be portable. Unfortunately, the line containing the closing brace of the dispatch() function definition is flagged as causing an internal error in our version of Cfront: CC fun.c: "fun.c", line 53: internal <<cfront 1.2.1 2/16/87>> error: bus error (or something nasty like that) 1 error */ #include <stdio.h> class A{ public: virtual int f(){printf("A.f\n"); return(0);} }; class B : public A{ public: int f() { printf("B.f\n"); return(0); } }; typedef int (A::*FP)(); void dispatch(A *ap, FP fp) { (ap->*fp)(); } main() { FP fp; A *objp = (A *)new B; fp = &A::f; dispatch(objp, fp); // calls B::f() fp = (FP)(&B::f); dispatch(objp, fp); // calls B::f() } /*------Shopiro's method, which compiles under Cfront------*/ /* This technique doesn't compile under Oregon C++. */ #include <stdio.h> class A{ public: virtual int f(){printf("A.f\n"); return(0);} int g(){printf("A.g\n"); return(0);}; }; class B : public A{ public: int f() { printf("B.f\n"); return(0); } int g() { printf("B.g\n"); return(0); } }; typedef int A::MEMF(); void dispatch(A *ap, MEMF *fp) { (ap->*fp)(); } main() { A *ap = new A; B *bp = new B; A *objp = bp; MEMF* fp = &A::f; (ap->*fp)(); // calls A::f() (bp->*fp)(); // calls B::f() (objp->*fp)(); // calls B::f() fp = &A::g; (ap->*fp)(); (bp->*fp)(); // should call B::g(), but calls A::g() (objp->*fp)(); // should call B::g()?, but calls A::g() } Text from original postings follows: >From: garya@Solbourne.COM (Gary Aitken) Message-ID: <856@garya.Solbourne.COM> Date: 21 Apr 89 22:29:33 GMT [... Some examples of what he wants to do] The intent is to get the pointer to the proper function via the virtual table. This does, in fact, work with cfront, although you get a warning telling you to use :: (which won't compile, since it expects a class name in front of it instead of a pointer to a class object). [... more stuff deleted] void printf(...) ; class A { public: virtual void f() ; } ; void A::f() {printf("A.f\n");} class B : public A { public: void f() ; } ; void B::f() {printf("B.f\n");} //void dispatch(A *ap, void (*A::fp)()) void dispatch(A *ap, void (*fp)(A*)) { // ap->(*fp)() ; /* how do we do this???? */ (*fp)(ap) ; /* this works for cfront, but what about others?? */ return ; } main() { A *ap ; B *bp ; A *objp ; auto void (*fp)() ; ap = new A ; bp = new B ; objp = (A*) bp ; fp = &A::f ; // static reference to a known function fp = &B::f ; // static reference to a known function fp = &objp->f ; // dynamic ref to unknown (at compile time) virtual function // dispatch(objp, (void (*)()) fp) ; dispatch(objp, (void (*)(A*)) fp) ; } -- Gary Aitken Solbourne Computer Inc. ARPA: garya@Solbourne.COM Longmont, CO UUCP: ...!{boulder,sun}!stan!garya >From: shopiro@alice.UUCP (Jonathan Shopiro) Message-ID: <9228@alice.UUCP> Date: 22 Apr 89 22:53:41 GMT [... quote from Gary's article deleted] For virtual functions, only dynamic (time of call) binding is supported. Do it this way: typedef void A::MEMF(); // MEMF is the type of a member function of A MEMF* fp = &A::f; (There is a way to do it without a typedef, but this is easier). Then the calls go like this: (ap->*fp)() // A::f() (bp->*fp)() // B::f() (objp->*fp)() // B::f() fp = &A::g; // note the variable can refer // to a virtual or non-virtual function (ap->*fp)() // A::g() (bp->*fp)() // A::g() (objp->*fp)() // A::g() There is no way to apply A::f() to a B object (but you're not supposed to want to :-)). [... more quotage from gary's posting deleted] void dispatch(A *ap, MEMF* fp) // this is how { (ap->*fp)(); // This is what I really want... // you got it } --