F
Francesco S. Carta
Hi there,
after reading an article complaining that pointers to member functions
are misconceived in C++, and after reading a comment about them saying
that they happen to be a rarely necessary feature - if ever necessary at
all - I decided to investigate them to see how I could take advantage of
them, and I also decided to post this message to get the group's feedback.
A pointer to member function is a pointer that can memorize the
displacement of any member function so that we can call that function on
an appropriate object without having to know which function we are
calling in particular - though, the object and the member function must
belong to the same class (please forgive me if this description is
oversimplified, but also please correct me if it happens to be wrong).
I've come to think that this feature can be really useful if we have
objects that can perform different actions in case they are used in a
context or another.
The approach to take (having not pointers to member functions at our
disposal) would be to use a switch or some ifs any time we have to
perform the action, so that we can choose the right function for the
current context or input.
If we happen to have several operations to perform, but the context /
input rarely changes, having to decide which action to perform at each
call is a waste of cycles.
With pointers to member functions we can store our decision about which
member function to call and speed up the whole process.
Since I'm biased towards interfaces and games, the example I post here
below is just a stub of a game where we have a common interface for
several different moving objects - I think the approach I've taken in
that example could lead to a nice infrastructure where different objects
must interact (or refuse to interact) with different environments.
I'd like to ask you if you can come up with some real life examples
where you have taken advantage of pointers to member functions -
unfortunately, being an hobbyist, my best example comes straight from
the top of my head ;-)
Thanks for your attention,
cheers,
Francesco
//-------
#include <iostream>
#include <string>
struct ThreeD {
int x;
int z;
int y;
ThreeD(int x = 0, int z = 0, int y = 0) : x(x), z(z), y(y) {}
};
typedef ThreeD Position;
typedef ThreeD Movement;
struct Crash {
Crash(std::string message = "") : message(message) {};
std::string what() const {
return message;
}
std::string message;
};
enum Field {
ground,
water,
air
};
class MovingObject {
public:
MovingObject(Position pos = Position()) : pos(pos) {}
virtual void enter(Field) {}
virtual void move(Movement) {}
protected:
Position pos;
};
class Car : public MovingObject {
public:
Car(Position pos = Position()) : MovingObject(pos), action(0) {}
void enter(Field f) {
switch (f) {
case water:
action = &Car::sink;
break;
case ground:
action = &Car::run;
break;
case air:
action = &Car::fall;
break;
}
}
void move(Movement m) {
if (action) {
(this->*action)(m);
}
}
private:
void run(Movement m) {
pos.x += m.x;
pos.z += m.z;
}
void sink(Movement) {
pos.x -= 1;
}
void fall(Movement) {
pos.x -= 10;
}
void (Car::*action)(Movement);
};
class Airplane : public MovingObject {
public:
Airplane(Position pos = Position())
: MovingObject(pos), action(0) {}
void enter(Field f) {
switch (f) {
case water:
throw Crash("This airplane cannot fly into water");
case ground:
throw Crash("This airplane cannot fly into ground");
case air:
action = &Airplane::fly;
}
}
void move(Movement m) {
if (action) {
(this->*action)(m);
}
}
private:
void fly(Movement m) {
pos.x += m.x;
pos.y += m.y;
pos.z += m.z;
}
void (Airplane::*action)(Movement);
};
class Boat : public MovingObject {
public:
Boat(Position pos = Position()) : MovingObject(pos), action(0) {}
void enter(Field f) {
switch (f) {
case water:
action = &Boat::navigate;
break;
case ground:
throw Crash("This boat cannot navigate onto the ground");
break;
case air:
action = &Boat::fall;
break;
}
}
void move(Movement m) {
if (action) {
(this->*action)(m);
}
}
private:
void navigate(Movement m) {
pos.x += m.x;
pos.z += m.z;
}
void fall(Movement) {
pos.x -= 10;
}
void (Boat::*action)(Movement);
};
class Submarine : public MovingObject {
public:
Submarine(Position pos = Position())
: MovingObject(pos), action(0) {}
void enter(Field f) {
switch (f) {
case water:
action = &Submarine::navigate;
break;
case ground:
throw Crash("Submarines cannot navigate into the ground");
break;
case air:
action = &Submarine::fall;
break;
}
}
void move(Movement m) {
if (action) {
(this->*action)(m);
}
}
private:
void navigate(Movement m) {
pos.x += m.x;
pos.y += m.y;
pos.z += m.z;
}
void fall(Movement) {
pos.x -= 10;
}
void (Submarine::*action)(Movement);
};
int main() {
MovingObject* car = new Car;
car->enter(ground);
car->move(Movement(5));
MovingObject* airplane = new Airplane;
airplane->enter(air);
airplane->move(Movement(10,0,3));
MovingObject* boat = new Boat;
boat->enter(water);
boat->move(Movement(2,2));
MovingObject* submarine = new Submarine;
submarine->enter(water);
submarine->move(Movement(0,0,-3));
try {
submarine->enter(ground);
} catch (Crash c) {
std::cout << c.what() << std::endl;
}
return 0;
}
//-------
after reading an article complaining that pointers to member functions
are misconceived in C++, and after reading a comment about them saying
that they happen to be a rarely necessary feature - if ever necessary at
all - I decided to investigate them to see how I could take advantage of
them, and I also decided to post this message to get the group's feedback.
A pointer to member function is a pointer that can memorize the
displacement of any member function so that we can call that function on
an appropriate object without having to know which function we are
calling in particular - though, the object and the member function must
belong to the same class (please forgive me if this description is
oversimplified, but also please correct me if it happens to be wrong).
I've come to think that this feature can be really useful if we have
objects that can perform different actions in case they are used in a
context or another.
The approach to take (having not pointers to member functions at our
disposal) would be to use a switch or some ifs any time we have to
perform the action, so that we can choose the right function for the
current context or input.
If we happen to have several operations to perform, but the context /
input rarely changes, having to decide which action to perform at each
call is a waste of cycles.
With pointers to member functions we can store our decision about which
member function to call and speed up the whole process.
Since I'm biased towards interfaces and games, the example I post here
below is just a stub of a game where we have a common interface for
several different moving objects - I think the approach I've taken in
that example could lead to a nice infrastructure where different objects
must interact (or refuse to interact) with different environments.
I'd like to ask you if you can come up with some real life examples
where you have taken advantage of pointers to member functions -
unfortunately, being an hobbyist, my best example comes straight from
the top of my head ;-)
Thanks for your attention,
cheers,
Francesco
//-------
#include <iostream>
#include <string>
struct ThreeD {
int x;
int z;
int y;
ThreeD(int x = 0, int z = 0, int y = 0) : x(x), z(z), y(y) {}
};
typedef ThreeD Position;
typedef ThreeD Movement;
struct Crash {
Crash(std::string message = "") : message(message) {};
std::string what() const {
return message;
}
std::string message;
};
enum Field {
ground,
water,
air
};
class MovingObject {
public:
MovingObject(Position pos = Position()) : pos(pos) {}
virtual void enter(Field) {}
virtual void move(Movement) {}
protected:
Position pos;
};
class Car : public MovingObject {
public:
Car(Position pos = Position()) : MovingObject(pos), action(0) {}
void enter(Field f) {
switch (f) {
case water:
action = &Car::sink;
break;
case ground:
action = &Car::run;
break;
case air:
action = &Car::fall;
break;
}
}
void move(Movement m) {
if (action) {
(this->*action)(m);
}
}
private:
void run(Movement m) {
pos.x += m.x;
pos.z += m.z;
}
void sink(Movement) {
pos.x -= 1;
}
void fall(Movement) {
pos.x -= 10;
}
void (Car::*action)(Movement);
};
class Airplane : public MovingObject {
public:
Airplane(Position pos = Position())
: MovingObject(pos), action(0) {}
void enter(Field f) {
switch (f) {
case water:
throw Crash("This airplane cannot fly into water");
case ground:
throw Crash("This airplane cannot fly into ground");
case air:
action = &Airplane::fly;
}
}
void move(Movement m) {
if (action) {
(this->*action)(m);
}
}
private:
void fly(Movement m) {
pos.x += m.x;
pos.y += m.y;
pos.z += m.z;
}
void (Airplane::*action)(Movement);
};
class Boat : public MovingObject {
public:
Boat(Position pos = Position()) : MovingObject(pos), action(0) {}
void enter(Field f) {
switch (f) {
case water:
action = &Boat::navigate;
break;
case ground:
throw Crash("This boat cannot navigate onto the ground");
break;
case air:
action = &Boat::fall;
break;
}
}
void move(Movement m) {
if (action) {
(this->*action)(m);
}
}
private:
void navigate(Movement m) {
pos.x += m.x;
pos.z += m.z;
}
void fall(Movement) {
pos.x -= 10;
}
void (Boat::*action)(Movement);
};
class Submarine : public MovingObject {
public:
Submarine(Position pos = Position())
: MovingObject(pos), action(0) {}
void enter(Field f) {
switch (f) {
case water:
action = &Submarine::navigate;
break;
case ground:
throw Crash("Submarines cannot navigate into the ground");
break;
case air:
action = &Submarine::fall;
break;
}
}
void move(Movement m) {
if (action) {
(this->*action)(m);
}
}
private:
void navigate(Movement m) {
pos.x += m.x;
pos.y += m.y;
pos.z += m.z;
}
void fall(Movement) {
pos.x -= 10;
}
void (Submarine::*action)(Movement);
};
int main() {
MovingObject* car = new Car;
car->enter(ground);
car->move(Movement(5));
MovingObject* airplane = new Airplane;
airplane->enter(air);
airplane->move(Movement(10,0,3));
MovingObject* boat = new Boat;
boat->enter(water);
boat->move(Movement(2,2));
MovingObject* submarine = new Submarine;
submarine->enter(water);
submarine->move(Movement(0,0,-3));
try {
submarine->enter(ground);
} catch (Crash c) {
std::cout << c.what() << std::endl;
}
return 0;
}
//-------