Finding a max for an enum - a template problem

K

Kevin

I'm creating a template to support state machines. In doing so, I
need to pass an enumeration for the number of transitions and a non
type parameter for the range of the enum (to allow me to allocate an
array of pointers big enough to hold one entry for each transition).
My template declaration looks roughly like this:

template <class TRANSITION, int NUMTRANSITIONS>
class StateMachine {
***SNIP***
void RegisterTransition(TRANSITION t, StateMachine *pState);
StateMachine *nextState[NUMTRANSITIONS];
***SNIP***
};


I invoke the template like so:

// Define an enumerated type for transitions
enum ToggleSwitch_t { ON, OFF, TOGGLE };
#define MAX_TRANSITION 3

// Set up a pointer to current state
StateMachine<ToggleSwitch_t, MAX_TRANSITION> *pCurrState;

// Create states
StateMachine<ToggleSwitch_t, MAX_TRANSITION>LightOn("LIGHT ON");
StateMachine<ToggleSwitch_t, MAX_TRANSITION> LightOff("LIGHT OFF");

// Set up transitions
LightOn.RegisterTransition(ON, &LightOn);
LightOn.RegisterTransition(OFF, &LightOff);
LightOn.RegisterTransition(TOGGLE, &LightOff);

LightOff.RegisterTransition(ON, &LightOn);
LightOff.RegisterTransition(OFF, &LightOff);
LightOff.RegisterTransition(TOGGLE, &LightOn);

It works fine as is, but I want to add a static array that will allow
me to index into states as an enum type. I could set up something
like

template <class TRANSITION, int NUMTRANSITIONS, class STATE, int
NUMSTATES>

but this is going to get very hairy very quickly. I would prefer if I
could somehow take the enum that's passed in and simply derive its
maximum from inside the template itself. That would give me something
easier to read like

template <class TRANSITION, class STATE>

I can't find any way to determine the max value of an enum. Most
previous posts are of the form "You can't do that - please try
workaround XYZ..." If I must, I could try using #define macros, but
that just seems to hide the mechanics rather than simplifying them,
which is a sure recipe for confusion for maintenance programming.

Does anyone have any suggestions?

Thanks,

Kevin
 
V

Victor Bazarov

Kevin said:
[..] I would prefer if I
could somehow take the enum that's passed in and simply derive its
maximum from inside the template itself.

There is no way.
[..]
I can't find any way to determine the max value of an enum.

Because there is none.
Most
previous posts are of the form "You can't do that - please try
workaround XYZ..." If I must, I could try using #define macros, but
that just seems to hide the mechanics rather than simplifying them,
which is a sure recipe for confusion for maintenance programming.

Does anyone have any suggestions?

You can't do that - please try workaround: each "enum" should not
really be an enum, but an array of, say, unsigned, wrapped in a class
or a struct (to make it a different type). The array of values will
have its size, from which you can determine when you hit the end of
the array, thus going through all values.

Enums are ancient and not very suitable for state machine modelling.

V
 
L

liverspot

You can do something like

enum ToggleSwitch_t { ON, OFF, TOGGLE, LAST };

and then take the value of LAST. You would need LAST to be the last
element in every enum you want to use.
 
K

Kevin

You can't do that - please try workaround: each "enum" should not
really be an enum, but an array of, say, unsigned, wrapped in a class
or a struct (to make it a different type). The array of values will
have its size, from which you can determine when you hit the end of
the array, thus going through all values.

So, something like:
template <int TRANSITIONS[], int STATES[]>
class StateMachine {
***SNIP***
StateMachine *nextState[sizeof(TRANSITIONS)];
***SNIP***
};

And

#define ON 1
#define OFF 2
#define TOGGLE 3
int MyTrans[] = {ON, OFF, TOGGLE};

#define LIGHT_ON 1
#define LIGHT_OFF 2
int MyStates[] = {LIGHT_ON, LIGHT_OFF, TOGGLE};

StateMachine<MyTrans, MyStates>LightOn("LIGHT IS ON");
StateMachine<MyTrans, MyStates> LightOff("LIGHT IS OFF");

Does that seem right?

- Kevin
 
V

Victor Bazarov

Kevin said:
You can't do that - please try workaround: each "enum" should not
really be an enum, but an array of, say, unsigned, wrapped in a class
or a struct (to make it a different type). The array of values will
have its size, from which you can determine when you hit the end of
the array, thus going through all values.

So, something like:
template <int TRANSITIONS[], int STATES[]>
class StateMachine {
***SNIP***
StateMachine *nextState[sizeof(TRANSITIONS)];

Probably

StateMachine *nextState[sizeof(TRANSITIONS) / sizeof(int)];

(sizeof does not report the *number* of elements, but their combined
size in bytes).
***SNIP***
};

And

