[comp.lang.c++] pointer to virtual function; how to use it

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
}
--