Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!tut.cis.ohio-state.edu!rutgers!rochester!uhura.cc.rochester.edu!sunybcs!bingvaxu!leah!itsgw!steinmetz!crdgw1!uunet!oresoft!dan From: dan@oresoft.uu.net (Daniel Elbaum) Newsgroups: comp.lang.c++ Subject: Re: pointer to virtual function; how to use it Message-ID: <672@oresoft.uu.net> Date: 25 Apr 89 02:10:56 GMT Reply-To: dan@oresoft.uu.net (Daniel Elbaum) Organization: Oregon Software, Portland, OR Lines: 237 [ 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 <> error: bus error (or something nasty like that) 1 error */ #include 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 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 } --