#define ON 1
#define OFF 2
#define TOGGLE 3
int MyTrans[] = {ON, OFF, TOGGLE};

#define LIGHT_ON 1
#define LIGHT_OFF 2
int MyStates[] = {LIGHT_ON, LIGHT_OFF, TOGGLE};

StateMachine<MyTrans, MyStates>LightOn("LIGHT IS ON");
StateMachine<MyTrans, MyStates> LightOff("LIGHT IS OFF");

Does that seem right?

Since you're asking in response to my reply, I feel I need to give you
an answer, but you're not going to like it, I'm afraid... Here it is:
I have no idea. If it solves the problem you had, then use it. If it
does not, let's talk more, perhaps you'll get me to understand what it
is you hope to accomplish.

I know that enums are not well suited for your task, they are very low
level and very limited functionality types. That's why I suggested
dumping them altogether. You are better off creating a separate type
for every state set/sequence you need to identify. Each state then
could be expressed/encoded by a value.

V
 
J

James Kanze

Kevin said:
On Jun 21, 10:06 am, "Victor Bazarov" <[email protected]> wrote:
[...]
Since you're asking in response to my reply, I feel I need to give you
an answer, but you're not going to like it, I'm afraid... Here it is:
I have no idea.

It's interesting to note that most of the experts, when
designing state machines for C++, have written code generators
to do it. Of course, most of this goes back to before the days
of template meta-programming, but as far as I know, no one has
come up with a better solution. You define a higher level
language in which you define the transitions, and a "compiler"
for that language which generates the C++.

In general, I'd say that this is to be prefered over template
meta-programming any time the "program" itself didn't need to
exploit information internal to C++ (like types, etc.). The
results are almost always more readable.
If it solves the problem you had, then use it. If it
does not, let's talk more, perhaps you'll get me to understand what it
is you hope to accomplish.
I know that enums are not well suited for your task, they are very low
level and very limited functionality types. That's why I suggested
dumping them altogether. You are better off creating a separate type
for every state set/sequence you need to identify. Each state then
could be expressed/encoded by a value.

Enums can have a role to play in a state machine, but they
certainly aren't a state machine. The "classical" solution, I
think, is along the lines you suggest: a base class State, and
each individual state a distinct class deriving from this base,
with transitions, actions, etc. being handled by virtual
functions in State.

You might want to check out http://smc.sourceforge.net/. This
seems to be derived from Robert Martin's work in the 1990's,
which is, as far as I know, still the reference. (Note: I've
not used this tool, and only stumbled upon it looking for
information about Robert Martin's state machine compiler.)
 
M

Michael DOUBEZ

Kevin a écrit :
I'm creating a template to support state machines. In doing so, I
need to pass an enumeration for the number of transitions and a non
type parameter for the range of the enum (to allow me to allocate an
array of pointers big enough to hold one entry for each transition).
[snip]

I can't find any way to determine the max value of an enum. Most
previous posts are of the form "You can't do that - please try
workaround XYZ..." If I must, I could try using #define macros, but
that just seems to hide the mechanics rather than simplifying them,
which is a sure recipe for confusion for maintenance programming.

Did you have a look at the "Variadic Integral Constant" MPL concept in
boost:
http://www.boost.org/libs/mpl/doc/refmanual/integral-sequence-wrapper.html

Instead of passing all enum, it could make sense to allow the
specification of the enums to use for the states.

typedef vector_c<states,ON,OFF,FAIL> base_protocol;
typedef typedef push_back<base_protocol,
integral_c<states,TRANSMIT> >::type
halfduplex_transmit;
typedef typedef push_back<base_protocol,
integral_c<states,RECEIVE> >::type halfduplex_receive;
typedef vector_c<states,ON,OFF,FAIL,
RECEIVE,TRANSMIT,COLLISION> full_duplex;
//...

Michael
 
A

Andreas Huber

James said:
It's interesting to note that most of the experts, when
designing state machines for C++, have written code generators
to do it. Of course, most of this goes back to before the days
of template meta-programming, but as far as I know, no one has
come up with a better solution.

I guess it depends on what you mean with "better" here. There are TMP
FSM solutions:
- Aleksey Gurtovoy's MPL example
http://www.mywikinet.com/mpl/fsm_example_25_jul_02.zip shows how a
simple but fast STT-based FSM can be implemented
You define a higher level
language in which you define the transitions, and a "compiler"
for that language which generates the C++.

I believe for quite a few problems there's no longer any need for such
external (front-end) compilers. C++ itself offers all the necessary
machinery to do the generation in the language itself, as libraries like
Boost.Spirit very nicely demostrate.
In general, I'd say that this is to be prefered over template
meta-programming any time the "program" itself didn't need to
exploit information internal to C++ (like types, etc.)

Which is the case for state machines, isn't it? You usually want to
write entry-, exit- and transition-actions in C++.

Regards,
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top