Downcasting pointer to member function. Is this legal usage?

Viewed 165

I store a list of pointers-to-member-function in an array. I want to index into the array and execute the appropriate function. There will be many arrays listing functions from different classes (all derived from Base), so the class is not known at compile time.

I have the scheme working, but I am not entirely happy with having to use a void pointer at one place, but I can't seem to avoid it.

Is my casting between Base and Derived member function pointers legal according to the C++11 standard(it is working with g++). I would appreciate language lawyer advice !

Below is a stripped down, but runnable version of my code.

#include <iostream>

using std::cout;

//*************************************

class Base {
public:

  typedef int (Base::*BaseThunk)();

  virtual int execute(BaseThunk x) {return 0;}

};

//*************************************

class Derived : public Base {
public:

  typedef int (Derived::*DerivedThunk)();

  int execute(BaseThunk step) {
    return (this->*step)(); //Is this OK ? step is really a DerivedThunk.
  }

  int f1() { cout<<"1\n";return 1;}
  int f2() { cout<<"2\n";return 2;}
  int f3() { cout<<"3\n";return 3;}

  static DerivedThunk steps[];
};

//Here is an array of pointers to member functions of the Derived class.
Derived::DerivedThunk Derived::steps[] = {&Derived::f1, &Derived::f2, &Derived::f3};

//*************************************

class Intermediate : public Base {
public:

  void f(void *x) { //I am worried about using void pointer here !

    BaseThunk *seq = reinterpret_cast<BaseThunk *>(x);

    Derived d;
    d.execute(seq[2]);
  }

};

//*************************************

int main() {
  Intermediate b;

  b.f(&Derived::steps);
}
1 Answers

Your concern about void* is well founded: this is undefined behavior because (in Intermediate::f) you’re performing pointer arithmetic on, and reading through, a pointer that doesn’t match the array’s type.

The good news is that there is an easy fix: since the purpose of your arrays is to have derived-class functions be called given only a Base& and a BaseThunk, you can store that type:

Base::BaseThunk Derived::steps[]=
  {static_cast<BaseThunk>(&Derived::f1),
   …};

The static_casts are somewhat verbose, but totally legitimate so long as you use the resulting BaseThunk objects with an object whose type is or is derived from Derived. You don’t even have to get a Derived* first:

int Base::execute(BaseThunk x)  // no need to be virtual
{return (this->*x)();}
Related