Sign in to follow this  
d000hg

Deriving from a class used for state-tracking?

Recommended Posts

Now it's common that in a class you may have an enumeration of possible states an object of that class can be in. But what about when you derive from this class, and a child needs an extra state? OO design says the parent shouldn't know about its children so putting every possible state in the base class is bad - but how can we achieve our aim in a 'good' way?

Share this post


Link to post
Share on other sites
hi,

I don't quite understand your problem :/


class CParent
{
public:
CParent();
~CParent();

virtual void SetStates(void);

protected:
int state1;
float state2;
};

class CChild : pulic CParent
{
public:
CChild();
~CChild();

virtual void SetStates(void); // set state3 and call the parent SetState method to set state1 and state2

protected:
char *state3;
};



Here, the CChild class will have visibility on state1 and state2, and add a third state. If you have a virtual class to set those states, you can store a list of CParent * and you don't have to care of visibility. The program will call the CChild::SetState if it needs.

Well, if this is not what you asked for, then sorry, I didn't understand then ^^

Share this post


Link to post
Share on other sites
Quote:
Original post by paic
I don't quite understand your problem :/


From what i understood he means something like:


struct foo {
enum bar {
CONSTANT1,
CONSTANT2,
CONSTANT3
};
// .....
};

struct foo2 : foo {
// now he wants to add a new member to the set of bar
// ....
};

Share this post


Link to post
Share on other sites
class CBase
{
public:
enum STATE {NONE,MOVING,WAITING,STOP};
virtual void ActOnState();
private:
STATE m_State;
};


Now let's say a child class wants a state called RUNNING, or TURNING, or SHOOT.
That's the problem. It's these enumerations which don't really work with derivation which are the problem.

EDIT: snk_kid beat me to the explanation of my own problem [smile]

Share this post


Link to post
Share on other sites
Quote:
Original post by d000hg
class CBase
{
public:
enum STATE {NONE,MOVING,WAITING,STOP};
virtual void ActOnState();
private:
STATE m_State;
};


Now let's say a child class wants a state called RUNNING, or TURNING, or SHOOT.
That's the problem. It's these enumerations which don't really work with derivation which are the problem.

EDIT: snk_kid beat me to the explanation of my own problem [smile]


This is a language limitation of C++, so you don't have a choice. You'll need to use an alternative, like the State Design Pattern. If this is merely an academic exercise for your own edification, you could try using Haskell, which does allow you to extend enumerations,

Share this post


Link to post
Share on other sites
It's a real-world problem. The state pattern seems a very complex way to fix what seems a simple problem, but I guess that's just the way it is. For applications where the original enumeration is not going to expand often or very much, presumably it's easier 9 times out of ten to just stick all the enumeration values needed by child classes into the base and live with the OO 'hack'?

Share this post


Link to post
Share on other sites
Why don't you use contants? After all, enumerations only define constants (and suffer the same problem than integer constants).

struct foo
{
static const int value1 = 0;
static const int value2 = 1;
static const int value3 = 2;
...
};
strut foo2 : public foo
{
static const int value4 = 3;
...
};

Of course, you have to give the value for every constant you create - it is not as simple as enumeration values. I see at least one "kind of" hack that will allow you to use a value without explicitely defining it. Something that look like this:

class UniqueValue
{
public:
operator int() { return (int)this; }
bool operator==(const UniqueValue& value) { return this == &value; }
};

// -- foo
#include "UniqueValue.h"
struct foo
{
static const UniqueValue enum_value_1;
static const UniqueValue enum_value_2;
static const UniqueValue enum_value_3;
};

// of course, in a cpp file:
const UniqueValue foo::enum_value_1;
const UniqueValue foo::enum_value_2;
const UniqueValue foo::enum_value_3;

// -- foo2
#include "foo.h"
struct foo2 : public foo
{
static const UniqueValue enum_value_4;
};

// and foo.cpp
const UniqueValue foo::enum_value_4;

This is of course far from complete, and I'm not even sure that it will compile. It works because each defined UniqueValue is static, and the address of 2 static objects can't be the same.
I guess there is a better way to do this (this way has some heavy problems) but I believe it should work.

Regards,

Share this post


Link to post
Share on other sites
Some interesting ideas there. Not sure if I can convince our lead programmer of the merits but it definitely looks like something I'll investigate for my own code, when I want states but without a whole FSM approach - for that I probably would follow the state pattern.

Share this post


Link to post
Share on other sites
Quote:


class UniqueValue
{
public:
operator int() { return (int)this; }
bool operator==(const UniqueValue& value) { return this == &value }
};




I think you forgot a *
operator int() { return (int)*this; }

Share this post


Link to post
Share on other sites
Quote:
Original post by Genjix
I think you forgot a *
operator int() { return (int)*this; }


No :)

I don't know what is stored at the *this address, and maybe this content is not unque among all the defined UniqueValue, but what I know is that the address of each UniqueValue is unique - thus, the value associated with the object is its address, not its content.

Regards,

Share this post


Link to post
Share on other sites
As a matter of personal taste I'd argue that using the State Design Pattern would be worth the hassle - code that is a mess of if(this_state) etc. gets really messy over years of programming.

I came across one embedded program that was originally C, therefore didn't have access to the State pattern, and so used the enumeration concept that you're proposing. Adding a new state ten years after it was originally written was a true maintenance nightmare. I had to trawl through over 100000 lines of code to find every place that checked the state and acted on it, then debug the hell out of it when I found new places or code that sneakily acted on it etc.

I'd say the concept of having a state interface with each state nicely and cleanly defining its own personal behaviour is an awful lot easier to work with. Since you're defining an inheritance hierarchy anyway, you won't lose anything by utilising that virtual behaviour to its fullest (in embedded some have complained about runtime overhead of virtual functions, sigh).

Quote:
Original post by d000hg
The state pattern seems a very complex way to fix what seems a simple problem, but I guess that's just the way it is. For applications where the original enumeration is not going to expand often or very much, presumably it's easier 9 times out of ten to just stick all the enumeration values needed by child classes into the base and live with the OO 'hack'?


What may seem small and simple code right now can balloon as people add new requirements or features. Bear in mind that 'just one more enumeration' may seem like simple - but what is it making simple? The hard part is going to be writing what happens in each state, not how you define each state. Therein lies the advantage of the State Design Pattern. A little bit of extra work now gives you a much easier time when actually adding new states.

An additional point that might cause concern: If this code is going to be somewhere low-level that a lot of other code depends on, you really will kick yourself every time you have to add a new enumeration and watch as your entire code-base recompiles. Break the compile-time dependencies now whilst you have the chance.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this