Topic: Casting of pointers to members [expr.static.cast] 5.2.8.9


Author: pstemari@erinet.com (Paul J. Ste. Marie)
Date: 1995/06/14
Raw View
Hear, hear.  While this does have some issues regarding vtable
layouts, that can be dealt with, and in general this provides for a
decent symmetry in the language.  While writing templates for
"bound" member function pointers is certainly possible, you'd need
one for every possible function signature.

 --Paul J. Ste. Marie, pstemari@well.sf.ca.us, pstemari@erinet.com

Contact your Senators today to oppose the Comunications Decency Act
(S.652, Title IV)!





Author: Per Angstrom <eri.edt.edtpang@memo.ericsson.se>
Date: 1995/06/09
Raw View
I know this subject has been debated before, but I have not seen it come
to a conclusion, at least not one I'm satisfied with. The point of this
posting is to restart the discussion, hoping to generate a lot of informed
comment. Here it is:


The April Draft, section 5.2.8 [expr.static.cast], says:

"9 An rvalue of type "pointer to member of D of type cv1 T" can  be  con
  verted  to  an  rvalue of type "pointer to member of B of type cv2 T",
  where B is a base class (_class.derived_) of D, if  a  valid  standard
  conversion  from "pointer to member of B of type cv2 T" to "pointer to
  member of D of type cv2 T" exists (_conv.mem_), and cv2  is  the  same
  cv-qualification  as, or greater cv-qualification than, cv1.  The null
  member pointer value (_conv.mem_) is  converted  to  the  null  member
  pointer  value of the destination type.  If class B contains or inher
  its the original member, the resulting pointer to member points to the
  member in class B.  Otherwise, the result of the cast is undefined."

I would like to change the last sentence to something like the following:

"Otherwise, the resulting pointer to member points to the member in
class D. Using the resulting pointer on an object that is not of
class D, or does not have D as an unambiguous base class, results in
undefined behaviour."

I am not a "language lawyer", so my terminology may not be appropriate,
but I hope my point comes through, namely, to make programs such as
my example below have defined behaviour.

It can be argued that template callback classes can solve the problem,
but after quite a lot of work on such a solution, I still think that it
is too complicated, especially when casting of pointers to members is so
easy, at least on the part of the developer.

IMO, an acceptable alternative should be non-intrusive, e.g., it should
not force member functions that may be used in callbacks to accept
void pointers as arguments, or use other, non-obvious argument passing
schemes.

I have also seen claims to the effect that having to resort to casting
of pointers to members is a sign of a deficient design, and that
virtual functions will do the trick. This might be true in some cases,
but consider the situation where a class defines a number of member
functions having the same signature; in this case it is not practical to
use virtual functions.

I suppose the main reason for disallowing the construct is type safety;
there is no guarantee that a typecast pointer to member will be used on
an appropriate object. To this I can say that developers who use this
technique probably know what they are doing, they are consenting adults,
and are willing to take full responsibility for their actions. Furthermore,
the safety problem can be virtually eliminated by the use of template
functions, as demonstrated in my example.

Questions:
1) Is there any hope the Standard will allow this construct?
2) If not, is there a non-intrusive alternative that is not overly
complex?
3) (SUN-specific) If not (1), will CC maintain its current behaviour, for
backward compatibility?

---
Per Angstrom (eri.edt.edtpang@memo.ericsson.se)
---

Example (tested on SUN CC 4.0.1):

#include <stdio.h>

class aclass {
};

typedef void (aclass::*pfn1)();
typedef int (aclass::*pfn2)( int );


class bclass: public aclass {
public:
 void
 foo1()
  { puts( "bclass::foo1" ); }

 void
 foo2()
  { puts( "bclass::foo2" ); }

 void
 foo3()
  { puts( "bclass::foo3" ); }

 int
 bar( int i )
  {
  puts( "bclass::bar" );
  return i + i;
  }
};

class cclass: public aclass {
public:
 void
 foo()
  { puts( "cclass::foo" ); }

 int
 bar( int i )
  {
  puts( "cclass::bar" );
  return i - i;
  }
};


void
call_function(
 aclass * pAclass,
 pfn1 pfn
 )
{
 try
  {
  // lots of code
  (pAclass->*pfn)();
  // lots of code
  }

 // lots of catch clauses

 catch ( ... )
  {
  // lots of code
  }
}

template < class T >
inline
void
call_func( T * p, void (T::*pfn)() )
{
 call_function( p, (pfn1) pfn );
}

int
call_function(
 aclass * pAclass,
 pfn2 pfn,
 int i
 )
{
 int rv = 0;
 try
  {
  // lots of code
  rv = (pAclass->*pfn)( i );
  // lots of code
  }

 // lots of catch clauses

 catch ( ... )
  {
  // lots of code
  }
 return rv;
}

template < class T >
inline
int
call_func( T * p, int (T::*pfn)( int ), int i )
{
 return call_function( p, (pfn2) pfn, i );
}


int
main()
{
 bclass b;
 call_func( &b, bclass::foo1 );
 call_func( &b, bclass::foo2 );
 call_func( &b, bclass::foo3 );
 call_func( &b, bclass::bar, 1 );

 cclass c;
 call_func( &c, cclass::foo );
 call_func( &c, cclass::bar, 1 );
}