[comp.lang.c++] pointers to member functions

asc@concurrent.co.uk (Andy Chittenden) (12/01/89)

// We are using pointers to member functions with version 1.2.  Below is
// cut down example.  This compiles fine under version 1.2 but doesn't
// under version 2.

class base {
  public:
    int fn1(int p);
};

class derived : public base {
  public:
    int fn2(int p);
};

typedef int (base::*EH_function) (int);
class dispatcher {
  public:
    dispatcher(EH_function p);
};

main()
{
	dispatcher a(base::fn1);
	dispatcher b(derived::fn2);	// fails to compile under v2
}

// I can understand why the second line does not compile (derived::fn2
// is not a function of base).  However, how is one meant to achieve the
// above effect under v2 (pass an arbitrary function of a derived class
// with matching parameters)?

Thanks in anticipation, Andy Chittenden

ark@alice.UUCP (Andrew Koenig) (12/02/89)

In article <950@sl10c.concurrent.co.uk>, asc@concurrent.co.uk (Andy Chittenden) writes:

[example that declares `dispatcher' as taking a pointer to a member of
class `base'

> 	dispatcher a(base::fn1);
> 	dispatcher b(derived::fn2);	// fails to compile under v2

> // I can understand why the second line does not compile (derived::fn2
> // is not a function of base).  However, how is one meant to achieve the
> // above effect under v2 (pass an arbitrary function of a derived class
> // with matching parameters)?

The `dispatcher' function takes a pointer to a member of class `base.'
There may be some members of class `derived' that are not members of
class `base,' so you can't pass any member of `derived' to a function
that takes a pointer to a member of `base' as its argument.

In particular, if fn2 were a member of `base' you would have been able
to say

	dispatcher b(base::fn2);

and there would have been no problem.

Watch this, though.  Every member of class `base' is also a member of
class `derived.'  Therefore you CAN pass a member of class `base'
to a function that expects a pointer to a member of class `derived'!
Example:

	void f(int (derived::*)(int));

	main()
	{
		f(derived::fn2);	// OK
		f(base::fn1);		// also OK!
	}

This may seem backwards at first, but if you think about it for a while
you will see that it has to be this way, version 1 to the contrary
notwithstanding.

This phenomenon is often called `contravariance.'
-- 
				--Andrew Koenig
				  ark@europa.att.com

roger@procase.UUCP (Roger H. Scott) (12/07/89)

In article <950@sl10c.concurrent.co.uk> asc@concurrent.co.uk (Andy Chittenden) writes:
>...
>// However, how is one meant to achieve the
>// above effect under v2 (pass an arbitrary function of a derived class
>// with matching parameters)?

1. Declare any/all such functions to be virtual functions in the base class.
   This is the "right" way.

2. Cast.  Have you ever seen a cast involving a pointer-to-member-function
   *without* a typedef?  This is the "wrong" way.

fredriks@kuling.UUCP (Fredrik Stax{ng) (12/12/89)

	I think the following piece of code would work. In The C++
Language Reference Manual 2.0 sec 5.4 it says:
	Pointer to member may be explicitly converted ... when the two
	type are pointers to member functions of classes derived from each
	other.
However my C++ compiler refuses to compile this (error: cast to member).
It is HCR C++ ver 2.01. 
	Is this supposed to work? If not, why not? (I know it is unsafe,
but it would be very un-C to disallow it because of that.)


class Base {
 public:
    void	Error() ;
} ;

typedef void (Base::*PMemF)() ;

class Derived: public Base {
 public:
    void	F1() ;
} ;


    
main() {
    PMemF	P3 ;

    P3 = (PMemF)(Derived::F1) ;
}


Fredrik Stax{ng                | C looks like a mix of Algol and TECO.
CS Student@Uppsala University  |              -- KPJ Jaakkola
fredriks@kuling.docs.uu.se     |
    
    






-

-- 
Fredrik Stax{ng                | C looks like a mix of Algol and TECO.
CS Student@Uppsala University  |              -- KPJ Jaakkola
fredriks@kuling.docs.uu.se     |

asc@concurrent.co.uk (Andy Chittenden) (12/18/89)

Thanks to both Roger Scott and Andrew Koenig for responding to my query.
However, I do have a problem with the answers - see below.

roger@procase.UUCP (Roger Scott) replies:
> In article <950@sl10c.concurrent.co.uk> asc@concurrent.co.uk (Andy Chittenden) writes:
> >...
> >// However, how is one meant to achieve the
> >// above effect under v2 (pass an arbitrary function of a derived class
> >// with matching parameters)?
> 
> 1. Declare any/all such functions to be virtual functions in the base class.
>    This is the "right" way.

I am using these functions as part of an exception handling mechanism.
I've also ideas for using them as part of an event driven scheduler
where each step in a cycle is defined by a function on a class with the
same parameters - it seems a shame that I must define all possible
functions on the base class where the actual functions to be despatched
are on the derived class ( a disc scheduler would be separate from a
terminal scheduler).  There must surely be another way.

> 
> 2. Cast.  Have you ever seen a cast involving a pointer-to-member-function
>    *without* a typedef?  This is the "wrong" way.

Why is this the "wrong" way?  I have tried casting but it does not
work.

Andrew Koenig also replies:

> ....
> Watch this, though.  Every member of class `base' is also a member of
> class `derived.'  Therefore you CAN pass a member of class `base'
> to a function that expects a pointer to a member of class `derived'!
> Example:
> 
> 	void f(int (derived::*)(int));
> 
> 	main()
> 	{
> 		f(derived::fn2);	// OK
> 		f(base::fn1);		// also OK!
> 	}
> 
> This may seem backwards at first, but if you think about it for a while
> you will see that it has to be this way, version 1 to the contrary
> notwithstanding.

I hope you're not seriously suggesting that I declare my typedef for the
derived type - this goes against the paradigm.  I understand that the
above would be valid as every member of base is also a member of
derived.

> 
> This phenomenon is often called `contravariance.'
 
I understand why the v2 compiler complains about my example.  What I was
looking for was a way to use the "feature" that was available in v1.2 of
cfront (see my reply to Roger above).  One way may be to use non-class
based functions - this goes against the general principle of keeping
things object-oriented.

Andy Chittenden

asc@concurrent.co.uk (Andrew Chittenden,ADT) (01/11/90)

Thanks to both Roger Scott and Andrew Koenig for responding to my query.
However, I do have a problem with the answers - see below.

roger@procase.UUCP (Roger Scott) replies:
> In article <950@sl10c.concurrent.co.uk> asc@concurrent.co.uk (Andy Chittenden) writes:
> >...
> >// However, how is one meant to achieve the
> >// above effect under v2 (pass an arbitrary function of a derived class
> >// with matching parameters)?
> 
> 1. Declare any/all such functions to be virtual functions in the base class.
>    This is the "right" way.

I am using these functions as part of an exception handling mechanism.
I've also ideas for using them as part of an event driven scheduler
where each step in a cycle is defined by a function on a class with the
same parameters - it seems a shame that I must define all possible
functions on the base class where the actual functions to be despatched
are on the derived class ( a disc scheduler would be separate from a
terminal scheduler).  There must surely be another way.

> 
> 2. Cast.  Have you ever seen a cast involving a pointer-to-member-function
>    *without* a typedef?  This is the "wrong" way.

Why is this the "wrong" way?  I have tried casting but it does not
work.

Andrew Koenig also replies:

> ....
> Watch this, though.  Every member of class `base' is also a member of
> class `derived.'  Therefore you CAN pass a member of class `base'
> to a function that expects a pointer to a member of class `derived'!
> Example:
> 
> 	void f(int (derived::*)(int));
> 
> 	main()
> 	{
> 		f(derived::fn2);	// OK
> 		f(base::fn1);		// also OK!
> 	}
> 
> This may seem backwards at first, but if you think about it for a while
> you will see that it has to be this way, version 1 to the contrary
> notwithstanding.

I hope you're not seriously suggesting that I declare my typedef for the
derived type - this goes against the paradigm.  I understand that the
above would be valid as every member of base is also a member of
derived.

> 
> This phenomenon is often called `contravariance.'
 
I understand why the v2 compiler complains about my example.  What I was
looking for was a way to use the "feature" that was available in v1.2 of
cfront (see my reply to Roger above).  One way may be to use non-class
based functions - this goes against the general principle of keeping
things object-oriented.

Apologies for the late response to these replies, but we have been
having trouble with our news feed.

Andy Chittenden

roger@decvax.UUCP (Roger H. Scott) (01/13/90)

In article <967@sl10c.concurrent.co.uk> asc@concurrent.co.uk (Andrew Chittenden,ADT) writes:
>However, I do have a problem with the answers - see below.
>
>> >// However, how is one meant to achieve the
>> >// above effect under v2 (pass an arbitrary function of a derived class
>> >// with matching parameters)?
>> 
>> 1. Declare any/all such functions to be virtual functions in the base class.
>>    This is the "right" way.
>
>I am using these functions as part of an exception handling mechanism.
>I've also ideas for using them as part of an event driven scheduler
>where each step in a cycle is defined by a function on a class with the
>same parameters - it seems a shame that I must define all possible
>functions on the base class where the actual functions to be despatched
>are on the derived class ( a disc scheduler would be separate from a
>terminal scheduler).  There must surely be another way.
If the derived class functions all take that same parameters (and return the
same type) then the solution to your problem is to create a virtual function
in the base class (with some generic name) and redefine it in each derived
class.  A poorly understood fact about pointers-to-member-functions is that
a pointer to a *virtual* member function is really like a "message" in a
language like SmallTalk - when "called" it will invoke the implementation
suitable to the object *on which it is called*, regardless of which class was
named when its address was taken.  E.g.,
    struct base {
	virtual void f();
    };
    typedef void (base::baseMF)();

    struct derived : public base {
	void f(); // redefine
    };

    void dispatch(base *p, baseMF *mf) {
	(p->*mf)();
    }

    void foo() {
	base b;
	derived d;
	dispatch(&b, &base::f); // invokes base::f()
	dispatch(&d, &base::f); // invokes derived::f()
	dispatch(&d, &derived::f); // ERROR - I lost my battle w/ AT&T on this
				   // one - my claim was that &derived::f is a
				   // synonym for &base::f - AT&T disagrees
    }

I should add that in my experience 9 out of 10 uses of pointers-to-member-
functions are needless.  Their perceived need is usually the result of poor
design of some other aspect of a system - they can often be eliminated by
making the right member functions virtual in the right places.

>> 2. Cast.  Have you ever seen a cast involving a pointer-to-member-function
>>    *without* a typedef?  This is the "wrong" way.
I suspect people have mis-read this semi-editorial semi-rhetorical comment.
My point was that noone wants to read

	f(p, q, (a *(b::*)())&c::d);

rather than

	typedef a *(b::bMF)();
	...
	f(p, q, (bMF *)&c::d);

which is bad enough in itself.

>
>Why is this the "wrong" way?  I have tried casting but it does not
>work.
Casting is a crock - it is an admission that you and your programming language
don't see eye-to-eye and negotiations have broken down.  It is very unfortunate
that Bjarne's book gives the strong impression that "void *"s and casts are
the stock-and-trade of C++ programming.  When you say you have tried casting
and "it does not work", what do you mean?  Did your code not compile?  If so,
either it is syntactically incorrect [not unlikely unless you are using typedefs,
and even then ...] and/or your C++ translator is flawed [known to be true for
1.2 cfront from AT&T, likely elsewhere].  If this is the case use typedefs,
check carefully, and try again.  Did your code compile but execute "incorrectly"
(i.e. - not as you expected)?  I would have to see your code to tell you what
is wrong in this case.

idf@cs.bham.ac.uk (Ian Fitchet <FitchetI>) (12/06/90)

 I came across a problem while trying to explain pointers to functions
from a C perspective in a C++ tutorial. This failed so I looked it up
in a book which said that the following code should be valid.

#include <stream.h>
class foo {
  int a;
public:
  foo() { a = 1; }
  void print() { cout << a << "\n"; }
  void bar() { a = 3; }
};
main()
{
  void (foo::*ptr2bar)() = &foo::bar;
  foo bletch;
  
  cout << "Initially a = " ;
  bletch.print();
  bletch.(*ptr2bar)();
  cout << "Finally a = ";
  bletch.print();
}

 The Sun C++ compiler (CC -v prints out "Sun C++ 2.0 FCS - 10/20/89")
does the following:

%cd /mbhome/staff/idf/src/cc/
%CC -o foo q.c
%CC  q.c:
%"q.c", line 36: error: syntax error
%1 error
%cc   -o /mbhome/staff/idf/src/cc/foo -c -I/usr/CC/incl 
%
%Compilation exited abnormally with code 1 at Wed Dec  5 16:03:08

 ie a syntax error on the call.

 g++ version 1.37.2 beta (based on GCC 1.37) does the following:

%cd /mbhome/staff/idf/src/cc/
%g++ -o foo q.c
%q.c: In function int main ():
%q.c:30: parse error before `*'
%
%Compilation exited abnormally with code 1 at Wed Dec  5 16:32:09

 ie gives a parse error on the declaration of ptr2bar
 void (foo::*ptr2bar)() = &foo::bar;

 Is it me, or are the compilers to blame?

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Ian Fitchet                               | "Have you heard the one about the
FitchetI@uk.ac.bham.cs                    | orangutan and the pomegranate?"
FitchetI%cs.bham.ac.uk@nsfnet-relay.ac.uk | 	- Gerald the Gorilla (NTNO'CN)

ianhogg@cs.umn.edu (Ian J. Hogg) (12/07/90)

In article <1369@christopher-robin.cs.bham.ac.uk> idf@cs.bham.ac.uk (Ian Fitchet <FitchetI>) writes:
>
> I came across a problem while trying to explain pointers to functions
>from a C perspective in a C++ tutorial. This failed so I looked it up
>in a book which said that the following code should be valid.
>
>#include <stream.h>
>class foo {
>  int a;
>public:
>  foo() { a = 1; }
>  void print() { cout << a << "\n"; }
>  void bar() { a = 3; }
>};
>main()
>{
>  void (foo::*ptr2bar)() = &foo::bar;
>  foo bletch;
>  
>  cout << "Initially a = " ;
>  bletch.print();
>  bletch.(*ptr2bar)();
   ^^^^^^^^^^^^^^^^^^^^^^
   (bletch.*ptr2bar)();

>  cout << "Finally a = ";
>  bletch.print();
>}
>

Compiles and runs fine on HP's cfront 2.0.  Lippman's book discusses this on
pages 211-212.

==============================================================================
Ian Hogg   ian@dms.cdc.com

stu4@larry.mcrcim.mcgill.edu (Craig Jones) (01/19/91)

Let me say at the outset, I am new to C++ (but loving it) so please
be patient with me if this is a simple question...

I am trying to pass a pointer of a function (that is a member of a 
class) to the ftw() function (taken from <ftw.h>).

My class is defined as follows:

class fileclass
{
//  some variables here 

        int show_file (char*, struct stat*, int);
// the above must be defined like this because of 
// restrictions in ftw().

  public:
	fileclass ();
        void show ();
        ~fileclass ();
};

And the functions as:

void fileclass::show()
{
	ftw(path, &show_file, 100);
}                 ^^^^^^^^^^^ Not sure what to put here.
                  
int fileclass::show_file(char* path, struct stat* status, int depth)
{
	printf("%s\n", path);
	return 0;
}

The question is: How do I write the second parameter to
                 the ftw function?

Thanks for any time and help....

Craig
bnrmtl!stu4@larry.mcrcim.mcgill.edu

garnett@shera..bellcore.com (Michael Garnett) (01/21/91)

In article <1991Jan18.222713.26249@scrumpy@.bnr.ca> bnrmtl!stu4@larry.mcrcim.mcgill.edu writes:
>I am trying to pass a pointer of a function (that is a member of a 
>class) to the ftw() function (taken from <ftw.h>).
>
	You can't pass a member function to ftw to call.  ftw does not know
	about your class, and cannot thus provide the "this" paramater. Methods
	must be called ON an object, ftw has no such object to call your function 
	on.
	A pointer to the member function showfile of fileclass is of type:
	int (fileclass::*)(const char*, stat*, int);
	This is not the same as regular pointers to functions.

Michael

stu4@larry.mcrcim.mcgill.edu (Craig Jones) (01/23/91)

This is in reference to an earlier posting I made asking
how to pass pointers of member functions (specifically
to ftw()).

Here is an example of how to do it (based on many replies
to my plea) ....

#include <stream.h>
#include <ftw.h>

// Class Definition

class fooclass
{
  public:
	static int greeting(char*, struct stat*, int);
//	^^^^^^ NECESSARY TO BE ABLE TO PASS THIS FUNCTION
//	       TO ftw()

	void call_ftw ();
	void call_ftw2(int (*function)(char*, struct stat*, int));
};

void fooclass::call_ftw()
{
	int return_int;

	return_int = ftw ("..", &fooclass::greeting, 10);
}

void fooclass::call_ftw2 (int (*function)(char*, struct stat*, int))
//
//  Calls ftw() and passes a pointer to the function to ftw.
//
{
	int return_int;

	return_int = ftw ("..", function, 10);
}

int fooclass::greeting(char* path, struct stat* status, int number)
{
	cout << "Now in greeting " << path << "\n";
	return 0;
}


// NON member function definition

int nonclass_greeting (char* path, struct stat* status, int number)
{
	cout << "Now in non class greeting " << path << "\n";
	return 0;
}

main()
{
	fooclass fooobj;

//  Call member function that calls ftw with a pointer to a 
//  member function
	fooobj.call_ftw();

	cout << "\n Now going to do next...\n\n";
	

//  Pass outside function as a pointer to be used inside
    a class member function
	fooobj.call_ftw2(nonclass_greeting);
}


Many thanks for all the help that was given.

-----------------------------------------------------------
  Craig Jones		bnrmtl!stu4@larry.mcrcim.mcgill.edu

  Bell Northern Research, Montreal.
-----------------------------------------------------------

Roy.Browning@p0.f506.n106.z1.fidonet.org (Roy Browning) (01/24/91)

In a message of <Jan 18 22:27>, Craig Jones (1:106/88@fidonet) writes:
 >void fileclass::show()
 >{
 >        ftw(path, &show_file, 100);
 >}                 ^^^^^^^^^^^ Not sure what to put here.
 >                  
 >int fileclass::show_file(char* path, struct stat* status, int depth)
 >{
 >        printf("%s\n", path);
 >        return 0;
 >}

Craig:

        I you declare the "show_file" member to be "static" then create a wrapper function along the following format.

inline _cdecl int
WrapperShow_File(char* path, stat* status, int depth)
{
        return classname::show_file(path, status, depth);
}

If "show_file" isn't static then you have to call it via a specific instance.

inline _cdecl int
WrapperShow_File(char* path, stat* status, int depth)
{
        return instancename.show_file(path, status, depth);
}

Substitute the 'C' wrapper function for the member name in the call to "ftw".  This should work per your request.

                         On the learning C++urve,

                               Roy Browning

dgorton@jupiter.newcastle.edu.au (David Mark Gorton) (06/07/91)

In Stroustrups "The C++ Programming Language" pp 153-154 an example is given 
of how to "fake" the expression of the type of a pointer to a member function. 
Has this been standardised ? (I dont have access to a copy of the ARM at the 
moment, though I have one on order.)

I am writing a class definition for TCP connections using the Transport Level
Interface and have the following class definition

class tcp {     // TCP connection
    private :
	// Data members
	...
    public :
	...
        void ConnectionRelease(void);
	...
};

The sample program in the Sun Network Programming Guide has a C function

connrelease()
{
    ...
}

and in the server function

run_server(...)
{
    signal(SIGPOLL, connrelease);
    if (ioctl(conn_fd, I_SETSIG, S_INPUT) < 0) 
	...
}
	
I wish to do the equivalent in C++ using ConnectionRelease() from class tcp.

The header file for <sys/signal.h> on our SUN 3/80 has 

typedef void SIG_FUNC_TYP(int);
typedef SIG_FUNC_TYP *SIG_TYP;

extern "C" {
        SIG_TYP signal(int, SIG_TYP);
	...
};

The AT&T C++ Compiler accepts the following

    //
    // Fake type of pointer to member - See <signal.h> and "The C++ 
    // Programming Language" - Bjarne Stroustrup, pp 153-154 for details
    //
    SIG_TYP pf = SIG_TYP(&tcp::ConnectionRelease);

    signal(SIGPOLL, pf);
    if (ioctl(fdConnection, (int) I_SETSIG, (caddr_t) S_INPUT) < 0)


Is this correct (and the standard) ??

Thanks in advance,
Dave

---------------------------------------------------------------------------
Dave Gorton.     Department of Computer Science,
                 University of Newcastle,
                 Newcastle, 2308, Australia.       .-_|\
Telephone   :    (049) 216 034                    /     \
                                                  \.--._/
Internet    :    dgorton@cs.newcastle.edu.au           v
	 	 csdmg@cc.newcastle.edu.au
---------------------------------------------------------------------------

sarima@tdatirv.UUCP (Stanley Friesen) (06/11/91)

In article <dgorton.676272036@jupiter.newcastle.edu.au> dgorton@jupiter.newcastle.edu.au (David Mark Gorton) writes:
>In Stroustrups "The C++ Programming Language" pp 153-154 an example is given 
>of how to "fake" the expression of the type of a pointer to a member function. 
>Has this been standardised ?

Nope. Quite the opposite, it is being thoroughly depreciated.  (Indeed it
will not work properly with multiple inheritance).  It has been replaced in
cfront 2.0 and later by a new composite type - the 'pointer to member'.

Try to get at least one book about level 2.X c++.  Lippman's is the best.
Any book covering cfront 2.0 or later will explain pointers to members.

[And of course, if your c++ compiler is only a level 1.2 compiler, upgrade it,
it is radically incompatible with all current compilers].
-- 
---------------
uunet!tdatirv!sarima				(Stanley Friesen)