class Car {
class BaseState {
explicit BaseState(Car* vehicle) : mVehicle(vehicle) {}
virtual void run() = 0;
Car* mVehicle;
}
class State1 : public BaseState {
explicit State1(Car* vehicle) : BaseState(vehicle) {}
virtual void run() {
// use data of Car
...
doSomething();
}
virtual void doSomething() {
}
}
class State2 : public BaseState {
}
...
}
class Convertible: public Car {
class State1 : public Car::State1 {
explicit State1(Convertible* vehicle) : Car::State1(vehicle) {}
virtual void doSomething() {
static_cast<Convertible*>(mVehicle)->foldTop();
}
}
class State2 : public Car::State2 {
}
...
void foldTop() {}
}
All States are derived from the BaseState so that they have the member variable mVehicle to access outer class variables. However, in each derived class, in all functions of each State, static_cast is needed to access derived class member variables and functions.
Any better solution?
=======================================================================
Yes. I tried template as below, but it cannot be compiled because errors like
"car.h: In member function ‘virtual void Car::State1::run()’: car.h:18:12: error: ‘mVehicle’ was not declared in this scope ".
// car.h
#include <iostream>
template <class T>
class Car {
public:
class BaseState {
public:
explicit BaseState(T* vehicle) : mVehicle(vehicle) {}
protected:
T* mVehicle;
};
class State1 : public BaseState {
public:
explicit State1(T* vehicle) : BaseState(vehicle) {}
virtual void run() {
mVehicle->x = 1;
mVehicle->y = 2;
mVehicle->doSomething1();
mVehicle->doSomething2();
processEvent();
}
virtual void processEvent() {
if (mVehicle->val > 2) {
std::cout << "too large" << std::endl;
}
}
};
class State2 : public BaseState {
public:
explicit State2(T* vehicle) : BaseState(vehicle) {}
virtual void run() {
mVehicle->x = 10;
mVehicle->y = 20;
processEvent();
}
virtual void processEvent() {
if (mVehicle->val > 20) {
std::cout << "too large" << std::endl;
}
}
};
virtual void doSomething1() {
val += x * y;
}
virtual void doSomething2() {
val += x + y;
}
protected:
int x;
int y;
int val;
};
// convertible.h
#include "car.h"
#include <iostream>
class Convertible : public Car<Convertible> {
protected:
class State1 : public Car<Convertible>::State1 {
explicit State1(Convertible* vehicle) : Car<Convertible>::State1(vehicle) {}
// want to override functions in base class states
virtual void processEvent() {
if (mVehicle->val > 10) {
std::cout << "too large" << std::endl;
mVehicle->val = 10;
}
}
};
// want to override some base class functions
// and access some special variables
// want to inherit other functions
virtual void doSomething2() {
z = 10;
val += x + y + z;
}
protected:
int z;
};
If I use State1(Car* vehicle)
instead of State1(T* vehicle)
, there is additional conversion error. What am I doing wrong?
If the program can figure out that Convertible::State1::processEvent()
should be executed, why cannot it automatically cast mVehicle
from Car*
to Convertible*
? Apparently mVehicle
points to a Convertible
object when Convertible::State1::processEvent()
is deduced. We do not need template if there is automatic cast.
Use templates.
Remove pointer from Car
inner classes (made them abstract classes full of pure virtuals).
Add new template class CarT
(or think about better name)
template <typename T>
class CarT {
class CarHolder {
explicit CarHolder(T* car) : car(car) {}
T* car;
};
class State1 : public Car::State1, protected CarHolder {
explicit State1(Car* vehicle) : CarHolder(vehicle) {}
virtual void run() {
// use data of Car
...
doSomething();
}
virtual void doSomething() {
}
};
class State2 : public Car::State2 {
};
...
};
This way you will have runtime polymorphism of Car
and it's State
's and good compile time polymorphism of derived classes (which in turn will remove need for ugly static_cast
)
class Convertible: public CarT<Convertible> {
typename CarT<Convertible> Base;
class State1 : public Base::State1 {
explicit State1(Convertible* vehicle) : Car::State1(vehicle) {}
virtual void doSomething() {
car->foldTop();
}
}
class State2 : public Base::State2 {
}
...
void foldTop() {}
}
class Convertible : public CarT<Convertible>
might look strange, but it will work (CarT
uses it template argument only as pointer, if it was using it as value member there might be some problems)
This implementation uses no casts, duplicate pointers, virtual getters, or CRTP. It has three parallel hierarchies:
So we have e.g.
Car Car::AbstractState Car::State<C>
| | |
+--- Convertible +--- Convertible::AbstractState +--- Convertible::State<C>
| | | | | |
| +--- Racer | +--- Racer::AbstractState | +--- Racer::State<C>
+--- Hybrid +--- Hybrid::AbstractState +--- Hybrid::State<C>
Each concrete state derives from and implements the corresponding abstract state. If we have a Car*
that points to a Convertible
, and we query its state, we get a Car::AbstractState*
which points to a concrete state object with the ultimate type of Convertible::State<Convertible>
. The user of the car hierarchy, however, doesn't know and doesn't care about the template machinery.
The code:
#include <iostream>
using namespace std;
struct Trace
{
Trace(const char* s) : s (s)
{
cout << s << " start\n";
}
~Trace()
{
cout << s << " end\n";
}
const char* s;
};
struct Car {
struct AbstractState
{
virtual void run() = 0;
};
template <typename C>
struct State : virtual AbstractState
{
explicit State(C* vehicle) : mVehicle(vehicle) {}
virtual void run()
{
Trace("Car::State::run");
doSomething();
};
virtual void doSomething()
{
Trace("Car::State::doSomething");
}
C* mVehicle;
};
virtual AbstractState* getState() { return new State<Car>(this); }
};
struct Convertible : Car {
struct AbstractState : virtual Car::AbstractState
{
virtual void runBetter() = 0;
};
template <typename C>
struct State : Car::State<C>, virtual AbstractState
{
using Car::State<C>::mVehicle;
explicit State(C* vehicle) : Car::State<C>(vehicle) {}
void doSomething()
{
Trace("Convertible::State::doSomething");
Car::State<C>::doSomething();
mVehicle->foldTop();
}
void runBetter()
{
Trace("Convertible::State::runBetter");
run();
doSomethingElse();
};
virtual void doSomethingElse()
{
Trace("Convertible::State::doSomethingElse");
}
};
void foldTop()
{
Trace("Convertible::foldTop");
}
Convertible::AbstractState* getState() { return new State<Convertible>(this); }
};
int main ()
{
Car car;
Convertible convertible;
Car& car2(convertible);
cout << "runing car\n";
Car::AbstractState* carstate = car.getState();
carstate->run();
cout << "runing convertible\n";
Convertible::AbstractState* convertiblestate = convertible.getState();
convertiblestate->run();
cout << "runing car2\n";
Car::AbstractState* carstate2 = car2.getState();
carstate2->run();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With