Re: [ROOT] C++ Question

From: George Heintzelman (georgeh@rentec.com)
Date: Thu Mar 15 2001 - 15:20:19 MET


> Hi Rooters!
> I have a C++ question....It is related to pointer to functions of a
> class. I want define in a base class a method (name RK in the example)
> which used pointer to methods of a derived class...and I don't now how
> to do. Here is what I do (and which of course don't work):
> 
> the base class A:
> 
> class A {
>    void (*Fct)(double t,double *X,double *dX);
>    void RK(...);
> };
>  RK methods use the pointer Fct.
> 
> then a class B:
> 
> class B : public A {
>     ...
>    void EDP1(double t,double *X,double *dX);
>    void EDP2(double t,double *X,double *dX);
>    void Evol(...);
> };
> 
> Now in the method Evol(...) I want to use A::RK(...) with either EDP1 or
> EDP2 for the pointer Fct (e.g. Fct=EDP1) but the cast don't work.
> 
> Is there any way to do what I want?

Sort of. First of all, you need to use the pointer-to-member syntax. To 
declare a pointer-to-member of a class, you use:

void (B::* Fct)(double t, double *X, double *dX);

(Fct is a pointer to a member of B which is a function taking double, 
double*, double* and returning void).

If b is an instance of B, you would call the function pointed to with 
Fct with:

b.*Fct(t,&x,&dx);

Likewise if pb is a pointer to an instance of b, you would use:

pb->*Fct(t,&x,&dx);

Okay, so far so good. Problems will arise though, because the 
inheritance scheme for pointer-to-function is contravariant. That is to 
say, a pointer-to-member-of-Base can be cast to a pointer-to-member-of-D
erived, but not the other way around. This seems backwards at first, 
but is necessary for correct behavior.

The reason for this is simple: if a is a Derived::*, you have no way of 
knowing whether it was originally defined in the Base class or in the 
Derived class. But if it were defined in the Derived class, you may not 
be able to meaningfully call it with an object of type Base, or of some 
other derived class of Base. The converse, however, is true; if the 
object is Derived, you know you can meaningfully execute Base class 
functions.

The upshot is that you won't be able to assign EDP1 or EDP2 to the 
variable Fct in class A, if Fct were defined as pointer-to-member of A.

You have a few choices for solutions:
1) Avoid the whole problem altogether. If you don't need the object 
itself inside EDP1 or EDP2, you can declare them as static. Pointers to 
static member functions are just pointers to ordinary functions, and 
you're done.
2) Maybe EDP1 and EDP2 really ought to be members of A, not members of 
B; ie, they don't depend on any B-specific information to do their 
duty. If so, it's easy and you're done.
3) If neither of those is true, you probably don't want to be using 
pointers-to-members, or rather you might want to be using them, but 
through the mechanism of a virtual function call inside RK, which in B 
happens to use a pointer-to-member (of B, now). Remember that RK has to 
be callable by any instance of A, and may not turn out to be a B 
object, so other descendants of A will have to supply their own 
behavior. This is exactly where a virtual function call is indicated. 
You're not done; you've got some thinking to do about what really 
belongs in the base class and what in the derived class, but at least 
you're on your way.

On a root-related point: I have no idea how well CINT handles 
pointers-to-members. Given that it is a fairly involved syntax and 
complicated language issue, I would be very careful using this kind of 
code in CINT until I was convinced that it worked right.

George Heintzelman
georgeh@aya.yale.edu



This archive was generated by hypermail 2b29 : Tue Jan 01 2002 - 17:50:39 MET