Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

State Machine/Class Granularity

3 views
Skip to first unread message

Wavemaker

unread,
Jul 7, 2005, 12:06:11 PM7/7/05
to
Say I have a state machine with two superstates. I'll call them S1 and
S2. Both superstates have two substates. I'll call them S11, S12 (for
the S1 superstate), S21, and S22 (for the S2 superstate).

If I were to put this state machine into a class using a switch
statement, it might look like this:

switch(superstate)
{
case S1:
switch(substate)
{
case S11:
// ...
break;

case S12:
// ...
break;
}
break;

case S2:
switch(substate)
{
case S21:
// ...
break;

case S22:
// ...
break;
}
break;
}

Putting aside the various approaches for implementing state machines
(I'm only using a switch statement here because it makes my question
easier to describe), I'm wondering if this is the right granularity for
a class/state machine. Instead of the above, I could divide the class in
two, putting one superstate in one class and the other superstate in
another class. Each class would have an initial state in which it would
ignore all events except the event(s) that causes the superstate it
represents to become active.

In other words, the class representing S1 would have an initial state in
which all it would do is wait for the right event. We could call this
state Waiting or Disabled. Once having received this event, it
transitions to an active state (formerly one of the substates described
above). When this happens, the other class receiving the same event goes
back into a waiting state. Only one of the classes is active at any
time.

I've been using this approach over the past few days, and it seems to
work well. Instead of one large class with two modes, for lack of a
better term, I have two small classes with a well defined, single
responsibility. The cohesiveness seems better. At any rate, I guess my
more general question is that if you are taking an approach of equating
classes with state machines, what is the right granularity? Say you have
a large state machine, how do you divide it up into classes?

Andreas Huber

unread,
Jul 7, 2005, 4:37:03 PM7/7/05
to
Wavemaker wrote:
[snip]

> At any rate, I guess my
> more general question is that if you are taking an approach of
> equating classes with state machines, what is the right granularity?
> Say you have a large state machine, how do you divide it up into
> classes?

That largely depends on what exactly you want to implement with your
FSM. Some applications work just fine with flat FSMs while others hugely
benefit from a hierarchical state model and other advanced FSM features.
For the former a OO state model is usually a waste (a simple function
pointer is enough to store the current state). For the latter it usually
makes sense to have one class per state, using derivation to implement
substates. To implement medium to high complexity FSMs in C++ you might
want to consider using the Boost.Statechart library, which can be found
here:

http://boost-sandbox.sf.net/libs/statechart

Regards,

--
Andreas Huber

When replying by private email, please remove the words spam and trap
from the address shown in the header.

H. S. Lahman

unread,
Jul 8, 2005, 4:33:52 PM7/8/05
to
Responding to Wavemaker...

> Say I have a state machine with two superstates. I'll call them S1 and
> S2. Both superstates have two substates. I'll call them S11, S12 (for
> the S1 superstate), S21, and S22 (for the S2 superstate).
>
> If I were to put this state machine into a class using a switch
> statement, it might look like this:
>
> switch(superstate)
> {
> case S1:
> switch(substate)
> {
> case S11:
> // ...
> break;
>
> case S12:
> // ...
> break;
> }
> break;
>
> case S2:
> switch(substate)
> {
> case S21:
> // ...
> break;
>
> case S22:
> // ...
> break;
> }
> break;
> }

My gut reaction is: DON'T DO THAT! B-)

I am not a fan of Harel state machines because they almost always
indicate poor abstraction of object responsibilities (i.e., object
cohesion is trashed because the object has too many disparate
responsibilities). So whenever one is tempted to use Harel, one should
look for a delegation solution so that two objects with different,
independent, simpler Moore/Mealy state machines can interact.

Beyond that, unless you are writing a code generator, the switch
structure is a very bad idea. Regardless of whether you actually use a
switch statement, you are making flow of control decisions in your
business logic based on what the current state is. That is a no-no in
FSAs because a state machine state should not depend in any way on
knowing what the previous state was of the next state will be. That is,
the transition is determined strictly by the event identity and the
current state of the state machine via an STT. (Not to mention that the
current state should always be a private attribute that no other object
should have access to.)

<aside>
If you are developing a code generator, then the logic is simply a
generic implementation of Harel structure. That is, the code is just
for initializing the STTs, which is fine.

Your code suggests to me that you are trying to implement the event
processing infrastructure associated with dispatching/consuming events.
Ideally that infrastructure should be fully reusable so you write it
only once and it Just Works for any state machine in any application.
That gets to be a real mess with Harel because of the potential
complexities. [I am not sure even Harel could implement such factory
infrastructure from the ambiguous UML semantic definitions. Translation
tool vendors have to make specific assumptions about sequencing when
histories and whatnot are involved.] Having to do that is another reason
for avoiding Harel.

It is a whole lot easier to deal with just Moore/Mealy state machines.
Then all one needs to do is initialize a static STT with method
addresses for each class and let the event queue manager do a table
lookup to dispatch the event data packet to the right action. With
Harel one ends up with multiple STTs that are a pain to initialize
properly in the general Harel case unless one is a compiler.
</aside>

>
> Putting aside the various approaches for implementing state machines
> (I'm only using a switch statement here because it makes my question
> easier to describe), I'm wondering if this is the right granularity for
> a class/state machine. Instead of the above, I could divide the class in
> two, putting one superstate in one class and the other superstate in
> another class. Each class would have an initial state in which it would
> ignore all events except the event(s) that causes the superstate it
> represents to become active.

This is close. Delegate a subset of cohesive object responsibilities to
another object. Then design state machines for each object based on the
more limited subset of responsibilities for each one. Typically that
will completely eliminate the need for superstates.

However, it would be easier to make the point with a specific example
from your situation rather than generic s1, ..., s22.

<aside>
Having said all this, there are situations where superstates are useful
as a notational artifact for reducing clutter in an otherwise flat state
machine. For example, one might have a state to process errors where
there would be a transition from every other state to it but only one
way to get out after a reset. In that case one can reduce the
transition clutter by making the error state a superstate with a single
floating transition to it (conventionally from inside it). But this
notion of "superstate" is much simpler than the Harel view. (For
example, with Harel there could be code in the outer switch's cases that
is executed before and/or after the code in the inner switch's cases.)
</aside>


*************
There is nothing wrong with me that could
not be cured by a capful of Drano.

H. S. Lahman
h...@pathfindermda.com
Pathfinder Solutions -- Put MDA to Work
http://www.pathfindermda.com
blog: http://pathfinderpeople.blogs.com/hslahman
(888)OOA-PATH

Andreas Huber

unread,
Jul 8, 2005, 8:14:41 PM7/8/05
to
H. S. Lahman wrote:
> [I am not sure even Harel could implement
> such factory infrastructure from the ambiguous UML semantic
> definitions. Translation tool vendors have to make specific
> assumptions about sequencing when histories and whatnot are
> involved.] Having to do that is another reason for avoiding Harel.

You've have made this ambiguity claim before. It would be interesting to
hear what *exactly* is so ambiguous about the Harel/UML semantics,
especially in conjunction with history. For sure, the UML standard does
contain some more or less obvious contradictions. However, all of them
can be resolved pretty easily.

BTW, it has become pretty clear in our last discussion that we disagree
on whether Harel is a good idea or not and I'm confident that we will
until the end of time, so I'd rather leave that alone. I'm only
interested in ambiguities in the Harel/UML semantics.

Wavemaker

unread,
Jul 9, 2005, 2:42:35 AM7/9/05
to

<snip>

You're right, I haven't used an STT explicitly yet. Each state handler
function consumes the event and makes the state transition itself.

I have to tell you, I've been a student of your posts for awhile now,
especially in regards to state machines. I recently adopted the approach
of using an event queue in conjunction with state machines, which you've
described on several occasions. A lot of the problems and challenges I
had faced up until that point simply went away with this new (for me)
approach. But I haven't gone all the way yet by using static STT.

I'm using C#, and unfortunately there are implementation challenges
involved in taking the static STT approach. When you initialize the STT,
you can only initialize it with delegates (akin to function pointers in
C++) representing static methods. An instance is needed to create a
delegate representing a non-static method. So you wind up with all of
the action methods being static with an extra parameter representing the
current instance. This is certainly doable, but I don't like it. An
alternative would be to make the STT non-static, but then you have the
extra memory cost of each instance of a state machine having its own
copy of the STT. At any rate, this is a low-level problem specific to C#
that I'm grappling with. The extra cost in memory may be worth the
clarity at the conceptual level of using an STT.

>> Putting aside the various approaches for implementing state machines
>> (I'm only using a switch statement here because it makes my question
>> easier to describe), I'm wondering if this is the right granularity
>> for a class/state machine. Instead of the above, I could divide the
>> class in two, putting one superstate in one class and the other
>> superstate in another class. Each class would have an initial state
>> in which it would ignore all events except the event(s) that causes
>> the superstate it represents to become active.
>
> This is close. Delegate a subset of cohesive object responsibilities
> to another object. Then design state machines for each object based
> on the more limited subset of responsibilities for each one.
> Typically that will completely eliminate the need for superstates.
>
> However, it would be easier to make the point with a specific example
> from your situation rather than generic s1, ..., s22.

Well, this has come up in several situations while working on my MIDI
toolkit in C#, which I've mentioned before. To give a very simple
example, the music from MIDI files is driven by clock ticks. So I've
created a clock class to generate tick events. The clock is either
running or it isn't, so there's really only two states here: running and
not running.

However, a clock can be in a master mode, meaning that it generates more
than just tick messages; it also generates tick messages that are
transmitted to external source(s). In master mode, it's driving the tick
generation not only internally for the application but also for any
device that is connected to the computer via MIDI and is set to receive
those messages. This allows MIDI music played on one device to be
syncrhonized with MIDI music played on another device. The master mode
can either be enabled or disabled. Master mode could be considered a
superstate of the running state. So we could have something like this:

Master Mode Enabled
Running
Not running
Master Mode Disabled
Running
Not running

This seems so simple that it appears that it could be easily coded into
a single class. But I've done this and not liked the results. The class
winds up too complicated. So what I've done is to create a simple clock
class for generating ticks that has no idea about the master mode. Then
I have a master mode class whose only concern is responding to ticks
created by the clock class and transmitting messages to external
devices. (BTW, "transmitting messages" is a little harder than it
sounds. There are additional messages that have to be sent besides the
"tick" message plus there is usually a timing conversion that must take
place as well.)

This has made me think about superstate/substates, hence my original
post. If we have a state machine representing a subsystem, how should we
partition the state machine into various classes? Would factoring out
substates as I did above be a good place to start? Or do I have it
backwards. Should the classes be decided upon first and then design each
one as a state machine?


H. S. Lahman

unread,
Jul 9, 2005, 10:50:00 AM7/9/05
to
Responding to Huber...

>> [I am not sure even Harel could implement
>> such factory infrastructure from the ambiguous UML semantic
>> definitions. Translation tool vendors have to make specific
>> assumptions about sequencing when histories and whatnot are
>> involved.] Having to do that is another reason for avoiding Harel.
>
>
> You've have made this ambiguity claim before. It would be interesting to
> hear what *exactly* is so ambiguous about the Harel/UML semantics,
> especially in conjunction with history. For sure, the UML standard does
> contain some more or less obvious contradictions. However, all of them
> can be resolved pretty easily.

Alas, I can't recall the exact details. PathMATE supports Harel because
it is in UML (Pathfinder just advises clients not to use it in
training). I wasn't directly involved in that implementation but I did
attend a meeting where the problem was discussed a couple of years ago.
I am pretty sure the situation involved multiple containing
superstates, all the states had histories, and the superstates had their
own actions.

I *think* the ambiguity had something to do with whether the last event
was addressed directly to the substate. For example, if the current
event is to the superstate but the last event was to the substate, which
execution context does one use for the substate action to be consistent
with the superstate context? OTOH, it may have been about an execution
model where self-directed events are given priority over external events
(e.g., does a substate event to another contained substate affect the
history context of the superstate causing the first action to execute?).

The bottom line was that Pathfinder had to define its own rules for
resolving the action sequence/context.

Andreas Huber

unread,
Jul 9, 2005, 11:31:00 AM7/9/05
to
H. S. Lahman wrote:
> Responding to Huber...
>
>>> [I am not sure even Harel could implement
>>> such factory infrastructure from the ambiguous UML semantic
>>> definitions. Translation tool vendors have to make specific
>>> assumptions about sequencing when histories and whatnot are
>>> involved.] Having to do that is another reason for avoiding Harel.
>>
>>
>> You've have made this ambiguity claim before. It would be
>> interesting to hear what *exactly* is so ambiguous about the
>> Harel/UML semantics, especially in conjunction with history. For
>> sure, the UML standard does contain some more or less obvious
>> contradictions. However, all of them can be resolved pretty easily.
>
> Alas, I can't recall the exact details.

Too bad.

> PathMATE supports Harel
> because it is in UML (Pathfinder just advises clients not to use it in
> training). I wasn't directly involved in that implementation but I
> did attend a meeting where the problem was discussed a couple of
> years ago. I am pretty sure the situation involved multiple
> containing superstates, all the states had histories, and the
> superstates had their own actions.
>
> I *think* the ambiguity had something to do with whether the last
> event was addressed directly to the substate. For example, if the
> current event is to the superstate but the last event was to the
> substate, which execution context does one use for the substate
> action to be consistent with the superstate context? OTOH, it may
> have been about an execution model where self-directed events are
> given priority over external events (e.g., does a substate event to
> another contained substate affect the history context of the
> superstate causing the first action to execute?).

That's too vague for me to see any ambiguities.

> The bottom line was that Pathfinder had to define its own rules for
> resolving the action sequence/context.

Pretty much every standard in computer science leaves such leeway,
that's called unspecified behavior. The more interesting question is
whether a user employing two different implementations could run into a
problem because the behavior is unspecified. For example, when exiting a
state configuration with orthogonal regions, the UML standard does not
specify in what sequence the regions are exited. In practice this is not
a problem because:

a) The user knows (or should know) that the sequence is unspecified
b) It's pretty hard to think of an example where it would make sense to
make the exit action of a state in one region dependent on the execution
of an exit action of a state in another region.

H. S. Lahman

unread,
Jul 9, 2005, 12:16:22 PM7/9/05
to
Responding to Wavemaker...

> You're right, I haven't used an STT explicitly yet. Each state handler
> function consumes the event and makes the state transition itself.
>
> I have to tell you, I've been a student of your posts for awhile now,
> especially in regards to state machines. I recently adopted the approach
> of using an event queue in conjunction with state machines, which you've
> described on several occasions. A lot of the problems and challenges I
> had faced up until that point simply went away with this new (for me)
> approach. But I haven't gone all the way yet by using static STT.

Ain't it great when a Plan comes together?

>
> I'm using C#, and unfortunately there are implementation challenges
> involved in taking the static STT approach. When you initialize the STT,
> you can only initialize it with delegates (akin to function pointers in
> C++) representing static methods. An instance is needed to create a
> delegate representing a non-static method. So you wind up with all of
> the action methods being static with an extra parameter representing the
> current instance. This is certainly doable, but I don't like it. An
> alternative would be to make the STT non-static, but then you have the
> extra memory cost of each instance of a state machine having its own
> copy of the STT. At any rate, this is a low-level problem specific to C#
> that I'm grappling with. The extra cost in memory may be worth the
> clarity at the conceptual level of using an STT.

I should have mentioned that and I was sloppy in saying "method
addresses". It is not just C#; one can't initialize a static table with
instance method addresses for two reasons. One is the hidden 'this'
pointer needed to access the instance attributes. The other is that the
instance methods are usually accessed via offsets in a local table
needed for virtual dispatch, so you have no way to "see" the real static
address of the method. So the actions end up being static as well with
a hard-wired 'this' pointer argument. Fortunately that is not a big
deal because the event queue manager has to have an instance reference
in hand to address the event anyway and nobody is calling the actions
other than the event queue manager. But the C# delegate rigmarole for
initialization does sound a tad kludgy.

>>>Putting aside the various approaches for implementing state machines
>>>(I'm only using a switch statement here because it makes my question
>>>easier to describe), I'm wondering if this is the right granularity
>>>for a class/state machine. Instead of the above, I could divide the
>>>class in two, putting one superstate in one class and the other
>>>superstate in another class. Each class would have an initial state
>>>in which it would ignore all events except the event(s) that causes
>>>the superstate it represents to become active.
>>
>>This is close. Delegate a subset of cohesive object responsibilities
>>to another object. Then design state machines for each object based
>>on the more limited subset of responsibilities for each one.
>>Typically that will completely eliminate the need for superstates.
>>
>>However, it would be easier to make the point with a specific example
>>from your situation rather than generic s1, ..., s22.
>
>
> Well, this has come up in several situations while working on my MIDI
> toolkit in C#, which I've mentioned before. To give a very simple
> example, the music from MIDI files is driven by clock ticks. So I've
> created a clock class to generate tick events. The clock is either
> running or it isn't, so there's really only two states here: running and
> not running.

Ahhh. I knew the login name seemed familiar. Alas, I probably don't
remember the details very well. The mind is always the second thing to go.

>
> However, a clock can be in a master mode, meaning that it generates more
> than just tick messages; it also generates tick messages that are
> transmitted to external source(s). In master mode, it's driving the tick
> generation not only internally for the application but also for any
> device that is connected to the computer via MIDI and is set to receive
> those messages. This allows MIDI music played on one device to be
> syncrhonized with MIDI music played on another device. The master mode
> can either be enabled or disabled. Master mode could be considered a
> superstate of the running state. So we could have something like this:
>
> Master Mode Enabled
> Running
> Not running
> Master Mode Disabled
> Running
> Not running

Note that there is a bad smell here if the Not Running substate is
exactly the same for both modes. A state defines a condition where a
/unique/ set of rules and policies apply. (Some subset of rules and
policies may be shared across states, as in Running, but the set as a
whole must be unique.)

My initial push back is whether you need a state at all for Master Mode.
If I understand this, then you are basically doing conditional event
generation based on the mode, which could be expressed as an attribute:

//generate internal tick messages
if (MASTER_MODE)
// generate external tick messages

However, if the processing associated with generating the messages is
complex...

>
> This seems so simple that it appears that it could be easily coded into
> a single class. But I've done this and not liked the results. The class
> winds up too complicated. So what I've done is to create a simple clock
> class for generating ticks that has no idea about the master mode. Then
> I have a master mode class whose only concern is responding to ticks
> created by the clock class and transmitting messages to external
> devices. (BTW, "transmitting messages" is a little harder than it
> sounds. There are additional messages that have to be sent besides the
> "tick" message plus there is usually a timing conversion that must take
> place as well.)

... as this indicates, then I would look for delegation -- pretty much
as you seem to have done here. This is especially true when things like
handshaking protocols are involved:

1 triggers 1
[Clock] ----------------- [ExternalSurrogate]
| 1 R1
|
R2 |
| triggers
| *
[InternalObject]

It seems to me that your [Clock] has a pretty pedestrian set of
responsibilities. It just generates a set of <presumably simple> events
to internal objects on a clock tick and, if the mode is MASTER, it
generates a single event to [ExternalSurrogate].

Now all the complexity of broadcasting multiple messages to particular
external devices and dealing with the external transmission protocols
lies in [ExternalSurrogate] and its state machine would reflect that.
But the subject matter is also well focused. [If each external device
has its own handshaking protocol you can avoid race conditions and
whatnot by employing an [External Surrogate] instance for each device
and make R1 have 1:* multiplicity (i.e., the clock just issues multiple,
duplicate external messages). You can then use Observer to register
external devices, if necessary.]

I think this is a much better solution than trying to deal with it all
in one state machine using superstates. For example, suppose you get a
race condition because of network delays with a new tick generated
before the [ExternalSurrogate] completes its handshaking (i.e., it is in
the wrong state to accept the next tick message). One answer is to
insert a queue for ticks that [ExternalSurrogate] polls. That's easy
here but it could be a real mess in the superstate cases.

>
> This has made me think about superstate/substates, hence my original
> post. If we have a state machine representing a subsystem, how should we
> partition the state machine into various classes? Would factoring out
> substates as I did above be a good place to start? Or do I have it
> backwards. Should the classes be decided upon first and then design each
> one as a state machine?

Ahhh. Good question. In fact, it is not uncommon in arenas like
telcomms to have requirements specified in terms of monolithic state
machines. My response is the same as the one I made about using Harel.
If one sees a state machine for the requirements so complex that it
must be hierarchical, it is time to look for delegation.

Look for ways to re-allocate the /responsibilities/ among multiple
objects. When doing so abstract the problem space and its entities.
That means identifying problem space entities that would logically "own"
the responsibilities you want to split out. Sometimes that means
splitting up an entity naturally (e.g., Car -> Car, Body, DriveTrain,
etc.). Sometimes it means identifying an entirely different (usually
conceptual) entity.

IOW, identify objects and allocate the responsibilities of the original
state machine before worrying about the inherent sequencing constraints
that the original state machine defines. Once you have reallocated the
responsibilities you can worry about enforcing those constraints in the
individual object state machines and their collaborations. That is,
your last suggestion is the answer.

Almost always that sort of problem space delegation will eliminate the
need for superstates on the first pass. If not, look for an alternative
delegation. If you can't find a reasonable delegation w/o superstates,
contact me offline and you can try to pick up some beer money with a
small wager. B-))

H. S. Lahman

unread,
Jul 10, 2005, 12:29:15 PM7/10/05
to
Responding to Huber...

>> The bottom line was that Pathfinder had to define its own rules for
>> resolving the action sequence/context.
>
>
> Pretty much every standard in computer science leaves such leeway,
> that's called unspecified behavior. The more interesting question is
> whether a user employing two different implementations could run into a
> problem because the behavior is unspecified. For example, when exiting a
> state configuration with orthogonal regions, the UML standard does not
> specify in what sequence the regions are exited. In practice this is not
> a problem because:

>
> a) The user knows (or should know) that the sequence is unspecified

As I recall that was pretty much the problem; the sequence can't be
unspecified in the model because code generators are quite
literal-minded and they do what you say, not what you meant. If a
different code generator using different assumptions is used the
resulting application could potentially yield different results without
any change to the user's model. So the model specification /must/ be
unambiguous about the functional behavior.

[BTW, as I indicated, this issue came up in a meeting I attended a few
years ago. It is possible the spec has been fixed since then because of
all the work done on an execution model starting in v1.5. (Lack of a
proper execution model for UML represented a major hole in the UML spec
until then.) Since I am on the model side, I wouldn't be aware of any
such fixes on the transformation side. However, I am skeptical because
it usually takes years to iron out inconsistencies with peripheral
definitions in UML when something changes. The spec has become so big
that the Left Hand/Right Hand problem is ubiquitous.]

Andreas Huber

unread,
Jul 10, 2005, 5:01:48 PM7/10/05
to
H. S. Lahman wrote:
>> a) The user knows (or should know) that the sequence is unspecified
>
> As I recall that was pretty much the problem; the sequence can't be
> unspecified in the model because code generators are quite
> literal-minded and they do what you say, not what you meant. If a
> different code generator using different assumptions is used the
> resulting application could potentially yield different results
> without any change to the user's model.

Only if the user somehow relies on one particular implementation of the
unspecified behavior and is then foolish enough to expect that his
model/code will behave the same on another implementation. In your
words: DON'T DO THAT!
Granted, there are some pretty unfortunate examples of unspecified
behavior, like e.g. in C++ that std::type_info::name() is not guaranteed
to return the same string on two different platforms. If the user
doesn't know he can easily write non-portable code that way. However,
for the cases where it is actually hard to depend your code on such
unspecified behavior (as in the exit-sequence case) I don't think it is
a bad thing at all. Since statecharts are a graphical formalism, no
clear ordering of orthogonal regions can exist. It could in theory but
then just shifting the border between two regions could alter the
sequence what I would consider a *very* bad thing.
A user not asking himself the obvious question how/whether the order is
defined before he depends on it simply acts *very* sloppily, don't you
think?

> So the model specification
> /must/ be unambiguous about the functional behavior.

No. As I said before, in the end it all boils down to following:

1) It should be clear that something is unspecified /
implementation-defined
2) It should be hard to become dependent on unspecified behavior

I think both is true for the exit-sequence in statecharts, so it is not
a problem in practice. Even if people don't read documentation they will
only notice *very* rarely.
For std::type_info::name() only 1) is true so people not reading
documentation can easily fall into this trap. Therefore, if at all
possible, standards bodies should avoid this.

H. S. Lahman

unread,
Jul 11, 2005, 2:05:53 PM7/11/05
to
Responding to Huber...

>>> a) The user knows (or should know) that the sequence is unspecified
>>
>>
>> As I recall that was pretty much the problem; the sequence can't be
>> unspecified in the model because code generators are quite
>> literal-minded and they do what you say, not what you meant. If a
>> different code generator using different assumptions is used the
>> resulting application could potentially yield different results
>> without any change to the user's model.
>
>
> Only if the user somehow relies on one particular implementation of the
> unspecified behavior and is then foolish enough to expect that his
> model/code will behave the same on another implementation. In your
> words: DON'T DO THAT!

But the OOA model is a functional /specification/ of solution behavior.
So the implemented behavior must be deterministic and unambiguous from
that specification. The model is also platform independent by
definition. As such its behavior must be consistent across platforms,
which includes the transformation engine used to generate code for a
particular platform. So the behavior represented by the model can't be
unspecified or left as a local platform option...

> Granted, there are some pretty unfortunate examples of unspecified
> behavior, like e.g. in C++ that std::type_info::name() is not guaranteed
> to return the same string on two different platforms. If the user
> doesn't know he can easily write non-portable code that way. However,
> for the cases where it is actually hard to depend your code on such
> unspecified behavior (as in the exit-sequence case) I don't think it is
> a bad thing at all. Since statecharts are a graphical formalism, no
> clear ordering of orthogonal regions can exist. It could in theory but
> then just shifting the border between two regions could alter the
> sequence what I would consider a *very* bad thing.
> A user not asking himself the obvious question how/whether the order is
> defined before he depends on it simply acts *very* sloppily, don't you
> think?

So what does the modeler do upon realizing that the behavior is not
specified by a feature in the meta-model? The modeler /must/ specify
the behavior unambiguously in a platform-independent manner for the code
generator. So the modeler does the same thing a C++ developer trying to
write a portable application would do: don't use the unspecified
feature! To ensure that, the unspecified feature should not be included
in the MDA profile used to develop the model. In this case we come full
circle to my original guideline: don't use Harel. B-))

Andreas Huber

unread,
Jul 11, 2005, 4:27:25 PM7/11/05
to
H. S. Lahman wrote:
>>> As I recall that was pretty much the problem; the sequence can't be
>>> unspecified in the model because code generators are quite
>>> literal-minded and they do what you say, not what you meant. If a
>>> different code generator using different assumptions is used the
>>> resulting application could potentially yield different results
>>> without any change to the user's model.
>>
>>
>> Only if the user somehow relies on one particular implementation of
>> the unspecified behavior and is then foolish enough to expect that
>> his model/code will behave the same on another implementation. In
>> your words: DON'T DO THAT!
>
> But the OOA model is a functional /specification/ of solution
> behavior. So the implemented behavior must be deterministic and
> unambiguous from that specification.

So? A model can be perfectly deterministic in its behavior, despite the
fact that it makes use of features the semantics of which are partially
implementation-defined. It is up to the user to use partially
implementation-defined features in a way so that his model stays
portable. See below.

> The model is also platform
> independent by definition. As such its behavior must be consistent
> across platforms, which includes the transformation engine used to
> generate code for a particular platform. So the behavior represented
> by the model can't be unspecified or left as a local platform
> option...

Why not? You can still write perfectly portable models/code despite the
fact that certain aspects of the behavior are implementation-defined.
See below.

>> Granted, there are some pretty unfortunate examples of unspecified
>> behavior, like e.g. in C++ that std::type_info::name() is not
>> guaranteed to return the same string on two different platforms. If
>> the user doesn't know he can easily write non-portable code that
>> way. However, for the cases where it is actually hard to depend your
>> code on such unspecified behavior (as in the exit-sequence case) I
>> don't think it is a bad thing at all. Since statecharts are a
>> graphical formalism, no clear ordering of orthogonal regions can
>> exist. It could in theory but then just shifting the border between
>> two regions could alter the sequence what I would consider a *very*
>> bad thing. A user not asking himself the obvious question how/whether
>> the order
>> is defined before he depends on it simply acts *very* sloppily,
>> don't you think?
>
> So what does the modeler do upon realizing that the behavior is not
> specified by a feature in the meta-model?

Come on, you know that as well as I do. The implementer simply chooses a
behavior that is consistent with the specification. The unspecified bits
can be implemented in any way he likes. For std::type_info::name() some
platforms return the actual name of the class like e.g.
"std::vector<int>", while others return the mangled name like e.g.
"1vector$int" or some such. In the exit sequence case the implementer
simply picks *any* orthogonal region, exits all its states, then picks
the next region and so forth.

> The modeler /must/ specify
> the behavior unambiguously in a platform-independent manner for the
> code generator.

No, no. The implementer just needs to implement the specification for
his platform(s).

> So the modeler does the same thing a C++ developer
> trying to write a portable application would do: don't use the
> unspecified feature!

Nonsense. People have been using std::type_info::name() in *portable*
code for ages. Pretty much the only thing you can't do with it is write
a name of a class into a file and expect to read a valid class name from
that file on a different platform. Many other uses are perfectly safe
and portable.

> To ensure that, the unspecified feature should
> not be included in the MDA profile used to develop the model. In
> this case we come full circle to my original guideline: don't use
> Harel. B-))

More nonsense. Lets say you have the following statechart (copy paste
into an editor with fixed-width font):

-----
| A |
|---------------------
| ----- | ----- |---
| | B | | | C | | | EvX
| ----- | ----- |<--
----------------------

When EvX triggers the only transition in this chart, the exit action
sequence could be B, C, A or C, B, A, right? So, a user running his
model on more than one platform can happily do so without any problems
whatsoever, given that the exit action of B does not depend on the side
effects of the exit action of C and vice versa (this is kind of hard to
do in practice anyway). The situation is pretty much the same as with
std::type_info::name(): As long as you obey certain rules you can write
perfectly portable code/models with it.
So, std::type_info::name() and orthogonal regions are perfectly usable,
even if certain aspects of the semantics are unspecified. Welcome to the
real world.

One more thing: If you still insist that exit action sequence must be
fully specified in the presence of orthogonal regions, I'd be interested
in hearing your algorithm to derive the sequence from an arbitrary
complex *UML* statechart... :-)

Andreas Huber

unread,
Jul 11, 2005, 4:36:39 PM7/11/05
to
Andreas Huber wrote:
> One more thing: If you still insist that exit action sequence must be
> fully specified in the presence of orthogonal regions, I'd be
> interested in hearing your algorithm to derive the sequence from an
> arbitrary complex *UML* statechart... :-)

The above sentence of course makes little sense given that you said that
you're not using Harel because of the implementation-defined behavior.
Please disregard.

raxit...@gmail.com

unread,
Jul 12, 2005, 4:02:11 AM7/12/05
to
Hello Andreas Huber ,H.S.Lahman and all....


the W3C First Working Draft about SCXML (State Chart XML)is publically
available on 5th july 2005

www.w3c.org/tr/scxml

Plz Take a look.....Its uses Harel SC ....

Also Can any one plz Let me know how Harel Approach is NOT convinient
for Platform Development...( Mainly H.S. Lahman) Because from Previous
post i am quiet ambiguous about How Harel Approach is NOT a Good
Choice.....?


Regards
Raxit

Wavemaker

unread,
Jul 12, 2005, 7:07:41 AM7/12/05
to
"H. S. Lahman" wrote:

<sip>

> Beyond that, unless you are writing a code generator, the switch
> structure is a very bad idea. Regardless of whether you actually use
> a switch statement, you are making flow of control decisions in your
> business logic based on what the current state is. That is a no-no in
> FSAs because a state machine state should not depend in any way on
> knowing what the previous state was of the next state will be. That
> is, the transition is determined strictly by the event identity and
> the current state of the state machine via an STT. (Not to mention
> that the current state should always be a private attribute that no
> other object should have access to.)

On the subject of implementing STTs as two dimensional arrays, I've run
into something of a problem. I have an event queue class for dispatching
events to my state machine classes. This represents a subsystem in the
software and is hidden inside a facade class. This approach is working
well for me. One thing that concerns me is that the number of events the
subsystem uses is getting rather large, around 50 or so.

I have several questions. Is the number of events used in a subsystem
somehow indicative of the subsystem's cohesiveness? If so, what is a
rule-of-thumb for sensing how much is too much?

Most of the state machines in the subsystem only subscribe to a small
subset of the total number of events. This leads to a problem with the
STTs for each state machine. They are very wasteful because of their
sparseness. I've been trying to concote a way make the STTs smaller.
Hashing the event ID would be one, but that's possibly too slow.

Another idea would be for each state machine to register a substitue
value for an event ID when it subscribes to that event with the event
queue. When the event queue pops an event, it retrieves the alternate ID
value and dispatches that along with the data packet to the state
machine. This is done for each state machine so that each one can have
their own private substitute event value. This solution sounds more
complicated than it actually is. And yet another approach would be for
each state machine to offset the event ID value somehow, but that's
tricky and makes the event list, which is just an enumeration, fragile.

Anyway, this is a low-level implementation problem, but it does have me
a bit stumped. I like the STT array approach because it directly maps to
an STT you can write by hand. And I wrote an STT state machine code
generator tonight that was almost trivial to do. I also like how the
action methods are completely ignorant about what the current state is
or what the next transition will be. So this approach is becoming my
favorite for implementing state machines. That is if I can figure out
this event ID problem.

Andreas Huber

unread,
Jul 12, 2005, 10:38:41 AM7/12/05
to
Hello Raxit

> the W3C First Working Draft about SCXML (State Chart XML)is publically
> available on 5th july 2005
>
> www.w3c.org/tr/scxml
>
> Plz Take a look.....Its uses Harel SC ....
>
> Also Can any one plz Let me know how Harel Approach is NOT convinient
> for Platform Development...( Mainly H.S. Lahman) Because from Previous
> post i am quiet ambiguous about How Harel Approach is NOT a Good
> Choice.....?

FWIW, I think it is fair to say that Mr. Lahmans view of UML/Harel
state machine semantics is one of a small minority. For more
information on his view of things, see the following thread:

http://tinyurl.com/dj6kz

Unfortunately the thread very quickly deviates into very theoretical
arguments so its probably not too interesting to read in full length.

Most people think that Harel/UML state machines are a good idea.

H. S. Lahman

unread,
Jul 12, 2005, 12:29:59 PM7/12/05
to
Responding to Huber...

>> So what does the modeler do upon realizing that the behavior is not
>> specified by a feature in the meta-model?
>
>
> Come on, you know that as well as I do. The implementer simply chooses a
> behavior that is consistent with the specification. The unspecified bits
> can be implemented in any way he likes. For std::type_info::name() some
> platforms return the actual name of the class like e.g.
> "std::vector<int>", while others return the mangled name like e.g.
> "1vector$int" or some such. In the exit sequence case the implementer
> simply picks *any* orthogonal region, exits all its states, then picks
> the next region and so forth.
>
>> The modeler /must/ specify
>> the behavior unambiguously in a platform-independent manner for the
>> code generator.
>
>
> No, no. The implementer just needs to implement the specification for
> his platform(s).

But there is no implementer making application-specific decisions!

We are talking about a code generator that is tied to a particular
computing environment. (I.e., the analogue of the C++ compiler in your
example.) If the specification behavior is ambiguous (unspecified at
the model level) the code generator must make an arbitrary choice of how
to implement the behavior, just like the C++ compiler on a specific
platform. That choice will apply to all OOA models processed in that
environment and it could affect the run-time results, just as the C++
compiler implementation would.

If that choice produces results that are not consistent with the
original functional requirements, then the application is broken for
that computing environment, just as the C++ program would be if ported
to a different platform. Worse, there is no way for the modeler to fix
the problem other than modifying the model for the specific environment.
Then the model is not independent of the computing environment and the
modeler needs to know far too much about the implementation...


>
>> So the modeler does the same thing a C++ developer
>> trying to write a portable application would do: don't use the
>> unspecified feature!
>
>
> Nonsense. People have been using std::type_info::name() in *portable*
> code for ages. Pretty much the only thing you can't do with it is write
> a name of a class into a file and expect to read a valid class name from
> that file on a different platform. Many other uses are perfectly safe
> and portable.

All it takes is one syntactically valid usage. But to avoid those
"special" situations the developer needs to know about them, which means
the developer must know how the implementation works.

However, that isn't the real issue. Your example is at the 3GL model
level. At that level the 3GL programmer must anticipate the vagaries of
the platform implementation by providing explicit "workaround" code.
IOW, the 3GL programmer explicitly anticipates the possible
implementations by using compiler pragmas or whatever for the
/anticipated/ environments.

Now switch to the OOA level and substitute 3GL programmer -> OOA
modeler. The OOA modeler would be in exactly the same position of
needing to anticipate the /possible/ implementations and providing
"workaround" model elements to deal with the results of those
possibilities in order to ensure the requirements were satisfied in all
possible situations. That trashes implementation-independence, which is
essentially what your were denigrating when you said

"Granted, there are some pretty unfortunate examples of unspecified
behavior, like e.g. in C++ that std::type_info::name() is not guaranteed

to return the same string on two different platforms...Therefore, if at

all possible, standards bodies should avoid this."

My assertion is simply that the UML standard should avoid that when
defining its meta-model of Harel. (As I already indicated, for all I
know OMG has fixed the problem after providing a meta-model for
execution semantics.)

H. S. Lahman

unread,
Jul 12, 2005, 12:49:09 PM7/12/05
to
Responding to Raxitsheth...

> the W3C First Working Draft about SCXML (State Chart XML)is publically
> available on 5th july 2005
>
> www.w3c.org/tr/scxml
>
> Plz Take a look.....Its uses Harel SC ....
>
> Also Can any one plz Let me know how Harel Approach is NOT convinient
> for Platform Development...( Mainly H.S. Lahman) Because from Previous
> post i am quiet ambiguous about How Harel Approach is NOT a Good
> Choice.....?

Harel is fine for describing complex sequencing constraints in the
large. It provides a quite elegantly compact view -- once one gets it
right. So I have no problem with requirements being specified with
Harel or even documenting things like subsystem behavior with Harel.

However, in the OO paradigm one uses abstraction and logical
indivisibility as tools for managing complexity. Within a given
subsystem an object represents the abstraction of a single, identifiable
problem space entity. We also use abstraction to simplify our view of
the entity to the essential responsibilities needed to resolve
requirements _in the subsystem context_. In addition, the OO paradigm
uses peer-to-peer collaboration to eliminate hierarchical implementation
dependencies commonly found in "spaghetti code".

All these (and other OO practices) conspire to produce lots of small,
highly cohesive objects that interact with one another directly. IOW,
we don't want objects to be complicated or have complex sequencing
constraints for their behaviors. (You have probably encountered people
talking about "god" objects being evil in OO applications, which is just
another way of saying objects should be simple.)

So my problem with Harel is not with Harel itself (though I don't like
the notion of history because it violates FSA structure). Rather, it is
with what it is almost always a symptom of in a single object state
machine: objects that are too complex. So if I see an object state
machine done with Harel I immediately step up a level and look for a
delegation solution that would simplify its responsibilities. IOW, if
Harel is used to define large scale constraints, those should be
distributed across multiple, independent object state machines in an OOA
solution.

H. S. Lahman

unread,
Jul 12, 2005, 2:26:45 PM7/12/05
to
Responding to Wavemaker...

> On the subject of implementing STTs as two dimensional arrays, I've run
> into something of a problem. I have an event queue class for dispatching
> events to my state machine classes. This represents a subsystem in the
> software and is hidden inside a facade class. This approach is working
> well for me. One thing that concerns me is that the number of events the
> subsystem uses is getting rather large, around 50 or so.
>
> I have several questions. Is the number of events used in a subsystem
> somehow indicative of the subsystem's cohesiveness? If so, what is a
> rule-of-thumb for sensing how much is too much?

Let's say each object state machine consumes 5 events on average. Then
you would have about 10 object state machines, which I wouldn't worry
about. If you are looking for heuristics, I would suggest the number of
objects with state machines and the complexity of those state machines.
As a crude reviewer rule of thumb I would want some solid
justification for

(A) an object state machine with more than 6 states and 10 transitions.

(B) a subsystem with more than 10 active objects (i.e., objects that
have a state machine life cycle).

However, both guidelines have a /lot/ of slush. Basically when the
guideline is broken one should just take a closer look for reasonable
alternatives. If there are any, that's fine.

BTW, one resolves (A) through delegation; splitting off some of the
object responsibilities into another object. One resolves (B) by
looking at the basic application partitioning; essentially breaking up
the subsystem via delegation.

>
> Most of the state machines in the subsystem only subscribe to a small
> subset of the total number of events. This leads to a problem with the
> STTs for each state machine. They are very wasteful because of their
> sparseness. I've been trying to concote a way make the STTs smaller.
> Hashing the event ID would be one, but that's possibly too slow.
>
> Another idea would be for each state machine to register a substitue
> value for an event ID when it subscribes to that event with the event
> queue. When the event queue pops an event, it retrieves the alternate ID
> value and dispatches that along with the data packet to the state
> machine. This is done for each state machine so that each one can have
> their own private substitute event value. This solution sounds more
> complicated than it actually is. And yet another approach would be for
> each state machine to offset the event ID value somehow, but that's
> tricky and makes the event list, which is just an enumeration, fragile.

Since the STT is usually implemented as a static Class table, many
implementations don't worry about about the STT space in these days of
VM. However, there are some tricks one can use to reduce the table size.

As you suggest, you can hash the event ID. The problem there is that
the hash function may need tinkering as events are added and removed
during maintenance. That's especially true if the hash function is
tailored to the state machine (e.g., applies and offset first).

Another way is to number the events consecutively by "walking" one
<receiving> state machine at a time. Now the lookup index for the STT
is just the event id minus some offset. However, you have to leave
slush for adding a few events for each state machine and one will be in
annoying trouble if you ever exceed it during maintenance because
everybody has to shift. It also requires that one recognize events that
are directed to multiple state machines and make sure they always have
the same number (e.g., dedicate the first N numbers for common events).

Another solution is to get cute with the event identifier itself as a
variation on hashing. Assuming 16 bits to play with, one can split the
16 bits so the low 8 are an event number and the higher 16 bits are a
bitmap of target classes. Now you can identify 256 events as
consecutive integers to each of 256 different state machines. (The
event number is unlikely to exceed 16 or 32 for any given state machine,
so you don't really need 256 event numbers.) So the "hash function" is
just masking off the higher order bits. (The event queue manager can do
a check of the high order bit to make sure it is going to the right object.)

Obviously this doesn't work for events going to multiple state machines,
but there are usually not very many of those. So you can deal with that
by having a "multiple" bit in the high order bits. You would need the
STT to have two sections, one for dedicated events and one for common
events and the common section may be pretty sparse, but you should still
save substantially on space since you are likely to have only a small
number of events consumed by that state machine.

[More cuteness. Suppose you only need the low order 4 bits for event
numbers (i.e., no state machine has more than 16 transitions). Make the
5th bit the "multi" bit and now do the lookup on the low order 5 bits.
It sounds nasty but with judicious uses of constants and bitwise
operators it is actually a pretty routine way to do things. At least in
R-T/E. B-)]

Another common solution is the one you suggest about "registering" a
<small> consecutive index to the event id for the local state machine.
Then the event queue manager just has a single 2D table for event/object
lookup (though it is a pretty big table). [This is often used in
commercial code generators that do not know the maximum number of
transitions or the number of common events a priori. (Though a highly
optimized code generator can figure it out by looking at all the state
machines first.)]

While it is simple to execute, initializing the table and keeping it in
synch during changes can be a problem. Of course the bitwise approach I
suggested above has similar problems if one is not sufficiently
prescient about event number range slush and common events.

The bottom line is that there are a number of ways to address the
problem, each with their own trade-offs. Safety will largely depend on
how much effort you are willing to invest in infrastructure to support
things like table initialization. But if you have gotten to the point
of writing your own state machine code generator, you are most of the
way there because the infrastructure investment can be highly reuasable
in the same way.


>
> Anyway, this is a low-level implementation problem, but it does have me
> a bit stumped. I like the STT array approach because it directly maps to
> an STT you can write by hand. And I wrote an STT state machine code
> generator tonight that was almost trivial to do. I also like how the
> action methods are completely ignorant about what the current state is
> or what the next transition will be. So this approach is becoming my
> favorite for implementing state machines. That is if I can figure out
> this event ID problem.

Us proselytizers always like to see converts.

Andreas Huber

unread,
Jul 12, 2005, 3:03:59 PM7/12/05
to
H. S. Lahman wrote:
> Responding to Huber...
>
>>> So what does the modeler do upon realizing that the behavior is not
>>> specified by a feature in the meta-model?
>>
>>
>> Come on, you know that as well as I do. The implementer simply
>> chooses a behavior that is consistent with the specification. The
>> unspecified bits can be implemented in any way he likes. For
>> std::type_info::name() some platforms return the actual name of the
>> class like e.g. "std::vector<int>", while others return the mangled
>> name like e.g. "1vector$int" or some such. In the exit sequence case
>> the implementer simply picks *any* orthogonal region, exits all its
>> states, then picks the next region and so forth.

Any comment on this?

>>
>>> The modeler /must/ specify
>>> the behavior unambiguously in a platform-independent manner for the
>>> code generator.
>>
>>
>> No, no. The implementer just needs to implement the specification for
>> his platform(s).
>
> But there is no implementer making application-specific decisions!

I don't understand that. I suspect we don't mean the same thing when we
say "modeler". When I first saw your usage of this word I thought that
it
is the equivalent of a programmer, i.e. a user of a modeling tool. But
then I saw the following sentence:

> The modeler /must/ specify
> the behavior unambiguously in a platform-independent manner for the
> code generator.

Since the user of a modeling tool doesn't specify anything *for* a code
generator (he doesn't even need to be aware that such a thing exists), I
then thought that you mean that modeler means the *implementer* of a
modeling tool and went by that definition. A clarification would be
appreciated.

> We are talking about a code generator that is tied to a particular
> computing environment. (I.e., the analogue of the C++ compiler in
> your example.) If the specification behavior is ambiguous
> (unspecified at the model level) the code generator must make an
> arbitrary choice of how to implement the behavior, just like the C++
> compiler on a specific platform.

Yes... (this just reiterates what I've been saying)

> That choice will apply to all OOA
> models processed in that environment and it could affect the run-time
> results, just as the C++ compiler implementation would.

Yes...

> If that choice produces results that are not consistent with the
> original functional requirements,

Why should it? I said more than once that the implementer chooses a
behavior that is *consistent* with the specification.

> then the application is broken for
> that computing environment, just as the C++ program would be if ported
> to a different platform.

No, no, no. What you say here applies if and only if the user doesn't
read the documentation/specification and thus makes assumptions about
the behavior of a feature/function. Example: Only if the user assumes
that std::type_info::name() returns the same string across *all*
platforms then (and only then) is his program broken. As I said before:
There are many other perfectly portable uses of this function.

> Worse, there is no way for the modeler to
> fix the problem other than modifying the model for the specific
> environment. Then the model is not independent of the computing
> environment and the modeler needs to know far too much about the
> implementation...

I assume you mean the user of a modeling tool. No, the user doesn't
need to know anything about the implementation. He only needs to know
about the *specification* of the model feature/function.

>>> So the modeler does the same thing a C++ developer
>>> trying to write a portable application would do: don't use the
>>> unspecified feature!
>>
>>
>> Nonsense. People have been using std::type_info::name() in *portable*
>> code for ages. Pretty much the only thing you can't do with it is
>> write a name of a class into a file and expect to read a valid class
>> name from that file on a different platform. Many other uses are
>> perfectly safe and portable.
>
> All it takes is one syntactically valid usage.

Not sure what you mean with this.

> But to avoid those
> "special" situations the developer needs to know about them,

Exactly! This all boils down to the fact that the developer needs to
read and understand the documentation/specification. If he doesn't, his
program is likely broken. In the real world people don't read
documentation that's why I said that the example of
std::type_info::name() is unfortunate.

> which
> means the developer must know how the implementation works.

No, no, no. The programmer only needs to know the *specification* of the
function.

> However, that isn't the real issue. Your example is at the 3GL model
> level.

I this context I don't see any distinction between a 3GL language and an
UML modeling tool. Both are "languages" (one is just graphical), both
have implementation-defined behavior and you can write perfectly
portable applications in both.

> At that level the 3GL programmer must anticipate the vagaries
> of the platform implementation by providing explicit "workaround"
> code.

This sounds as if you mean that this is *always* the case. Fact is: It
isn't. There are a lot of applications that don't need one single line
of platform-specific code.

> IOW, the 3GL programmer explicitly anticipates the possible
> implementations by using compiler pragmas or whatever for the
> /anticipated/ environments.

I suspect you haven't been writing an application in a 3GL language for
like 10-15 years? Things have changed considerably since then.

> Now switch to the OOA level and substitute 3GL programmer -> OOA
> modeler. The OOA modeler would be in exactly the same position of
> needing to anticipate the /possible/ implementations and providing
> "workaround" model elements to deal with the results of those
> possibilities in order to ensure the requirements were satisfied in
> all possible situations.

At this point I'm wondering whether you don't *want* to understand. I'm
saying this because you did not respond to the paragraph with the UML
statechart in my last post. This paragraph clearly shows that you can
model a perfectly portable model despite the fact that some of the
behavior is implementation-defined. I think we can considerably shorten
the discussion if you have a look at that paragraph and tell me exactly
how that statechart has *always* non-portable behavior (it seems as
though you're claiming that).

> That trashes implementation-independence,

No, it doesn't, see below:

> which is essentially what your were denigrating when you said
>
> "Granted, there are some pretty unfortunate examples of unspecified
> behavior, like e.g. in C++ that std::type_info::name() is not
> guaranteed to return the same string on two different
> platforms...Therefore, if at all possible, standards bodies should
> avoid this."

No. That function only exhibits non-portable behavior if it is used for
something it was never intended for. Unfortunately, that use-case is a
pretty common one (cross-platform persistence) and that's why people who
fail to read the documentation/specification frequently fall into this
trap. THAT doesn't make the function *generally* unusable.

> My assertion is simply that the UML standard should avoid that when
> defining its meta-model of Harel.

I bet that the exit action sequence of an UML FSM with multiple
orthogonal regions will be implementation-defined until the exact moment
when the standard mandates that each orthogonal region of a given state
needs to have an unambiguous number. AFAIK, this is not currently the
case and that's why I also bet that the sequence is still
implementation-defined in the current specification.

> (As I already indicated, for all I
> know OMG has fixed the problem after providing a meta-model for
> execution semantics.)

You definitely need to show me exactly where they "fix" the exit-action
sequence of statecharts with multiple orthogonal regions.

H. S. Lahman

unread,
Jul 13, 2005, 12:39:02 PM7/13/05
to
Responding to Huber...

>>> Come on, you know that as well as I do. The implementer simply
>>> chooses a behavior that is consistent with the specification. The
>>> unspecified bits can be implemented in any way he likes. For
>>> std::type_info::name() some platforms return the actual name of the
>>> class like e.g. "std::vector<int>", while others return the mangled
>>> name like e.g. "1vector$int" or some such. In the exit sequence case
>>> the implementer simply picks *any* orthogonal region, exits all its
>>> states, then picks the next region and so forth.
>
>
> Any comment on this?

I already did in the message. (A) the "implementer" has to provide
explicitly in the model for the anticipated vagaries of the
implementation, which defeats portability. (B) there is no implementer
to provide such workarounds.

>
>>>
>>>> The modeler /must/ specify
>>>> the behavior unambiguously in a platform-independent manner for the
>>>> code generator.
>>>
>>>
>>>
>>> No, no. The implementer just needs to implement the specification for
>>> his platform(s).
>>
>>
>> But there is no implementer making application-specific decisions!
>
>
> I don't understand that. I suspect we don't mean the same thing when we
> say "modeler". When I first saw your usage of this word I thought that it
> is the equivalent of a programmer, i.e. a user of a modeling tool. But
> then I saw the following sentence:

You can implement a 3GL program two ways. An implementer can manually
turn it into Assembly or one can employ a compiler. The situation is
the same for an OOA model. An implementer can implement it in a 3GL or
Assembly by elaboration or one can use a code generator. The context
here is a code generator where there is no implementer to provide
workarounds for the unspecified model behavior to ensure the
requirements are satisfied for all implementations. Therefore the model
/must/ be fully specified.

>
>> The modeler /must/ specify
>> the behavior unambiguously in a platform-independent manner for the
>> code generator.
>
>
> Since the user of a modeling tool doesn't specify anything *for* a code
> generator (he doesn't even need to be aware that such a thing exists), I
> then thought that you mean that modeler means the *implementer* of a
> modeling tool and went by that definition. A clarification would be
> appreciated.

Yes, the implementer of the transformation engine must make a decision.
But it is the same decision that the C++ compiler implementer makes.
Like the C++ compiler, that decision applies to all OOA models
transformed for that environment and the transformation engine is tied
to a particular computing environment. More important, the OOA modeler
has no control over it. Therefore any decision made to resolve the
unspecified behavior may not yield the correct results in another
computing environment unless the OOA modeler somehow modifies the OOA model.

>> If that choice produces results that are not consistent with the
>> original functional requirements,
>
>
> Why should it? I said more than once that the implementer chooses a
> behavior that is *consistent* with the specification.

As I have said several times, there is no such implementer when one uses
a platform-specific code generator.

The only way to deal with unspecified behavior in the model artifacts is
for the modeler to provide the same sort of hacks that the C++ developer
would have to provide around std:type_info::name() to deal with all
possible platform implementations if one wanted a complete and correct
3GL specification that was portable. And that would have to be done _in
the model_ because there is no implementer to provide
application-specific fixups for the implementation in hand to ensure
correct behavior on a particular platform.

>
>> then the application is broken for
>> that computing environment, just as the C++ program would be if ported
>> to a different platform.
>
>
> No, no, no. What you say here applies if and only if the user doesn't
> read the documentation/specification and thus makes assumptions about
> the behavior of a feature/function. Example: Only if the user assumes
> that std::type_info::name() returns the same string across *all*
> platforms then (and only then) is his program broken. As I said before:
> There are many other perfectly portable uses of this function.

And how does the C++ developer provide a complete, correct specification
of behavior that will execute properly on different platforms where
different strings are returned? Since the program code must process the
returned string properly, the only way to do that is for the C++
developer to anticipate the different platform implementations and
provide pragmas, macros, or whatever that insert the right processing
code for each anticipated platform. That has to be hard-coded _in the
C++ program_ by the C++ developer using the feature. In effect the C++
developer is just creating multiple platform-specific models where the
behavior /is/ properly specified.

>
>> Worse, there is no way for the modeler to
>> fix the problem other than modifying the model for the specific
>> environment. Then the model is not independent of the computing
>> environment and the modeler needs to know far too much about the
>> implementation...
>
>
> I assume you mean the user of a modeling tool. No, the user doesn't
> need to know anything about the implementation. He only needs to know
> about the *specification* of the model feature/function.

But that's the point. If the artifact specification says the behavior
is unspecified, then the modeler doesn't know what it will be in an
arbitrary computing environment and, consequently, can't know that
requirements are correctly resolved.

>> But to avoid those
>> "special" situations the developer needs to know about them,
>
>
> Exactly! This all boils down to the fact that the developer needs to
> read and understand the documentation/specification. If he doesn't, his
> program is likely broken. In the real world people don't read
> documentation that's why I said that the example of
> std::type_info::name() is unfortunate.

No. The documentation/specification you are talking about here is for
the specific platform/computing environment. That is necessary to
predict what the actual behavior will be when the model artifact
behavior is unspecified.

The OOA model is, by definition, independent of particular computing
environments. So the model developer need not and should not know
anything about particular implementations in particular computing
environments. Much more important, the developer should not have to
explicitly provide workarounds in the model itself to ensure correct
behavior in anticipated computing environments that implement
differently. Therefore the model artifacts must be fully specified.

>> However, that isn't the real issue. Your example is at the 3GL model
>> level.
>
>
> I this context I don't see any distinction between a 3GL language and an
> UML modeling tool. Both are "languages" (one is just graphical), both
> have implementation-defined behavior and you can write perfectly
> portable applications in both.
>
>> At that level the 3GL programmer must anticipate the vagaries
>> of the platform implementation by providing explicit "workaround"
>> code.
>
>
> This sounds as if you mean that this is *always* the case. Fact is: It
> isn't. There are a lot of applications that don't need one single line
> of platform-specific code.

It is always the case if the developer wants the program to be portable
across platforms where the implementations are different. An OOA model
must /always/ be portable to any computing environment. That's why OOA
is different than OOD even though one applies exactly the same
methodology and uses the same notation.

>
>> IOW, the 3GL programmer explicitly anticipates the possible
>> implementations by using compiler pragmas or whatever for the
>> /anticipated/ environments.
>
>
> I suspect you haven't been writing an application in a 3GL language for
> like 10-15 years? Things have changed considerably since then.

OK, enlighten me. How would you specify the correct processing for a
program the processes a class name extracted by std:type_info::name() by
a program on another platform that uses different mangling?

>
>> Now switch to the OOA level and substitute 3GL programmer -> OOA
>> modeler. The OOA modeler would be in exactly the same position of
>> needing to anticipate the /possible/ implementations and providing
>> "workaround" model elements to deal with the results of those
>> possibilities in order to ensure the requirements were satisfied in
>> all possible situations.
>
>
> At this point I'm wondering whether you don't *want* to understand.

Once again you have shut down a thread with me via ad hominem attacks.
Note that it was your follow-up message that indicated that your state
machine example was not relevant. Ta-ta.

Andreas Huber

unread,
Jul 13, 2005, 7:40:22 PM7/13/05
to
It is plain obvious that we're obviously not understanding each other
most of the time. I can only guess but it seems we often have different
ideas of what words like platform, implementer, user, modeler, etc.
mean. I therefore only respond to the sections where I think we are
still on the same page.


H. S. Lahman wrote:
> Responding to Huber...
>
>>>> Come on, you know that as well as I do. The implementer simply
>>>> chooses a behavior that is consistent with the specification. The
>>>> unspecified bits can be implemented in any way he likes. For
>>>> std::type_info::name() some platforms return the actual name of the
>>>> class like e.g. "std::vector<int>", while others return the mangled
>>>> name like e.g. "1vector$int" or some such. In the exit sequence
>>>> case the implementer simply picks *any* orthogonal region, exits
>>>> all its states, then picks the next region and so forth.
>>
>>
>> Any comment on this?
>
> I already did in the message. (A) the "implementer" has to provide
> explicitly in the model for the anticipated vagaries of the
> implementation, which defeats portability.

I completely fail to understand this. And once more I suspect we don't
mean the same when we say "implementer". My definition of an implementer
is someone who implements a platform. A platform can be a C++ compiler +
standard libraries (examples: gcc, Microsoft Visual C++, etc.) *or* it
can also be a UML modeling tool (Rhapsody, Rose RT, etc.).

> (B) there is no
> implementer to provide such workarounds.

No understanding here either. Why does the implementer need to provide
work-arounds? How can there be no implementer?

>>
>>>>
>>>>> The modeler /must/ specify
>>>>> the behavior unambiguously in a platform-independent manner for
>>>>> the code generator.
>>>>
>>>>
>>>>
>>>> No, no. The implementer just needs to implement the specification
>>>> for his platform(s).
>>>
>>>
>>> But there is no implementer making application-specific decisions!
>>
>>
>> I don't understand that. I suspect we don't mean the same thing when
>> we say "modeler". When I first saw your usage of this word I thought
>> that it is the equivalent of a programmer, i.e. a user of a modeling
>> tool. But then I saw the following sentence:
>
> You can implement a 3GL program two ways. An implementer can manually
> turn it into Assembly or one can employ a compiler. The situation is
> the same for an OOA model. An implementer can implement it in a 3GL
> or Assembly by elaboration or one can use a code generator. The
> context here is a code generator where there is no implementer to
> provide
> workarounds for the unspecified model behavior to ensure the
> requirements are satisfied for all implementations. Therefore the
> model /must/ be fully specified.

I've read the above at least 10 times, I still don't understand it.
Especially I fail to see how this is relevant to my paragraph above.

>>> The modeler /must/ specify
>>> the behavior unambiguously in a platform-independent manner for the
>>> code generator.
>>
>>
>> Since the user of a modeling tool doesn't specify anything *for* a
>> code generator (he doesn't even need to be aware that such a thing
>> exists), I then thought that you mean that modeler means the
>> *implementer* of a modeling tool and went by that definition. A
>> clarification would be appreciated.
>
> Yes, the implementer of the transformation engine must make a
> decision. But it is the same decision that the C++ compiler
> implementer makes.

I don't see how this answers my request for clarification what you mean
by "modeler". Note that my original request read as follows:

<quote>


> I don't understand that. I suspect we don't mean the same thing when
> we say "modeler". When I first saw your usage of this word I thought
> that it
> is the equivalent of a programmer, i.e. a user of a modeling tool. But
> then I saw the following sentence:
>

>> The modeler /must/ specify
>> the behavior unambiguously in a platform-independent manner for the
>> code generator.
>
> Since the user of a modeling tool doesn't specify anything *for* a
> code generator (he doesn't even need to be aware that such a thing
> exists), I then thought that you mean that modeler means the
> *implementer* of a modeling tool and went by that definition. A
> clarification would be appreciated.

</quote>

> Like the C++ compiler, that decision applies to all OOA models
> transformed for that environment and the transformation engine is tied
> to a particular computing environment. More important, the OOA
> modeler has no control over it. Therefore any decision made to
> resolve the
> unspecified behavior may not yield the correct results in another
> computing environment unless the OOA modeler somehow modifies the OOA
> model.

You obviously still fail to see the point I have been trying to make
over the last 3 posts. My point is the following:
1) Just about any standard in computer science (Examples: C++ standard,
UML standard, .NET CLI standard) specifies certain aspects of the
behavior as implementation-defined (example: std::type_info::name()
returns an implementation-defined string).
2) Point 1) inevitably means that implementers of such a standard (the
people who implement C++ compilers + std library, UML modeling tools,
.NET runtime environment) need to make a decision how to implement the
things specified as implementation-defined.
3) Point 2) obviously leads to different behavior among platforms
implementing the same standard. Note that despite these differences each
of those platforms is still 100% standard-conformant

I hope that we agree so far. Now it gets interesting:

4) An application crafted on top of one of the platforms is perfectly
portable, as long as it doesn't make any assumptions *beyond* of what is
guaranteed by the standard. This is true even if the application makes
use of functions whose behavior is in one way or another
implementation-defined.

The interesting question is: Do you agree with point number 4) or not?
See more later.

>>> If that choice produces results that are not consistent with the
>>> original functional requirements,
>>
>>
>> Why should it? I said more than once that the implementer chooses a
>> behavior that is *consistent* with the specification.
>
> As I have said several times, there is no such implementer when one
> uses a platform-specific code generator.

How can there be no implementer? Someone somewhere did implement that
code generator. Lets assume that code generator is the back-end of a
100% UML conformant modeling tool. And at some point the person
implementing the code generator point inevitably made a decision in what
sequence the orthogonal regions of a UML state chart are exited.

You obviously really believe that every program that uses a function the
behavior of which is in a certain aspect implementation-defined cannot
be portable. Well, I guess the only way I can convince you is with
actual code (C++):

#include <map>
#include <string>
#include <typeinfo>
#include <iostream>

struct B { virtual ~B() {} };

typedef B * CreateFunction();

typedef std::map<
std::string, CreateFunction * > CreatorMap;

class Factory
{
public:
template< class T >
static void Register()
{
creatorMap_[ typeid( T ).name() ] =
&CreateImpl< T >;
}

static B * Create( const std::string & name )
{
CreatorMap::const_iterator found =
creatorMap_.find( name );

if ( found != creatorMap_.end() )
{
return ( found->second )();
}

return 0;
}

private:
template< class T >
static B * CreateImpl() { return new T(); }

static CreatorMap creatorMap_;
};

CreatorMap Factory::creatorMap_;

struct C : public B {
C() { std::cout << "C created\n"; } };
struct D : public B {
D() { std::cout << "D created\n"; } };

int main()
{
// Register types
Factory::Register< C >();
Factory::Register< D >();

// Create objects of types, given a name
// and immediately destroy them to
// prevent leaks
delete Factory::Create( typeid( D ).name() );
delete Factory::Create( typeid( C ).name() );

return 0;
}

For brevity this doesn't do much interesting but it does the exact same
thing on at least two platforms that return different strings from
std::type_info::name() (I tested on MSVC7.1 and GCC3.4.3, I'd encourage
you to try it on any recent C++ compiler). It doesn't take too much
imagination to see that the Factory class can be used to create empty
objects from class names that have at some point been written into a
file and reread later. I've used something similar in a real-world app
to persist no longer needed objects into a *cache* file so that they can
be reconstituted later (of course, for this to work the states of the
objects were written to the file too). Now the important thing to note
here is that the cache file does not need to be portable across
platforms (the cache is trashed as soon as the program exits).

Can we agree that this program ...
1) uses std::type_info::name()?
2) is portable?
3) shows the same behavior on all standard-conformant platforms?

>>> Worse, there is no way for the modeler to
>>> fix the problem other than modifying the model for the specific
>>> environment. Then the model is not independent of the computing
>>> environment and the modeler needs to know far too much about the
>>> implementation...
>>
>>
>> I assume you mean the user of a modeling tool. No, the user doesn't
>> need to know anything about the implementation. He only needs to know
>> about the *specification* of the model feature/function.
>
> But that's the point. If the artifact specification says the behavior
> is unspecified, then the modeler doesn't know what it will be in an
> arbitrary computing environment and, consequently, can't know that
> requirements are correctly resolved.
>
>>> But to avoid those
>>> "special" situations the developer needs to know about them,
>>
>>
>> Exactly! This all boils down to the fact that the developer needs to
>> read and understand the documentation/specification. If he doesn't,
>> his program is likely broken. In the real world people don't read
>> documentation that's why I said that the example of
>> std::type_info::name() is unfortunate.
>
> No. The documentation/specification you are talking about here is for
> the specific platform/computing environment.

I don't see how you came to that conclusion. You're wrong, I was talking
about the specification of the standard, for example the C++ standard,
the UML standard, whatever. I wasn't talking about the
documentation/specification for a specific platform. For our
std::type_info::name() example I was talking about all the text you'll
find under 18.5.1 of the current C++ standard.

> That is necessary to
> predict what the actual behavior will be when the model artifact
> behavior is unspecified.

You don't need to predict that for certain uses of
std::type_info::name(), see above.

> The OOA model is, by definition, independent of particular computing
> environments.

So is a standard-conforming C++ program.

> So the model developer need not and should not know
> anything about particular implementations in particular computing
> environments. Much more important, the developer should not have to
> explicitly provide workarounds in the model itself to ensure correct
> behavior in anticipated computing environments that implement
> differently. Therefore the model artifacts must be fully specified.

No, see my statechart example.

>>> However, that isn't the real issue. Your example is at the 3GL
>>> model level.
>>
>>
>> I this context I don't see any distinction between a 3GL language
>> and an UML modeling tool. Both are "languages" (one is just
>> graphical), both have implementation-defined behavior and you can
>> write perfectly portable applications in both.

Any comment on this?

>>> At that level the 3GL programmer must anticipate the vagaries
>>> of the platform implementation by providing explicit "workaround"
>>> code.
>>
>>
>> This sounds as if you mean that this is *always* the case. Fact is:
>> It isn't. There are a lot of applications that don't need one single
>> line of platform-specific code.
>
> It is always the case if the developer wants the program to be
> portable across platforms where the implementations are different.

Wrong, see my example C++ program above.

> An OOA model must /always/ be portable to any computing environment.
> That's why OOA is different than OOD even though one applies exactly
> the same
> methodology and uses the same notation.
>
>>
>>> IOW, the 3GL programmer explicitly anticipates the possible
>>> implementations by using compiler pragmas or whatever for the
>>> /anticipated/ environments.
>>
>>
>> I suspect you haven't been writing an application in a 3GL language
>> for like 10-15 years? Things have changed considerably since then.
>
> OK, enlighten me. How would you specify the correct processing for a
> program the processes a class name extracted by std:type_info::name()
> by a program on another platform that uses different mangling?

I have said several times now that this exact usecase is not covered by
the C++ standard. The standard explicitly says that the string is
implementation-defined. Hence one cannot implement what you describe in
a portable manner. However, I have also said several times that there
are *other* uses for this function (the example program above shows such
a use-case). Programs that obey the implicit rule that a string returned
by std::type_info::name() is only meaningful on the same platform *are*
absolutely portable.

>>> Now switch to the OOA level and substitute 3GL programmer -> OOA
>>> modeler. The OOA modeler would be in exactly the same position of
>>> needing to anticipate the /possible/ implementations and providing
>>> "workaround" model elements to deal with the results of those
>>> possibilities in order to ensure the requirements were satisfied in
>>> all possible situations.
>>
>>
>> At this point I'm wondering whether you don't *want* to understand.
>
> Once again you have shut down a thread with me via ad hominem attacks.

Once again? You never complained about an attack before. See more on
this below:

> Note that it was your follow-up message that indicated that your state
> machine example was not relevant. Ta-ta.

This confirms the suspicion I have been having for about 3 followups:
Sometimes you don't read my posts carefully enough. Let me quote that
follow-up message:

<quote>


> Andreas Huber wrote:
>> One more thing: If you still insist that exit action sequence must be
>> fully specified in the presence of orthogonal regions, I'd be
>> interested in hearing your algorithm to derive the sequence from an
>> arbitrary complex *UML* statechart... :-)
>

> The above sentence of course makes little sense given that you said
> that you're not using Harel because of the implementation-defined
> behavior. Please disregard.

</quote>

I can't fathom how one can possibly misunderstand this. Note that I've
quoted only the last sentence of my original post. Also note that I'm
specifically referring to only that sentence in my follow up. No matter
how many times I read it I can't see any way how you could possibly have
reached the conclusion that this also refers to the paragraph above "One
more thing" in my original post.

Concerning your complaint: If you took that as an attack I apologize (it
was not intended as one). Note that I was careful *not* to say "You
don't want to understand". THAT would be an attack. Instead I stated a
fact, namely that I couldn't possibly see any other way how you could
have snipped and not answered that statechart paragraph of mine and then
make a statement that is obviously contrary to what I've been trying to
explain. Also note that I carefully explained why I came to that
conclusion (you've snipped that too). Finally, that alleged "attack" is
a direct result of *your* failure to read my followup with sufficient
care.

Wavemaker

unread,
Jul 14, 2005, 2:54:11 PM7/14/05
to

"H. S. Lahman" wrote:
> Responding to Wavemaker...

<snip>

>> Most of the state machines in the subsystem only subscribe to a small
>> subset of the total number of events. This leads to a problem with
>> the STTs for each state machine. They are very wasteful because of
>> their sparseness. I've been trying to concote a way make the STTs
>> smaller. Hashing the event ID would be one, but that's possibly too
>> slow.

<snip>

> Another common solution is the one you suggest about "registering" a
> <small> consecutive index to the event id for the local state machine.
> Then the event queue manager just has a single 2D table for
> event/object lookup (though it is a pretty big table). [This is often
> used in commercial code generators that do not know the maximum number
> of transitions or the number of common events a priori. (Though a
> highly optimized code generator can figure it out by looking at all
> the state machines first.)]
>
> While it is simple to execute, initializing the table and keeping it
> in synch during changes can be a problem. Of course the bitwise
> approach I suggested above has similar problems if one is not
> sufficiently prescient about event number range slush and common
> events.

First off, I appreciate your description of the various solutions to
this problem. The one above, is the one I've settled on for now. The
substitute event ID is stored along with the state machine object in the
2D table inside the event queue.

As a sanity check, I'm going to describe some of my progress writing a
state machine code generator; it's in the form of a builder class. Any
comments or criticisms are welcome.

After tweaking it over the past couple of days, I've settled on what I
hope is a useful design. The StateMachineBuilder class builds state
machines as strings representing the actual state machine code. After
being built, the string can be written to file, printed or whatever.
He's a snippet of code of the StateMachineBuilder in action:

StateMachineBuilder builder = new StateMachineBuilder();

builder.AddUsing("System");

builder.NamespaceName = "StateMachineExample";
builder.ClassName = "Keyboard";

// State Event NextState Action
builder.AddTransition(
"Default", "CapsLock", "CapsLocked", null);

builder.AddTransition(
"Default", "AnyKey", "Default", "DisplayLowercase");

builder.AddTransition(
"CapsLocked", "CapsLock", "Default", null);

builder.AddTransition(
"CapsLocked", "AnyKey", "CapsLocked", "DisplayUppercase");

builder.InitialState = "Default";

builder.Build();

Console.WriteLine(builder.StateMachine);

null means a no operation (a NoOp method is substituted by the state
machine). For invalid operations, an "Invalid" action would be
appropriate. The builder would then create an Invalid action method in
which the logic for dealing with invalid transitions could be placed.
Also, self-transitions are handled by having the next state name the
same as the current state name. Maybe there would be a better way to
indicate this such as using hyphens (e.g. "--") in the next state
category?

What I like about this is that the STT is built right into the code.
It's clearly laid out (erm, with the exception of some line wrapping I
had to put in for this post). Also, it would be easy to build a GUI
front end to make state machine generation even easier.

Here is part of the code generated by the builder; it's in the
constructor of the state machine:

eventQueue.Subscribe("CapsLock", 0, this);
eventQueue.Subscribe("AnyKey", 1, this);

The event queue is passed to the state machine's constructor. The state
machine knows which events it's interested in, so it subscribes to those
events there. The first parameter of the Subscribe method is the name of
the event. The event name does not have to preexist before being
subscribed to. If it doesn't already exist, the event queue "registers"
it and gives it a unique event ID. The second parameter is the
substitute event ID the event queue should use for this state machine
when the specified named event is raised. And the last parameter is, of
course, the state machine itself so that the event queue has an instance
to dispatch the subscribed events to.

In addition to some other boiler plate code, the builder generates empty
action methods, for example:

private void DisplayLowercase(object data)
{
// TODO: DisplayLowercase logic.
}

private void DisplayUppercase(object data)
{
// TODO: DisplayUppercase logic.
}

What's interesting to me is how this approach to state machines has
driven my design. For example, I had designed a state machine previously
representing a slave clock. This clock receives MIDI clock messages from
an external device, interprets them, and generates tick events at the
specified timing resolution. The clock periodically checks (about every
half-second or so) to see if it is still receiving clock messages. If it
has stopped receiving clock messages (say the MIDI cord to the computer
became unplugged), it would need to raise an event to let the rest of
the subsystem know and transition back to a default state.

I had put this logic in the form of a guard inside a "Running" state
handler. But this required the handler to know how to make transitions.
This is a no-no, I've since learned. :-) With this approach to state
machines, the action handlers do not have any knowledge about states or
transitions, so I was faced with a challenge when redesigning the state
machine. I solved the problem with the addition of another state. This
got rid of any need for a guard, and since I dislike them, I liked this
solution better. At any rate, I think this approach to state machines
drove me towards a better design.

H. S. Lahman

unread,
Jul 15, 2005, 2:29:10 PM7/15/05
to
Responding to Wavemaker...

> As a sanity check, I'm going to describe some of my progress writing a
> state machine code generator; it's in the form of a builder class. Any
> comments or criticisms are welcome.
>
> After tweaking it over the past couple of days, I've settled on what I
> hope is a useful design. The StateMachineBuilder class builds state
> machines as strings representing the actual state machine code. After
> being built, the string can be written to file, printed or whatever.
> He's a snippet of code of the StateMachineBuilder in action:

Let me ask a very basic question: What is the purpose of your builder?
Is it simply to generate the nuts & bolts FSM infrastructure code in an
application with FSMs for a known problem? Or is the intent to build
FSMs dynamically (e.g., the FSM itself is defined at run-time)? In the
first case the builder is just a tool for providing automation of code
writing as a convenience to the developer. The second case is more like
what a commercial transformation engine does (i.e., taking arbitrary
specifications from an external source and constructing code in a
problem-independent manner).

The reason I ask is that you mention getting input from a UI, which
suggests the FSMs are being designed "on the fly", either at run-time or
as part of a higher level (RAD-like) IDE for designing applications.

If you are just looking for an automation tool for individual
applications you might think about simply using source templates. Most
of the infrastructure is defined in lookup tables which have to be
filled in anyway and the actions are normal methods whose bodies have to
be written anyway. In addition, languages like C++ have convenient
facilities (macros, #define, etc.) for managing synchronization.
Synchronization is more manual but one can help oneself through process
(e.g., comments in the template: if THIS table is updated, check
consistency with THAT table).

What you describe below is pretty much what a translation transformation
engine does when it processes a Statechart from a UML drawing tool.
Instead of a UI, an XMI reader for the drawing tool repository is
providing all the ASCII strings you use as arguments. That's fine and I
applaud your initiative but it may be overkill unless you have such a
generic construction requirement that externalizes the FSM
specifications. OTOH, if you already have most of the code done... B-)

>
> StateMachineBuilder builder = new StateMachineBuilder();
>
> builder.AddUsing("System");
>
> builder.NamespaceName = "StateMachineExample";
> builder.ClassName = "Keyboard";
>
> // State Event NextState Action
> builder.AddTransition(
> "Default", "CapsLock", "CapsLocked", null);
>
> builder.AddTransition(
> "Default", "AnyKey", "Default", "DisplayLowercase");
>
> builder.AddTransition(
> "CapsLocked", "CapsLock", "Default", null);
>
> builder.AddTransition(
> "CapsLocked", "AnyKey", "CapsLocked", "DisplayUppercase");
>
> builder.InitialState = "Default";
>
> builder.Build();
>
> Console.WriteLine(builder.StateMachine);
>
> null means a no operation (a NoOp method is substituted by the state
> machine). For invalid operations, an "Invalid" action would be
> appropriate. The builder would then create an Invalid action method in
> which the logic for dealing with invalid transitions could be placed.
> Also, self-transitions are handled by having the next state name the
> same as the current state name. Maybe there would be a better way to
> indicate this such as using hyphens (e.g. "--") in the next state
> category?

You really don't care if an event is reflexive to the state because the
table takes care of getting the right action in either case.
Conceivably it could be a performance enhancement so that one doesn't
have to do ASCII key lookups somewhere. However, reflexive events are
rare enough that simply checking for the special case for every event
probably makes any gain moot.

However, this segues to another issue of performance. You don't want to
be doing STT lookups using ASCII keys. So State and Event want to be
converted to a nice integer index somewhere along the line. In a
translation transformation engine that would one the primary jobs of the
builder. It would also make sure the conversion table was around so
that when code was created from the text-based AAL text event generation
the integer indices would be substituted in the event ID in the produced
code.

IOW, apropos of the point above, a major reason one has a builder in a
general purpose code generator is because one /must/ convert convenient
ASCII 4GL representation into something more efficient. But for your
own development of an individual program you already know exactly what
needs to be done so you can bypass the conversion step. (More
precisely, encode it directly in tables.)


>
> What I like about this is that the STT is built right into the code.
> It's clearly laid out (erm, with the exception of some line wrapping I
> had to put in for this post). Also, it would be easy to build a GUI
> front end to make state machine generation even easier.

Right. The calls to addTransition are essentially just laying out an
STT. You will even group the calls together and comment them as if it
were an STT. It's the same table with some syntactic sugar.

As far as the GUI is concerned, you are providing an interface that
doesn't care where the ASCII comes from. IOW, the interface captures
the invariants of FSM creation and parameterizes the details in nice,
portable, source-indpendent ASCII via the STT paradigm. Note that your
builder interface could be a Facade class that serves as a subsystem
interface to the FSM construction subject matter. Then one can freely
substitute the source between GUI, browser, command line, XMI reader,
etc. That's another justification for doing things as you have and you
may have gotten to exactly the same place purely from the perspective of
application partitioning.

>
> Here is part of the code generated by the builder; it's in the
> constructor of the state machine:
>
> eventQueue.Subscribe("CapsLock", 0, this);
> eventQueue.Subscribe("AnyKey", 1, this);
>
> The event queue is passed to the state machine's constructor. The state
> machine knows which events it's interested in, so it subscribes to those
> events there. The first parameter of the Subscribe method is the name of
> the event. The event name does not have to preexist before being
> subscribed to. If it doesn't already exist, the event queue "registers"
> it and gives it a unique event ID. The second parameter is the
> substitute event ID the event queue should use for this state machine
> when the specified named event is raised. And the last parameter is, of
> course, the state machine itself so that the event queue has an instance
> to dispatch the subscribed events to.

Very good. However, I am concerned about the mechanism the event queue
uses to provide the mapping. It will have a target instance address in
hand from the event itself. Is the intention to match that with
subscribed 'this' pointers to determine the substitute event ID?

What I am getting at is that I think you need some other mechanism for
identifying the class. You are going to be accessing a static member
anyway, so all you need is the class name to generate code (e.g., <class
name>::consumeEvent(...) in C++) and you will have a target instance
handle in hand with the event to pass as an argument. If you employ a
unique integer class identifier for each class, you can register both
the same way

eventQueue.ClassSubscribe("Keyboard", 14);
...
eventQueue.Subscribe("CapsLock", 0, "Keyboard");
...

(Though in a full code generator one would probably keep the class
mapping table somewhere else that the FSM builder accesses because it
would be useful in other contexts.)

Now the event has '14' as a target class ID and some number for
"Capslock" to use to look up the '0' for the local event ID. At the
same time it uses '14' to look up the class name to insert in the code
for the call.

>
> In addition to some other boiler plate code, the builder generates empty
> action methods, for example:
>
> private void DisplayLowercase(object data)
> {
> // TODO: DisplayLowercase logic.
> }
>
> private void DisplayUppercase(object data)
> {
> // TODO: DisplayUppercase logic.
> }
>
> What's interesting to me is how this approach to state machines has
> driven my design. For example, I had designed a state machine previously
> representing a slave clock. This clock receives MIDI clock messages from
> an external device, interprets them, and generates tick events at the
> specified timing resolution. The clock periodically checks (about every
> half-second or so) to see if it is still receiving clock messages. If it
> has stopped receiving clock messages (say the MIDI cord to the computer
> became unplugged), it would need to raise an event to let the rest of
> the subsystem know and transition back to a default state.
>
> I had put this logic in the form of a guard inside a "Running" state
> handler. But this required the handler to know how to make transitions.
> This is a no-no, I've since learned. :-) With this approach to state
> machines, the action handlers do not have any knowledge about states or
> transitions, so I was faced with a challenge when redesigning the state
> machine. I solved the problem with the addition of another state. This
> got rid of any need for a guard, and since I dislike them, I liked this
> solution better. At any rate, I think this approach to state machines
> drove me towards a better design.

I think that is mostly attributable to the fact that the FSM forces one
to follow FSA rules. (I would argue OOA/D requires one to follow the
same rules but in a more subtle way so deviation and foot-shooting is
easier.) It is nontrivial to build good state machines and get them to
interact properly. But the effort is worth it because once one gets
them right they tend to be very robust.

Wavemaker

unread,
Jul 15, 2005, 6:00:21 PM7/15/05
to

"H. S. Lahman" wrote:
> Responding to Wavemaker...

<snip>

I think I need to qualify my immediate responses below because it's
become apparent to me that I may not entirely understand the usual
approach to using event queues with state machines, especially in
regards to code generators. You may want to skip down to the last third
of this post first.

>> After tweaking it over the past couple of days, I've settled on what
>> I hope is a useful design. The StateMachineBuilder class builds state
>> machines as strings representing the actual state machine code. After
>> being built, the string can be written to file, printed or whatever.
>> He's a snippet of code of the StateMachineBuilder in action:
>
> Let me ask a very basic question: What is the purpose of your builder?
> Is it simply to generate the nuts & bolts FSM infrastructure code in
> an application with FSMs for a known problem? Or is the intent to
> build FSMs dynamically (e.g., the FSM itself is defined at run-time)?
> In the first case the builder is just a tool for providing automation
> of code writing as a convenience to the developer. The second case is
> more like what a commercial transformation engine does (i.e., taking
> arbitrary specifications from an external source and constructing code
> in a problem-independent manner).

The first case.

> The reason I ask is that you mention getting input from a UI, which
> suggests the FSMs are being designed "on the fly", either at run-time
> or as part of a higher level (RAD-like) IDE for designing
> applications.

I wrote an UI for it last night. I now have a relatively nice Windows
application front end for my builder class. The UI has a Windows table
control for entering the STT. Also, there are textboxes for entering
other values for the state machine, plus a "Build" button for generating
the code. :-)

This has been fun to play with. I look at my notes about a state machine
I'm designing, enter the values into the STT, browse to the location I'd
like the code generated, and hit Build. I can do this with my
development environment application open, so I can almost immediately
see the resulting code and compile it.

I think my use falls into the first case you've described. C# is a
compiled language, so I'm not sure how it would use FSMs defined at
run-time. Conceptually, since my builder class generates a simple string
representation of the state machine code, I could see how an
interpretted environment could read that string and run it. But when the
state machine is initially created, its action methods are empty, so
there is no behavior yet associated with it. But perhaps that could be
done somehow dynamically as well? This all assumes I understand what you
mean by creating FSMs dynamically, which I easily may not.

> If you are just looking for an automation tool for individual
> applications you might think about simply using source templates.
> Most of the infrastructure is defined in lookup tables which have to
> be filled in anyway and the actions are normal methods whose bodies
> have to be written anyway. In addition, languages like C++ have
> convenient facilities (macros, #define, etc.) for managing
> synchronization. Synchronization is more manual but one can help
> oneself through process (e.g., comments in the template: if THIS table
> is updated, check consistency with THAT table).

Hmm, can a source template initialize a state transition table? I
honestly don't know. After doing it a few times by hand, I'm already
appreciating how nice it is to have it automated.

> What you describe below is pretty much what a translation
> transformation engine does when it processes a Statechart from a UML
> drawing tool. Instead of a UI, an XMI reader for the drawing tool
> repository is providing all the ASCII strings you use as arguments.

That's very interesting. I was curious about that.

> That's fine and I applaud your initiative but it may be overkill
> unless you have such a generic construction requirement that
> externalizes the FSM specifications. OTOH, if you already have most
> of the code done... B-)

With the caveat that I may not have done it right (see below), :-) this
has been a fun, and for the most part, straightforward exercise. I've
enjoyed it, and anticipate using this tool quite a bit in the future.

<snip>

> However, this segues to another issue of performance. You don't want
> to be doing STT lookups using ASCII keys. So State and Event want to
> be converted to a nice integer index somewhere along the line.

Absolutely. My approach is pretty simple. When an event is registered,
the event queue checks to see if it has already been registered. If not,
it adds the event name to its collection of events. The event ID is an
integer index into the collection in which the event name resides.

If a state machine needs to know an event ID so that it can raise the
event, it can ask the event queue:

int someEventID = eventQueue.GetEventID("SomeEvent");

The event ID will not change for the lifetime of the application, so the
ID can be retreived early on. The lookup costs is only incurred once
(but this could possibly be automated as well, I haven't gotten that
far yet).

There is a potential problem with this approach. If a state machine is
interested in the event ID of an event that has not yet been registered,
it will get back an invalid value (-1). The event queue has a "Register"
method that allows an event to be preregistered. So an application can
preregister all events before hand to get around this
(another area of possible automation).

I guess what I'm trying to do is treat event IDs the same way you would
pointers. You don't care what their values are, but you do want them to
behave correctly each time you use them. And you want them initialized
properly as well.

<snip>

>> Here is part of the code generated by the builder; it's in the
>> constructor of the state machine:
>>
>> eventQueue.Subscribe("CapsLock", 0, this);
>> eventQueue.Subscribe("AnyKey", 1, this);
>>
>> The event queue is passed to the state machine's constructor. The
>> state machine knows which events it's interested in, so it subscribes
>> to those events there. The first parameter of the Subscribe method is
>> the name of the event. The event name does not have to preexist
>> before being subscribed to. If it doesn't already exist, the event
>> queue "registers" it and gives it a unique event ID. The second
>> parameter is the substitute event ID the event queue should use for
>> this state machine when the specified named event is raised. And the
>> last parameter is, of course, the state machine itself so that the
>> event queue has an instance to dispatch the subscribed events to.
>
> Very good. However, I am concerned about the mechanism the event
> queue uses to provide the mapping. It will have a target instance
> address in hand from the event itself. Is the intention to match that
> with subscribed 'this' pointers to determine the substitute event ID?

Not exactly. In reading your description below, I think I have
misunderstood the usual architecture of the event queue. In fact, I
think I'm a little confused at this point, especially when you say that
the target instance will come from the event itself.

Let me describe my current design:

I have an abstract class called StateMachine. All state machines derive
from this class. Derived classes instantiate and initialize two static
tables (that belong to the subclasses), one for transitions and one for
the action methods. These two tables are passed to the StateMachine base
class when an instance of the derived class is created.

The StateMachine class has a dispatch method that looks like this:

public void Dispatch(int eventID, object data)
{
actionHandlerTable[state, eventID](this, data);
state = nextStateTable[state, eventID];
}

So the StateMachine object has the responsibility of passing itself to
the action methods. That's where the static action methods get the
target instance (this requires a downcast in the action methods, but I
think this may be one of those situations in which that isn't a
problem).

Inside the event queue is a 2D array used for the subscribers. When a
state machine subscribes to an event, a "Subscriber" object is created
with the substitute eventID as well as the instance of the state
machine. The subscriber is added to the list of subscribers at the event
index. So the 2D array is really an array of subscriber lists.

When an event is raised, the event ID and data are passed to the event
queue:

public void Publish(int eventID, object data)
{
// ...
}

The eventID and data are enqueued into the event queue. When they are
dequeued later, the list of subscriber objects at the specified event
index is iterated over. The Dispatch method of each state machine is
called and passed the substitute event ID (both easily retrieved from
the subscriber object) and the data for the event. So the event queue
leaves it up to each state machine object to pass itself to the action
methods inside the Dispatch method.

This approach allows multiple state machines to subscribe to the same
event. In my very limited experience with this paradigm, I've found that
usually events only go to one state machine, but there are situations in
which multiple state machines need to be notified of an event.

However, I've got a sneaky suspicion that I may have misunderstood
something fundamental about this paradigm. When an event is raised, must
it be targeted to one and only one state machine? And when an event is
raised, must the object responsible for the event possess the target
instance so it can include it with the event?

> What I am getting at is that I think you need some other mechanism for
> identifying the class. You are going to be accessing a static member
> anyway, so all you need is the class name to generate code (e.g.,
> <class name>::consumeEvent(...) in C++) and you will have a target
> instance handle in hand with the event to pass as an argument. If you
> employ a unique integer class identifier for each class, you can
> register both the same way
>
> eventQueue.ClassSubscribe("Keyboard", 14);
> ...
> eventQueue.Subscribe("CapsLock", 0, "Keyboard");
> ...
>
> (Though in a full code generator one would probably keep the class
> mapping table somewhere else that the FSM builder accesses because it
> would be useful in other contexts.)
>
> Now the event has '14' as a target class ID and some number for
> "Capslock" to use to look up the '0' for the local event ID. At the
> same time it uses '14' to look up the class name to insert in the code
> for the call.

I'm not sure where the class name code would be generated. Inside the
event queue? The way I have the event queue currently designed, it only
has to know about the StateMachine class. The code generator doesn't
event touch the event queue, in fact. It's totally generic. So I think I
must be misunderstanding what you're saying. I'm also confused about
where the "target instance handler" comes from, if not when the state
machine subscribes to an event.


Wavemaker

unread,
Jul 16, 2005, 12:06:53 AM7/16/05
to
Sorry to reply twice to the same post, but I think I'm starting to get
it.

After studying your post and googling some past posts, I think I
understand what you're describing with regards to how the event queue
and state machines are structured.

The Dispatch method is static; it exists at the class level. After IDing
the class, the event queue calls this methed when it pops an event. The
event argument has a reference to the target instance. It passes the
event argument to the Dispatch method. The Dispatch method does whatever
casting it needs to do on the object reference and does the table look
ups for the action methods and the state transitions.

In the approach I described in my previous post, the event queue is
reponsible for keeping track of which state machines are interested in
which events. Instead, a state machine interested in a certain event
should register (or somehow be associated) with the state machine that's
responsible for raising that event. When a state machine raises the
event, it packs the data for that event along with the interested state
machine instance into the event argument and sends them to the event
queue where it does its thing.

Am I right in thinking that if an event has multiple targets, its the
responsibility of the state machine raising the event to fire off that
event for each target in turn? IOW, multiple state machines may have
registered with the state machine for the same event; there is a list of
targets interested in the event. When the event is raised, the process
of packing an argument and sending it to the event queue is done for
each target. Is this correct?

BTW, you're previous post on the event ID problem is making a little
more sense to me now. I was trying to picture the solutions with my
current approach, and they weren't exactly fitting. :-)

Seems to me that this approach wouldn't be much harder to implement than
my present approach. I just need to factor out the functionality for
subscribing to events to the state machines themselves.

<snip>

> What I am getting at is that I think you need some other mechanism for
> identifying the class. You are going to be accessing a static member
> anyway, so all you need is the class name to generate code (e.g.,
> <class name>::consumeEvent(...) in C++) and you will have a target
> instance handle in hand with the event to pass as an argument. If you
> employ a unique integer class identifier for each class, you can
> register both the same way
>
> eventQueue.ClassSubscribe("Keyboard", 14);
> ...
> eventQueue.Subscribe("CapsLock", 0, "Keyboard");
> ...
>
> (Though in a full code generator one would probably keep the class
> mapping table somewhere else that the FSM builder accesses because it
> would be useful in other contexts.)
>
> Now the event has '14' as a target class ID and some number for
> "Capslock" to use to look up the '0' for the local event ID. At the
> same time it uses '14' to look up the class name to insert in the code
> for the call.

I'm still a little fuzzy on this, but I think I need to check my
understanding and make sure I'm on the right page before asking any more
questions.


H. S. Lahman

unread,
Jul 16, 2005, 12:43:23 PM7/16/05
to
Responding to Wavemaker...

>>The reason I ask is that you mention getting input from a UI, which
>>suggests the FSMs are being designed "on the fly", either at run-time
>>or as part of a higher level (RAD-like) IDE for designing
>>applications.
>
>
> I wrote an UI for it last night. I now have a relatively nice Windows
> application front end for my builder class. The UI has a Windows table
> control for entering the STT. Also, there are textboxes for entering
> other values for the state machine, plus a "Build" button for generating
> the code. :-)
>
> This has been fun to play with. I look at my notes about a state machine
> I'm designing, enter the values into the STT, browse to the location I'd
> like the code generated, and hit Build. I can do this with my
> development environment application open, so I can almost immediately
> see the resulting code and compile it.
>
> I think my use falls into the first case you've described. C# is a
> compiled language, so I'm not sure how it would use FSMs defined at
> run-time. Conceptually, since my builder class generates a simple string
> representation of the state machine code, I could see how an
> interpretted environment could read that string and run it. But when the
> state machine is initially created, its action methods are empty, so
> there is no behavior yet associated with it. But perhaps that could be
> done somehow dynamically as well? This all assumes I understand what you
> mean by creating FSMs dynamically, which I easily may not.

That's fine. It's just a lot of work. (That's what I meant by
overkill.) Of course, if one has an Inquiring Mind, one's perspective
changes. B-)

>>If you are just looking for an automation tool for individual
>>applications you might think about simply using source templates.
>>Most of the infrastructure is defined in lookup tables which have to
>>be filled in anyway and the actions are normal methods whose bodies
>>have to be written anyway. In addition, languages like C++ have
>>convenient facilities (macros, #define, etc.) for managing
>>synchronization. Synchronization is more manual but one can help
>>oneself through process (e.g., comments in the template: if THIS table
>>is updated, check consistency with THAT table).
>
>
> Hmm, can a source template initialize a state transition table? I
> honestly don't know. After doing it a few times by hand, I'm already
> appreciating how nice it is to have it automated.

No, I was thinking of a source template that is like a body stub. IOW,
it /is/ the state machine infrastructure. You just fill in the table
values directly at placeholders rather than through your UI.

>>What you describe below is pretty much what a translation
>>transformation engine does when it processes a Statechart from a UML
>>drawing tool. Instead of a UI, an XMI reader for the drawing tool
>>repository is providing all the ASCII strings you use as arguments.
>
>
> That's very interesting. I was curious about that.
>
>
>>That's fine and I applaud your initiative but it may be overkill
>>unless you have such a generic construction requirement that
>>externalizes the FSM specifications. OTOH, if you already have most
>>of the code done... B-)
>
>
> With the caveat that I may not have done it right (see below), :-) this
> has been a fun, and for the most part, straightforward exercise. I've
> enjoyed it, and anticipate using this tool quite a bit in the future.

With the one reservation I had, it sounds like you are doing it right so
far. But later there is a disconnect.

> <snip>
>
>>However, this segues to another issue of performance. You don't want
>>to be doing STT lookups using ASCII keys. So State and Event want to
>>be converted to a nice integer index somewhere along the line.
>
>
> Absolutely. My approach is pretty simple. When an event is registered,
> the event queue checks to see if it has already been registered. If not,
> it adds the event name to its collection of events. The event ID is an
> integer index into the collection in which the event name resides.

Right. That's what an optimizing code generator would do. Since
lookups from the ASCII side are only needed for the code generation
inputs, the code can be fully optimized.

>
> If a state machine needs to know an event ID so that it can raise the
> event, it can ask the event queue:
>
> int someEventID = eventQueue.GetEventID("SomeEvent");
>
> The event ID will not change for the lifetime of the application, so the
> ID can be retreived early on. The lookup costs is only incurred once
> (but this could possibly be automated as well, I haven't gotten that
> far yet).
>
> There is a potential problem with this approach. If a state machine is
> interested in the event ID of an event that has not yet been registered,
> it will get back an invalid value (-1). The event queue has a "Register"
> method that allows an event to be preregistered. So an application can
> preregister all events before hand to get around this
> (another area of possible automation).

There is another problem during maintenance if one changes the name of
the event. Hardly insurmountable, but one of those gotchas that tend to
haunt home-grown generic code generators over time. B-)

>
> I guess what I'm trying to do is treat event IDs the same way you would
> pointers. You don't care what their values are, but you do want them to
> behave correctly each time you use them. And you want them initialized
> properly as well.

Right. I would express it a bit differently, though. You are providing
an alternative identity for optimization and that has to
managed/synchronized with the original identity.

>
> <snip>
>
>>>Here is part of the code generated by the builder; it's in the
>>>constructor of the state machine:
>>>
>>>eventQueue.Subscribe("CapsLock", 0, this);
>>>eventQueue.Subscribe("AnyKey", 1, this);
>>>
>>>The event queue is passed to the state machine's constructor. The
>>>state machine knows which events it's interested in, so it subscribes
>>>to those events there. The first parameter of the Subscribe method is
>>>the name of the event. The event name does not have to preexist
>>>before being subscribed to. If it doesn't already exist, the event
>>>queue "registers" it and gives it a unique event ID. The second
>>>parameter is the substitute event ID the event queue should use for
>>>this state machine when the specified named event is raised. And the
>>>last parameter is, of course, the state machine itself so that the
>>>event queue has an instance to dispatch the subscribed events to.
>>
>>Very good. However, I am concerned about the mechanism the event
>>queue uses to provide the mapping. It will have a target instance
>>address in hand from the event itself. Is the intention to match that
>>with subscribed 'this' pointers to determine the substitute event ID?
>
>
> Not exactly. In reading your description below, I think I have
> misunderstood the usual architecture of the event queue. In fact, I
> think I'm a little confused at this point, especially when you say that
> the target instance will come from the event itself.

Generally one decides what instance will process the event where the
event is generated. For example, in AAL one might have:

ref = this -> R12
GENERATE e1 TO ref WITH <data value list>

The first line navigates the R12 relationship to whatever is on the
other end (assuming a 1 multiplicity in this case). The second line
generates an event, e1, to that instance, ref, with some attribute
values for the data packet. So generally the event placed on the event
queue will have the form {event ID, target ID, data packet}. Note that
the event is issued on a peer-to-peer basis; hold that thought for a
moment because it is the source of a disconnect.

Usually the target ID will be an instance address. That's fine for a
code generator that will use an Object* pointer and all objects with
state machines will have an Object.consumeEvent method. But it is
pretty ugly code for manual maintenance. That's why I proposed a
somewhat more maintainable version...

Bingo! Your solution allows multiple state machines to receive the
event all right -- but only on a broadcast basis. It is effectively an
Observer pattern.

Remember that events just implement messaging for object collaborations.
The problem is that most events are generated for a specific
peer-to-peer (1 on 1) collaboration. Assume R12 is a * relationship in
my AAL example above. If one wanted to broadcast the event one would
write something like

refSet = this -> R12
FOREACH ref IN refSet
GENERATE e1 TO ref WITH <data>

This is more verbose but that's because it is more uncommon. In reality
it is much more likely one just wants to send the event to a single
instance in the set that meets some context requirement:

ref = this -> R12 WHERE attribute test condition>
GENERATE e1 TO ref WITH <data>

<aside>
Actually, one normally doesn't want to use WHERE clauses because of the
selection search if it can be avoided. So the selection from the set of
possible participants will often be made elsewhere once based on
external context and the method initiating the collaboration will
already have a specific instance reference in hand. As a result it is
fairly common to have something like:

1 R1 *
[ObjectA] -------------------- [ObjectB]
| 1 | 1
| R2 |
+------------------------------+

where R1 represents the notion of the "current" member of the R1 set
being processed. For example, in a networking context we might model a
Channel being opened to a particular network Port among several and the
connection remains in place for several messages defined by some
orthogonal context like placing an order from a shopping cart. R1 then
represents the available candidates for resource allocation while R2
represents the actual connection.
</aside>

There is also the problem that the e1 event might be relevant to
different objects with different state machines. In that case it would
be highly unusual to broadcast the event to all the different state
machines because the contexts of collaboration would likely be very
different (i.e., it is not as simple 1:* navigation).

So you implementation must be able deal with individual events sent to
particular objects. To make the event queue generic it pops exactly one
event to one instance at a time. (This is also necessary to manage
concurrency scope.) So if one really wants to broadcast, the events
need to be pushed one at a time on the queue. So....

Actually it won't take much to fix your implementation. You need to put
the STT that's in actionHandlerTable[state, eventID](this, data) in the
class as a static table. Then the event queue always invokes
<class>::consumeEvent (instance, local event ID, data packet).
consumeEvent does the action STT lookup using the local event ID. The
instance is the instance reference from the event itself, as I described
above. Then all you need is a lookup for the local event ID from the
global event ID. So you code in the event queue would look something like

EventQueue::pop()
int localEventID;
Event* event;

event = // extract the current event

localEventID = EventConversionTable [event->class, event->eventID];
dispatchTable[event->class](event->instance, localEventID,
event->data);

// housekeeping for queue

Here I have introduce the notion of a class identifier that is part of
the event itself: {event ID, instance ID, class ID, data packet}. The
dispatchTable is initialized with static references: &<class
name>::consumeEvent. That's where you code generator comes in. When it
writes the EventQueue code, it "hard-wires" the ASCII class name at the
index assignment defined for each EventQueue::classSubscribe invocation
that I proposed in the last message.

Note that by moving the actual FSM STT to the class, the EventQueue
doesn't need to know anything about states. In fact, there is no state
machine entity at all. One just has the EventQueue and the static class
level consumeEvent method with the STT as a static class table. (One
also needs and instance "current state" attribute and that can only be
manipulated for the transition by a class or instance method.) This is
lot simpler because one does not need a state machine instance for each
object to deal with the fact that different objects can be in different
states of their common state machine.

When the code generator writes the event generation code, it needs
access to that same table to insert the right class index into the
event. (It knows what's on the end of the relationship from the Class
Model, so it knows the name when it processes the reference.)

H. S. Lahman

unread,
Jul 16, 2005, 12:48:08 PM7/16/05
to
Responding to Wavemaker...

> The Dispatch method is static; it exists at the class level. After IDing
> the class, the event queue calls this methed when it pops an event. The
> event argument has a reference to the target instance. It passes the
> event argument to the Dispatch method. The Dispatch method does whatever
> casting it needs to do on the object reference and does the table look
> ups for the action methods and the state transitions.
>
> In the approach I described in my previous post, the event queue is
> reponsible for keeping track of which state machines are interested in
> which events. Instead, a state machine interested in a certain event
> should register (or somehow be associated) with the state machine that's
> responsible for raising that event. When a state machine raises the
> event, it packs the data for that event along with the interested state
> machine instance into the event argument and sends them to the event
> queue where it does its thing.

By George, I think he's got it!

> Am I right in thinking that if an event has multiple targets, its the
> responsibility of the state machine raising the event to fire off that
> event for each target in turn? IOW, multiple state machines may have
> registered with the state machine for the same event; there is a list of
> targets interested in the event. When the event is raised, the process
> of packing an argument and sending it to the event queue is done for
> each target. Is this correct?

Exactly. Too bad I didn't read this first; I could have saved a lot of
bandwidth on my response to the other message. B-)

Wavemaker

unread,
Jul 16, 2005, 3:51:07 PM7/16/05
to

"H. S. Lahman" wrote:
> Responding to Wavemaker...

<snip>

>> Not exactly. In reading your description below, I think I have


>> misunderstood the usual architecture of the event queue. In fact, I
>> think I'm a little confused at this point, especially when you say
>> that the target instance will come from the event itself.
>
> Generally one decides what instance will process the event where the
> event is generated. For example, in AAL one might have:
>
> ref = this -> R12
> GENERATE e1 TO ref WITH <data value list>
>
> The first line navigates the R12 relationship to whatever is on the
> other end (assuming a 1 multiplicity in this case). The second line
> generates an event, e1, to that instance, ref, with some attribute
> values for the data packet. So generally the event placed on the
> event queue will have the form {event ID, target ID, data packet}.
> Note that the event is issued on a peer-to-peer basis; hold that
> thought for a moment because it is the source of a disconnect.

<snip>

>> However, I've got a sneaky suspicion that I may have misunderstood
>> something fundamental about this paradigm. When an event is raised,
>> must it be targeted to one and only one state machine? And when an
>> event is raised, must the object responsible for the event possess
>> the target instance so it can include it with the event?
>
> Bingo! Your solution allows multiple state machines to receive the
> event all right -- but only on a broadcast basis. It is effectively
> an Observer pattern.
>
> Remember that events just implement messaging for object
> collaborations. The problem is that most events are generated for a
> specific peer-to-peer (1 on 1) collaboration. Assume R12 is a *
> relationship in my AAL example above. If one wanted to broadcast the
> event one would write something like
>
> refSet = this -> R12
> FOREACH ref IN refSet
> GENERATE e1 TO ref WITH <data>
>
> This is more verbose but that's because it is more uncommon. In
> reality it is much more likely one just wants to send the event to a
> single instance in the set that meets some context requirement:
>
> ref = this -> R12 WHERE attribute test condition>
> GENERATE e1 TO ref WITH <data>

Aha! Ok, now I understand. Up until this point, it's seemed arbitrary to
me where the notification resides, whether in the state machines or the
event queue. This makes things much clearer. And right off the bat I can
see how this would be useful and solve a problem I was having with
"broadcasting" events in my toolkit.

> <aside>
> Actually, one normally doesn't want to use WHERE clauses because of
> the selection search if it can be avoided. So the selection from the
> set of possible participants will often be made elsewhere once based
> on external context and the method initiating the collaboration will
> already have a specific instance reference in hand. As a result it is
> fairly common to have something like:
>
> 1 R1 *
> [ObjectA] -------------------- [ObjectB]
> | 1 | 1
> | R2 |
> +------------------------------+
>
> where R1 represents the notion of the "current" member of the R1 set
> being processed. For example, in a networking context we might model
> a Channel being opened to a particular network Port among several and
> the connection remains in place for several messages defined by some
> orthogonal context like placing an order from a shopping cart. R1
> then represents the available candidates for resource allocation while
> R2 represents the actual connection.
> </aside>

This makes sense. Is one of the downsides to broadcasting events that
you wind up adding states to state machines simply to ignore events when
they are not interested in them? This was a problem I was having. If on
the other hand, I focus the events to a specific target at the times in
which it is appropriate for the target to receive the event, the problem
goes away.

I'm a little fuzzy on how the receiver of the event is chosen. Could it
depend on the state of the event raiser? The event is the same, but if
the source of the event is in one state, it targets a particular
instance, if it is in another state, it targets another instance.

I also suppose the target could be determined externally, which I think
is what you say above. Perhaps one state machine could raise an event
anouncing to another state machine which instance is the appropriate
target. In this case, the target could be part of the event argument
(though I don't know if there would be a problem in passing the target
around that way. If the event raiser already has the target, the
anouncement would just let it know which is the current target).

Erm, I'm probably all over the place on this, but that's usually how I
am when I learn something knew and try to understand its implications
and implementations.

I will study the rest of your post on adapting my implementation. It's
made things clearer.

Wavemaker

unread,
Jul 16, 2005, 3:49:15 PM7/16/05
to

"H. S. Lahman" wrote:
> Responding to Wavemaker...

<snip>

>> Am I right in thinking that if an event has multiple targets, its the


>> responsibility of the state machine raising the event to fire off
>> that event for each target in turn? IOW, multiple state machines may
>> have registered with the state machine for the same event; there is a
>> list of targets interested in the event. When the event is raised,
>> the process of packing an argument and sending it to the event queue
>> is done for each target. Is this correct?
>
> Exactly. Too bad I didn't read this first; I could have saved a lot
> of bandwidth on my response to the other message. B-)

Oops! Well, I can tell you that it definitely wasn't wasted. :-) It gave
me a deeper understanding of the "whys" in addition to the "hows." It's
very much appreciated. Thanks for answering my questions. I think I've
made a lot of progress here. I've gone from a kind of nebulous
impression of what event queues + state machines meant, to something
much more concrete. As always, it's been enlightening.


H. S. Lahman

unread,
Jul 17, 2005, 1:01:04 PM7/17/05
to
Responding to Wavemaker...

In theory we could assume a broadcast message was generated from every
method to announce its completion. Then (in OOA/D) we could go to the
level of a UML Interaction Diagram and connect the dots for the overall
solution by deciding where those broadcast events should go. IOW, one
uses the Interaction Diagram as a sort of Observer registration process
for collaboration; everyone other than the designated recipients would
just ignore the broadcast.

Similarly in theory the OOPLs could have a mechanism where flow of
control for collaboration was defined entirely outside the definitions
of individual classes and object state machines. However, that is much
tougher to do efficiently when one is dealing with 3GL type systems and
procedural message passing (e.g., ignoring an event requires a test
statement to be executed).

So...

>
> I'm a little fuzzy on how the receiver of the event is chosen. Could it
> depend on the state of the event raiser? The event is the same, but if
> the source of the event is in one state, it targets a particular
> instance, if it is in another state, it targets another instance.

One way to define collaboration in the above scenario is by applying DbC
in the OOA/D. There is some precondition in the overall solution that
must prevail before a given method can be executed. That precondition
is compound in both the place in the solution algorithm sequence (i.e.,
what other methods must be executed before or after it) and in the
"state" of the solution as expressed by state variable values.

One can use DbC to determine where the message should be generated by
observing the postconditions of other methods. Wherever the post
condition of executing another method includes a match to the
precondition, one should generate the message there. Since OO
collaboration is peer-to-peer, we address the message directly from
where the precondition first prevails to where the precondition is required.

So, in theory, we could design all our objects, state machines, and
methods without any message generation at all. We could then solve the
overall solution flow of control problem by applying DbC as above and
"backfilling" the message generation by inserting message generation and
addressing in the methods. Which is exactly what a UML Interaction
Diagram does at a high level of abstraction.

As a practical matter no experienced developer actually does this
because it tends to be pretty tedious and not very intuitive. In
addition, just to design the objects, state machines, and methods one
needs to have one eye on the overall problem. That's because we only
abstract what we need to solve the problem in hand and we have to tailor
the abstractions to the problem in hand. So most OOA/D methodologies
provide alternatives to achieve the same DbC end but from the other
direction.

So Jacobson uses Use Cases to incrementally develop around requirements
sequence. Wirfs-Brock uses a notion of roles and requests. In effect
such approaches introduce an abstract client/service view where one
abstracts an entity as a client and then immediately looks for the
services that it needs and defines them. Those services then become
clients for other services in a daisy chain of object abstractions. But
all such methodologies still start with the static entity abstractions
in the Class Model as a framework and those are elaborated class ->
responsibilities -> behavior definitions as a base as one iteratively
develops the full solution. Nonetheless, behind the methodological
abstraction DbC is alive and well as it underlies those daisy-chains.

When one uses state machines the notation and constraints are so formal
that DbC tends to be more natural. So R-T/E people have used it as a
tool for validating interactions between state machines since long
before OO. It is a valuable tool, though, for resolving complex data
integrity issues at the Interaction Diagram level whether one uses
object state machines or not. So it should be a part of the developer's
standard tool kit.

Before getting to your specific questions I need to digress a bit about
how the OO paradigm manages state variables. Any public attributes can
be accessed from any other class so long as there is a relationship path
between them in the Class Model. Since virtually all Class Models are
fully connected, it is almost always true one can reach any class'
public attributes from any other class. IOW, superficially all public
attribute data would be global.

However, that is a very superficial view because of participation
constraints in relationships. That is, not all instances of a given
class are connected to the same instances of another class via a
relationship in the Class Model. IOW, individual participation in a
relationship is actually quite limited at the instance level. Thus
relationship instantiation and navigation for collaboration is the
cornerstone of the OO paradigm's management of state variables. For
most data it is a case of you-can't-get-there-from-here. (There are
exceptions, but one has to go well out of one's way to implement them.)

This is why a large fraction of the GoF patterns are about factories.
We want to encapsulate the rules and policies of relationship
instantiation on one place because they are so important. (Another
reason is efficiency; we tend to navigate the same relationships many
times during multiple collaborations and we don't want to worry about
(write code for) making sure we've got the right objects talking to one
another for each collaboration.) One can also argue that all of the GoF
patterns are about managing relationships whose participation is
dynamically complex, which indicates the importance of relationship in
the OO paradigm.

So, getting almost back to your questions, when we send a message there
are three things we need to resolve. One is unique identity, whether it
is an event or an OOPL method signature. That's because we need to
provide unambiguous mapping between what the sender did and what
particular response is appropriate. Second is an (optional) data
packet. Third is an address; we have to know who cares about what just
happened.

Addressing is awkward because it potentially breaks encapsulation. The
sender can't resolve the address of a specific receiver without knowing
something about context. This is especially true in the OOPLs where
there is no separation of message and method and the message identity is
the method identity. Since we name methods by what they do, it is
pretty hard not to have some expectation about what happens when we
generate a message. When the sender implementation depends upon that
expectation, we are in deep ka-ka.

In the OOA/D, though, we have a lot of tools that provide a high degree
of decoupling. Using an asynchronous behavior communication model and
state machines separates message and method. In addition, UML provides
additional mechanisms even without state machines. But we still have
the problem of addressing the message. In OOA/D there are <at least>
two main ways of abstracting that. One is the one I've already
mentioned, raising the level of abstraction to the level of an
Interaction Diagram rather than thinking about connecting the solution
dots when writing individual methods.

Another is abstract action language. Recall that in my example of AAL
the addressing was completely devoid of any semantics of particular
classes unless there was a WHERE clause for subset selection. Even then
the WHERE only depends upon knowledge properties, not behaviors. (The
OO paradigm gives equal stature to knowledge and behavior
responsibilities, but in OOA/D they are handled quite differently.)
And, as I noted, one tries to avoid WHERE clauses anyway. So AAL
abstracts addressing messages into pure relationship navigation.

More important, the participation in the relationship is not defined
during the collaboration navigation. One addresses the message with
complete faith that the right entity is already on the other end of the
line. This is a very important separation of concerns that underlies
the notion of a special "current" relationship in my example. If I want
a Channel to talk to the same Port throughout a series of packet
transmissions, then I make sure that participation is instantiated
/before/ undertaking any of the packet transmissions.

This is important because it removes any responsibility from Channel for
knowing about how Ports are selected, allocated, etc.. In fact,
conceptually it doesn't even need to know a Port is on the other end of
the line. Divesting Channel of such responsibilities makes life much
simpler for Channel and that sort of modularization makes life easier
for developers and maintainers.

OK, to your specific questions... Solution flow of control is defined
by daisy-chained collaborations between objects. Collaborations are
defined in terms of sending messages on a peer-to-peer basis between
objects. The right message must be sent from the right sender at the
right time to the right receiver. The first three are all handled by
the notion of announcements. An object (right sender) that does
something specific (right message) announces it has done it (right
time). So far, so good.

The tricky part lies in getting it to the right receiver. The DbC
approach I mentioned above is a rigorous, albeit tedious, approach to
matching senders and receivers that can be done at the UML Interaction
Diagram level. However, you still need to select which /specific/
receiver cares about the announcement, not just the class of receivers.

That receiver is largely constrained by relationship participation. If
the relationship is 1:1 it comes "for free" when the relationship is
instantiated. That's why the notion of a "current" Channel/Port
connection is useful when there are multiple candidates and a broad,
well-defined collaboration context. If the relationship is 1:*, then a
selection may have to be made. However, that selection is based purely
on knowledge state and it is easily mechanized in collection classes.

[This is easily extended to navigating relationship paths involving
multiple classes in a Class Model. So in AAL one can have

ref = this -> R12 -> R92 -> R5 WHERE ...

This allows relationship navigation to be implemented independently of
the class semantics along the path. In fact, full code generators for
OOA model typically treat relationships as cross-cutting aspects and
produce quite generic (although not always very readable) code.]

So the basic answer lies in relationship definitions. Those are driven
by problem space logical connections. The OO paradigm simply uses them
to greatly restrict instance accessibility. To that extent, one gets
the right receiver largely "for free" when one defines (perhaps
mentally) a Class Model and then implements and instantiates the
relationships. Any selection is reduced to state variable conditions
that are orthogonal to the behavior flow of control.

Another piece of the answer lies in context. One instantiates
relationships based upon specific problem space rules and policies that
effectively say, "THIS entity only talks to THAT entity under THESE
conditions". For * relationships, any selection will be dictated by
some condition that prevails for the collaboration context. The key is
to separate that relationship instantiation from the actual
collaborations that navigate the relationship subsequently.

As I mentioned above, when the condition gets beyond what one can handle
via state variable conditions, one has to resort to the GoF patterns to
reorganize responsibilities (delegation) and provide dynamic dispatch.
But note that in most of the Gof Patterns like Strategy, somebody has to
instantiate the relationship between [Context] and the right [Strategy]
subclass instance based on context and that criteria will invariably be
expressed in terms of a state variable condition somewhere.

Hope this helps to clarify defining receivers.

Wavemaker

unread,
Jul 18, 2005, 2:24:53 AM7/18/05
to

"H. S. Lahman" wrote:
> Responding to Wavemaker...

<snip>

> Actually it won't take much to fix your implementation. You need to

I've been working on my state machine toolkit, and I've made some
progress (I think). First off, I got rid of the observer/broadcast model
in favor of peer-to-peer communication. I've also added a class called
SubsystemBuilder. It uses a collection of StateMachineBuilders
internally to create the STT code for each state machine. It enforces
some constraints such as making sure each state machine has a unique
name and ID (more on the state machine ID below) and resides in the
subsystem's namespace.

It also reads the events from the state machines and generates an
enumeration type representing all of the events in the subsystem. The
values of the enumerations are used as event IDs. This to me seems like
a much more robust approach than what I was using before by having the
state machines register/subscribe their events by name with the event
queue. That solution is looking a little silly to me at this point.

The basic functionality of the event queue is in an abstract class
called EventQueueBase. When a new subsystem is built, it generates an
EventQueue class derived from EventQueueBase. The EventQueue class
initializes a static event conversion table that it passes to its base
class. This is the table used by the base class for converting event
IDs. This way, the base class never has to be touched by the code
generator; it's only generating code for the derived class.

> When the code generator writes the event generation code, it needs
> access to that same table to insert the right class index into the
> event. (It knows what's on the end of the relationship from the Class
> Model, so it knows the name when it processes the reference.)

If I understand, a full code generator would also generate code for
raising events. IOW, this code would not be written by hand. Because the
event raising code is generated from a class model, the code generator
knows which class ID value to insert into the code; it can judge which
class type a state machine is sending the event to. Is that correct?

I haven't gotten that far, and I doubt that I will (though you never
know). So I think a compromise to the architecture you've described is
to keep my state machine class. When the code generator generates a
state machine derived class, it adds code that passes a unique class ID
up to the state machine base class. This ID is what is used by the event
queue to index the event conversion table. Also, the Dispatch method is
in the state machine class, so it can call it directly rather than
having to look it up in a dispatch table.

Without the help of a code generator to generate the event raising code,
clients will have to keep track of not only the instance object they are
sending events to but also the class ID. With the state machine base
class, clients only have to have a state machine instance to include
with the event. So I think for now my design is an ok compromise for my
goals. Does this make sense?


Wavemaker

unread,
Jul 18, 2005, 10:57:02 AM7/18/05
to

"H. S. Lahman" wrote:

<snip>

> Hope this helps to clarify defining receivers.

Absolutely. That was a tour de force post. It's broadened my perspective
and given me a better understanding of OOA/D. I tend to be codecentric
and sometimes lose sight of the larger picture. Your post was an
excellent remedy. Thank you very much.

H. S. Lahman

unread,
Jul 18, 2005, 2:49:19 PM7/18/05
to
Responding to Wavemaker...

> I've been working on my state machine toolkit, and I've made some
> progress (I think). First off, I got rid of the observer/broadcast model
> in favor of peer-to-peer communication. I've also added a class called
> SubsystemBuilder. It uses a collection of StateMachineBuilders
> internally to create the STT code for each state machine. It enforces
> some constraints such as making sure each state machine has a unique
> name and ID (more on the state machine ID below) and resides in the
> subsystem's namespace.

Alas, I sense another disconnect between us.

Why do you need multiple StateMachineBuilders? Once you develop an
identity strategy it should be reusable. So you would need only one
library class for the event queue. The static consumeEvent behavior is
going to always be the same since it is driven parametrically off the
STT and it will manipulate the currentState attribute the same way for
transitions.

So the only things that are unique to an individual class are the values
in the STT, which map to action body stubs defined for the class.
(Since by the time the STT is initialized, event and state identity has
been reduced to integer indices, that portion is quite generic as well.)
So I would expect the builder to build every state machine exactly the
same way once it is provided with the events, transitions, and action
identifiers. It seems to me your builder could do that for any state
machine with basically the same API you already described.

That is, the <ASCII> API just has to link {from state, event, to state}
and (state, action) for Moore models or {from state, action, to state}
for Mealy models. The builder then converts ASCII to indices and does
the nuts & bolts code generation for the class. But at that point the
processing should be pretty generic.

The last sentence also concerns me because it implies that the state
machine exists as an identifiable entity. While it is, indeed,
associated uniquely with a class, it is actually distributed in the code
among different object elements: the STT, the currentState attribute,
the static consumeEvent dispatch method, and the action methods. (This
ignores optimizations we talked about for global vs. local event
identity.) So the only thing one can really point at as being THE state
machine is the STT, which is just a data table that will probably have
the same name (identity) in every class.

BTW, consistent with this view that the state machine only exists as
elements of the class, you might think about letting the builder build
the entire class skeleton. That is, the only thing left to define when
one uses an object state machine for behavior are the class attributes.
Since those are just {name, ADT ID} pairs, those declarations are
trivial to provide in your state machine builder UI. Then all you have
to fill in manually are the action method stubs. IOW, think of your
builder as a class builder rather than a state machine builder.

[Naturally life is rarely that simple. B-) Some attributes may be
complex enough to warrant accessors that are more than dumb
getters/setters and those synchronous services will also have to be
defined somehow. There are also issues like whether a particular setter
wants to be public and whatnot. As I indicated before, the devil in
code generation is in the details. But you can still save yourself
quite a few keystrokes.]

>
> It also reads the events from the state machines and generates an
> enumeration type representing all of the events in the subsystem. The
> values of the enumerations are used as event IDs. This to me seems like
> a much more robust approach than what I was using before by having the
> state machines register/subscribe their events by name with the event
> queue. That solution is looking a little silly to me at this point.

Not so silly, I think. You still need to register the local event ID
with the event queue manager. More important, the API for the builder
may look like a registration service but all it is really doing is
defining the STT. The registration paradigm just happens to be a
convenient one for doing that incrementally and generically in a UI.
(As opposed to my suggestion of filling in place holders in source
templates for the poor man's builder.)

It seems to me that the only thing that has changed is the location of
some of the code that is being generated (i.e., from EventQueue to
class). I don't think the API technique for specifying it needs to
change that much.

>
> The basic functionality of the event queue is in an abstract class
> called EventQueueBase. When a new subsystem is built, it generates an
> EventQueue class derived from EventQueueBase. The EventQueue class
> initializes a static event conversion table that it passes to its base
> class. This is the table used by the base class for converting event
> IDs. This way, the base class never has to be touched by the code
> generator; it's only generating code for the derived class.

This is another notion that I am uncomfortable with. Subclassing
represents generalization/specialization within a group of entities that
are fundamentally the same. That is, any object from any leaf subclass
IS-A root superclass entity. What you seem to be describing here is
that the superclass and subclass have a sort of client/service
relationship where superclass and subclass have fundamentally different
responsibilities (i.e., one builds things that the other needs to use).

If I understand your description properly, the activities executed by
the EventQueue subclass are only performed at startup while the
EventQueueBase superclass's responsibilities are only executed when
events are processed during collaborations. To me that says these are
different critters rather than one being a specialization of the other.

<whole new world digression>
Note that the only reason you need the EventQueue subclass is to provide
customized code generation of the tables for the specific application
(more precisely, subsystem). You could do that without subclassing via

1 R1 1
[EventQueue] ---------------- [TableSpec]

where [TableSpec] is a dumb data holder for the tables. Your code
builder constructs that table and the EventQueue instance just does the
lookup from it. This provides the same sort of decoupling with less
static infrastructure.

However, there is an added bonus. I used "TableSpec" advisedly (rather
than just "TableData") because this is an analysis pattern for
parametric polymorphism. [TableSpec] is just a dumb data holder for
values that parametrically influence a generic behavior in [EventQueue].
So it could be initialized from an external configuration data file.
So you don't really need a builder for the [EventQueue] at all; you can
modify its behavior by simply modifying the table data in the
configuration file. (A factory of some sort is needed to read the
configuration file and create TableSpec, though.)

Now let's take it one step further:

1 R1 *
[QueueFactory] ----------------- [TableSpec]
| 1 | 1
| |
| R2 | R3[equal,R1,R3]
| |
| creates |
| * 1 specifies |
[EventQueue] ------------------------+

(The constraint on R3 just says one must get to the same [EventQueue]
instances from [TableSpec] via R3 as one gets to via R1 -> R2.)

Here [TableSpec] is that same dumb data holder. But now it is used by a
factory object to initialize the tables in an EventQueue when it is
created. Again, [QueueFactory] has an invariant behavior that is
influenced parametrically by [TableSpec]. So now one can reuse
[QueueFactory] and [EventQueue] across applications or subsystems
without change. All one has to provide is the unique configuration file
that is used to initialize the TableSpec. So now all your builder has
to do is create the external configuration file. Since there is no code
generation here you can modify the events without even recompiling and
relinking the application!

[Since you probably don't want to have QueueFactory pass a gazillion
parameters to a constructor, [EventQueue] will probably have an addEntry
interface method for initializing the tables that QueueFactory will
tediously invoke for each TableSpec entry when the EventQueue is created
at startup.]

I'll leave it as an exercise for the student to figure out how to do
exactly the same thing for object state machines. B-) So if you really
want to go gung ho over this, you can can fully define new object state
machines without even regenerating the code and <sometimes> without even
recompiling it.
</whole new world digression>

>>When the code generator writes the event generation code, it needs
>>access to that same table to insert the right class index into the
>>event. (It knows what's on the end of the relationship from the Class
>>Model, so it knows the name when it processes the reference.)
>
>
> If I understand, a full code generator would also generate code for
> raising events. IOW, this code would not be written by hand. Because the
> event raising code is generated from a class model, the code generator
> knows which class ID value to insert into the code; it can judge which
> class type a state machine is sending the event to. Is that correct?

Exactly. This is done in the methods that generate the events put on
the queue. A code generator looking at the full model will have enough
information to determine the class from the AAL relationship navigation
and the Class Model. It would have its own internal tables to convert
class name to an index for the identity strategy.

Since you aren't doing a full code generator, you will have to put those
calls in yourself manually. When you do, you will need to look at the
table to get the right class index.

>
> I haven't gotten that far, and I doubt that I will (though you never
> know). So I think a compromise to the architecture you've described is
> to keep my state machine class. When the code generator generates a
> state machine derived class, it adds code that passes a unique class ID
> up to the state machine base class. This ID is what is used by the event
> queue to index the event conversion table. Also, the Dispatch method is
> in the state machine class, so it can call it directly rather than
> having to look it up in a dispatch table.
>
> Without the help of a code generator to generate the event raising code,
> clients will have to keep track of not only the instance object they are
> sending events to but also the class ID. With the state machine base
> class, clients only have to have a state machine instance to include
> with the event. So I think for now my design is an ok compromise for my
> goals. Does this make sense?

Sure.

However, if you have some spare time and want to pursue the Wonderful
World of Code Generation a bit further, I would point out that
generating events is a very aspect-like process since the format is
identical. Recall my AAL example:

ref = this -> R1
GENERATE e1 TO ref WITH <data values>

The GENERATE statement will transform into something like:

myEventQueue->push (14, ref, <data values>);

That can easily be put in a function macro:

GENERATE(12, ref, <data values>)

Now that doesn't gain a lot but I am traveling a road here. Now suppose
one also has some generic naming conventions for relationship
implementation, such as

Object* navigateR1;

Now the first statement transforms into something like:

Object* ref;

ref = this.navigateR1;

Again, fodder for a function macro. (A bit more complicated for set
access via * relationships.) So we have

GENERATE(14, (NAVIGATE(R1)), <data values>);

Now suppose we don't have the whole table yet to convert the event ID to
14. What we would like to do is write:

GENERATE(reset, R1, <variables>);

and come back later and do the substitution once we knew what all the
events were. One way to do that is to treat the GENERATE as a
placeholder and use a perl script to do the lexical replacement when we
do have the table. To do that the perl script just needs to read the
table that maps event name to 14 from wherever it is defined and then it
can "walk" the code to do the substitution for all event generations.
[In this case one wouldn't need the function macros; the script would
write the code directly. They were just convenient to the example logic
chain.]

One could take that perl script a step further and eliminate the Object*
and navigateR1 obfuscation by letting the script name things properly.
To do that one just needs another set of tables for the perl script to
read that map {from class, relationship ID, to class, semantic name}
where "semantic name" is a meaningful name for the referential
attribute. In effect you are putting part of a Class Model into a table
for the perl script. The perl script can then "walk" any code you have
where relationships are navigated and insert readable code that will
have full type checking by the compiler.

[Warning. You might find this an amusing exercise to get insight into
the feasibility of full code generation. However, perl scripts is not
the way to go in the long term. It's another case of the devil being in
the details and the more specialized perl scripts one has the more time
one will spend tinkering with them for each new application.]

Wavemaker

unread,
Jul 18, 2005, 5:56:34 PM7/18/05
to

"H. S. Lahman" wrote:
> Responding to Wavemaker...
>
>> I've been working on my state machine toolkit, and I've made some
>> progress (I think). First off, I got rid of the observer/broadcast
>> model in favor of peer-to-peer communication. I've also added a class
>> called SubsystemBuilder. It uses a collection of StateMachineBuilders
>> internally to create the STT code for each state machine. It enforces
>> some constraints such as making sure each state machine has a unique
>> name and ID (more on the state machine ID below) and resides in the
>> subsystem's namespace.
>
> Alas, I sense another disconnect between us.

I think it's because I've been using the term state machine and class
synonymously. My fault. When I say state machines in a subsystem, I'm
talking about all of the classes that have an STT, Dispatch method,
action methods, etc.

I've failed to make a distinction between classes and state machines.
That's led to the confusion. I've also failed to adequately describe
what it is the StateMachineBuilder class does...

> BTW, consistent with this view that the state machine only exists as
> elements of the class, you might think about letting the builder build
> the entire class skeleton. That is, the only thing left to define
> when one uses an object state machine for behavior are the class
> attributes. Since those are just {name, ADT ID} pairs, those
> declarations are trivial to provide in your state machine builder UI.
> Then all you have to fill in manually are the action method stubs.
> IOW, think of your builder as a class builder rather than a state
> machine builder.

With the exception of adding attributes, this is where I'm at. The
StateMachineBuilder writes the code for an entire class with the STT
initialized as well as the action handler table, and so on. The code is
compilable as is. After saving the string produced by the builder to a
text file, you can add the file to a project in the development
environment and it will compile (provided it's linked to the "State"
dll, more on that below). All you have to do at that point is fill in
the blanks (action methods, add attributes, etc).

Should I rename it to ClassBuilder?

The SubsystemBuilder builds the class skeletons for the entire
subsystem. So if you wanted to create a subsystem and begin by adding a
class called Clock to the subsystem, it would look like this:

SubsystemBuilder builder = new SubsystemBuilder();

builder.AddStateMachine("Clock");

The subsystem builder internally creates a StateMachineBuilder for the
"Clock" state machine/class. Now, let's say we want to add a transition
for Clock:

// State Event Next State Action
builder.AddTransition("Clock", // Name of class - acts as a key.
"Stopped", "Start", "Running", "ActivateTimer");

The first parameter is the name of the class; it acts as a key. The
subsystem builder looks up the state machine builder for "Clock" and
adds the transition to it.

Say we want to add another class called Alarm:

builder.AddStateMachine("Alarm");

This will create another state machine builder for the "Alarm" class.
Adding a transition...

// State Event Next State Action
builder.AddTransition("Alarm",
"NotRinging", "TimeElapsed", "Ringing", "RingBell");

And some housekeeping:

builder.NamespaceName = "SubsystemDemo";

builder.SetInitialState("Clock", "Stopped");
builder.SetInitialState("Alarm", "NotRinging");

Ok, we can actually build the subsystem at this point:

builder.Build();

And can retrieve the code generated by the builder:

string clock = builder["Clock"];
string alarm = builder["Alarm"];

// An enumeration type with all of the events
// for the subsystem.
string eventType = builder.EventType;

// A custom event queue for this subsystem.
string eventQueue = builder.EventQueue;

All of the strings represent compilable code. Save them to text files,
load them into a development environment, and you're off. :-)

So each state machine builder has the responsibility of generating the
class skeleton for the class it represents.

I've tested all of this, and it's a pleasing moment when I look at the
resulting files and see all of that code I won't have to write. :-)

Some additional notes...

Before building the subsystem, the subsystem builder looks at the events
registered with each state machine builder. It then creates an
enumeration type representing all of the events in the subsystem. Again,
the values of the enumerations are used as event IDs. It also creates a
custom event queue with a table for the event ID conversions. I would
post some of the resulting code, but it's pretty boring as well as a
little verbose.

<snip>

>> The basic functionality of the event queue is in an abstract class
>> called EventQueueBase. When a new subsystem is built, it generates an
>> EventQueue class derived from EventQueueBase. The EventQueue class
>> initializes a static event conversion table that it passes to its
>> base class. This is the table used by the base class for converting
>> event IDs. This way, the base class never has to be touched by the
>> code generator; it's only generating code for the derived class.
>
> This is another notion that I am uncomfortable with. Subclassing
> represents generalization/specialization within a group of entities
> that are fundamentally the same. That is, any object from any leaf
> subclass IS-A root superclass entity. What you seem to be describing
> here is that the superclass and subclass have a sort of client/service
> relationship where superclass and subclass have fundamentally
> different responsibilities (i.e., one builds things that the other
> needs to use).
>
> If I understand your description properly, the activities executed by
> the EventQueue subclass are only performed at startup while the
> EventQueueBase superclass's responsibilities are only executed when
> events are processed during collaborations. To me that says these are
> different critters rather than one being a specialization of the
> other.

Yeah, this felt like a kludge. I had implemented it another way by
having the subsystem builder create a seperate EventConversionTable
class. An instance of this class would be passed to the event queue at
creation. But I wanted to avoid the extra step of having to create an
instance of the table and pass it into the event queue, so I did it
through inheritance. Not a very good excuse, I admit.

Another problem with inheritance is that I'm locking clients into the
state machine library. IOW, they will have to link to the library's dll
so that the compiler will have access to the base class. Making the
generated code 100% standalone might be a noble goal, but perhaps
unrealistic. At any rate, I need to find a better, cleaner solution. The
one you described later in the post, using parametric polymorphism,
sounds
good.

I guess what I also wanted to avoid was duplicating the event queue
code. This is multithreaded code instead of just boiler plate stuff, so
it would be much more sinister for clients to mess with. And there may
be changes I could make to it in the future so that it's more robust,
efficient, whatever. This is a situation in which having to link to a
library is actually beneficial. If the event queue improves at some
point, clients would just need a new copy of the dll to link to. None of
their code would have to change. So I'm a little undecided about this.

H. S. Lahman

unread,
Jul 19, 2005, 12:45:22 PM7/19/05
to
Responding to Wavemaker...

>>BTW, consistent with this view that the state machine only exists as
>>elements of the class, you might think about letting the builder build
>>the entire class skeleton. That is, the only thing left to define
>>when one uses an object state machine for behavior are the class
>>attributes. Since those are just {name, ADT ID} pairs, those
>>declarations are trivial to provide in your state machine builder UI.
>>Then all you have to fill in manually are the action method stubs.
>>IOW, think of your builder as a class builder rather than a state
>>machine builder.
>
>
> With the exception of adding attributes, this is where I'm at. The
> StateMachineBuilder writes the code for an entire class with the STT
> initialized as well as the action handler table, and so on. The code is
> compilable as is. After saving the string produced by the builder to a
> text file, you can add the file to a project in the development
> environment and it will compile (provided it's linked to the "State"
> dll, more on that below). All you have to do at that point is fill in
> the blanks (action methods, add attributes, etc).
>
> Should I rename it to ClassBuilder?

StateMachineBuilder is probably OK because the builder isn't building
the whole class. (I just had an incorrect impression of what it did.)

If one also defines the attributes, then a purist might try
ActiveClassBuilder or FSMClassBuilder. (The translation methodologies
usually refer to classes with FSM life cycles as 'active' since that is
the only way those methodologies describe pure behavior
responsibilities.) Better to be more specific than save keystrokes
(i.e., one wouldn't use the same builder to build a dumb data holder class).

Stream of consciousness thought... Since adding attribute definitions
is easy in most cases, you have a very short step to:

[ClassBuilder]
A
|
+----------+------------+
| |
[SpecClassBuilder] [FSMClassBuilder]

where all the attribute definition stuff is provided by the superclass
and the FSMClassBuilder flavor just provides FSM creation specialization.

This is a very clean and organized approach. As I understand it Builder
is essentially the interface to the overall code generation for a bunch
of classes w/ FSMs.

>
> Ok, we can actually build the subsystem at this point:
>
> builder.Build();
>
> And can retrieve the code generated by the builder:
>
> string clock = builder["Clock"];
> string alarm = builder["Alarm"];
>
> // An enumeration type with all of the events
> // for the subsystem.
> string eventType = builder.EventType;
>
> // A custom event queue for this subsystem.
> string eventQueue = builder.EventQueue;
>
> All of the strings represent compilable code. Save them to text files,
> load them into a development environment, and you're off. :-)
>
> So each state machine builder has the responsibility of generating the
> class skeleton for the class it represents.

I'm still not clear why you need multiple state machine builders when
the STT is defined parametrically. It seems to me one instance of one
StateMachineBuilder instantiated at startup should be enough.

> I've tested all of this, and it's a pleasing moment when I look at the
> resulting files and see all of that code I won't have to write. :-)

Indeed. You are well on your way to becoming hooked on translation. (I
doubt that I have written 10 KLOC of 3GL code since '87.)

> Some additional notes...
>
> Before building the subsystem, the subsystem builder looks at the events
> registered with each state machine builder. It then creates an
> enumeration type representing all of the events in the subsystem. Again,
> the values of the enumerations are used as event IDs. It also creates a
> custom event queue with a table for the event ID conversions. I would
> post some of the resulting code, but it's pretty boring as well as a
> little verbose.

Oops. This sounds like SystemBuilder is an object in the implementation
of a code generation subsystem (as opposed to the subsystem interface
that the UI talks to). In that case I am a bit uncomfortable with its
cohesion. It is has a whole lot of responsibilities (instantiating
StateMachineBuilders, providing STT parametric data, coordinating code
generation, recovering generated code, etc.). It also sounds like a god
object that coordinates everyone else in the subsystem. IOW, all
collaboration roads lead to SystemBuilder.

I would look for ways to delegate those responsibilities so that they
can be accessed directly from the subsystems interface or other objects.
For example, to get the generated code back I would provide a
collection of the generated classes and let the interface make the
request to it rather than the SystemBuilder. You are going to need that
collection anyway to keep track of things but it is important to the
problem because the interface needs to extract classes by name. That
justifies making it an explicit, standalone collection entity in the
subsystem.

So I would look around for a problem space entity that might logically
"own" such responsibilities. One possibility is Class. Semantically a
Class /is/ a collection of objects. In this context the abstraction is
a quite different view semantically than the class definition code being
generated but the same basic notion is being abstracted. That doesn't
quite work, though, because you are creating only the class definition,
not objects, and the important uniqueness lies in the class itself. But
that suggests bumping the collection up a level in abstraction:

[Entity]
| 1
|
| R1
|
| represented by
| *
[ClassTemplate]
+ name
+ stringDefinitionHandle

When StateMachineBuilder creates the code it creates a place holders and
adds it to the [Entity] collection. Now when the interface requests the
code for a particular class, it asks [Entity] directly rather than
[SystemBuilder].

Note that [Entity] would be a logical place for the table that maps
ASCII class names to integer indices as well.

You're going to have to put some more words around the duplicated code
you are worried about in the event queue.

A digression on a poor man's time slicing... One of the nice features
of using state machines is that it is often possible to use them for
concurrency with much less overhead than conventional OS threading. The
problem with OS threading is that it is often designed to support time
slicing at the instruction level for hard R-T constraints. That is
nontrivial and it can involve a lot of context-switching overhead.

However, when one employs state machines it is possible to provide large
scale (action scope) time slicing for very little overhead. The trick
is to use multiple instances of the event queue where each instance
handles events for different sets of object state machines. Now one can
use a simple round robin rotation of which event queue pops next (e.g.,
a queue of queue managers). In effect this provides method-level time
slicing among the sets of objects since only one action executes to
completion for each event.

One of the nice things is that once one uses an asynchronous model to
solve the problem with one queue manager, introducing multiple queue
managers will not break anything. So this should Just Work so long as
one does not need finer granularity than method slicing. One can even
get cute and prioritize the queues. (There are some details to work out
because the push has to be synchronous so one has to avoid doing pop
queue housekeeping at the same time.)

Wavemaker

unread,
Jul 19, 2005, 4:19:38 PM7/19/05
to

"H. S. Lahman" wrote:
> Responding to Wavemaker...

<snip>

>> Some additional notes...
>>
>> Before building the subsystem, the subsystem builder looks at the
>> events registered with each state machine builder. It then creates an
>> enumeration type representing all of the events in the subsystem.
>> Again, the values of the enumerations are used as event IDs. It also
>> creates a custom event queue with a table for the event ID
>> conversions. I would post some of the resulting code, but it's pretty
>> boring as well as a little verbose.
>
> Oops. This sounds like SystemBuilder is an object in the
> implementation of a code generation subsystem (as opposed to the
> subsystem interface that the UI talks to). In that case I am a bit
> uncomfortable with its cohesion. It is has a whole lot of
> responsibilities (instantiating StateMachineBuilders, providing STT
> parametric data, coordinating code generation, recovering generated
> code, etc.). It also sounds like a god object that coordinates
> everyone else in the subsystem. IOW, all collaboration roads lead to
> SystemBuilder.

I agree and appreciate your suggestions. The class has gotten out of
hand, and I'll take a hard look at delegating out some if its
responsibilities.

I wanted to mention that I implemented the suggestion you made yesterday
to initialize the EventQueue class with a table spec configuration file.
The SubsystemBuilder writes the file (yet another responsibility that
needs to be delegated out) as an XML file. An EventQueueFactory reads
the file, creates the event conversion table we've discussed, and
creates and initializes an EventQueue object with the table. Works like
a charm. This is my first foray with this approach, and I have to say I
like it quite a bit.

<snip>

> A digression on a poor man's time slicing... One of the nice features
> of using state machines is that it is often possible to use them for
> concurrency with much less overhead than conventional OS threading.
> The problem with OS threading is that it is often designed to support
> time slicing at the instruction level for hard R-T constraints. That
> is nontrivial and it can involve a lot of context-switching overhead.
>
> However, when one employs state machines it is possible to provide
> large scale (action scope) time slicing for very little overhead. The
> trick is to use multiple instances of the event queue where each
> instance handles events for different sets of object state machines.
> Now one can use a simple round robin rotation of which event queue
> pops next (e.g., a queue of queue managers). In effect this provides
> method-level time slicing among the sets of objects since only one
> action executes to completion for each event.
>
> One of the nice things is that once one uses an asynchronous model to
> solve the problem with one queue manager, introducing multiple queue
> managers will not break anything. So this should Just Work so long as
> one does not need finer granularity than method slicing. One can even
> get cute and prioritize the queues. (There are some details to work
> out because the push has to be synchronous so one has to avoid doing
> pop queue housekeeping at the same time.)

Fascinating. I had read that state machines could be used to create
"poor man's" threading, but only had a vague notion of how it could be
done. This makes it clear. Each event queue represents a thread, and the
round robin rotation switches between each thread to allow its time to
run. Pretty cool. I'll have to try this sometime. I imagine that this
approach would enforce cohesiveness in method design. You would want
each method to do exactly one thing and to do it as quickly as possible
so as to not slow the system down.

H. S. Lahman

unread,
Jul 20, 2005, 1:19:06 PM7/20/05
to
Responding to Wavemaker...

> I wanted to mention that I implemented the suggestion you made yesterday
> to initialize the EventQueue class with a table spec configuration file.
> The SubsystemBuilder writes the file (yet another responsibility that
> needs to be delegated out) as an XML file. An EventQueueFactory reads
> the file, creates the event conversion table we've discussed, and
> creates and initializes an EventQueue object with the table. Works like
> a charm. This is my first foray with this approach, and I have to say I
> like it quite a bit.

I have to warn you that you are likely to encounter a problem that is
common in code generation if you use your builder to deal with
incremental changes. Some of your exported definitions will need to be
re-built if you make certain kinds of changes (e.g., changing an event
or method name). With your current system that presents two problems.

The first is that you have to know what needs to get rebuilt and tell
the SystemBuilder to do it. Since you built it, you know it intimately
and will probably be able to anticipate such situations. However, you
will eventually get burnt on some build. The second problem is that
your SystemBuilder requires you to re-specify the entire thing (e.g.,
the whole object state machine) to regenerate the configuration data,
which can be tedious. There are ways around both problems but they
require your code generation to be significantly smarter.

So why not build the system once and then just maintain the
configuration files? That's fine so long as all the changes can be
handled through the configuration files. But life rarely works out that
simply, especially if one is only automating some of the code
generation. (OTOH, some pretty complicated subject matters can be
abstracted and generalized almost completely, which is why we have GUI
that define everything in configuration files.)

BTW, make sure your generated static table implementation code is put in
a single file all by itself. The problem is that if you have to rebuild
the initialization, you don't want the files where you have already
filled in manual stuff like action bodies to be overwritten. [I would
consider writing the action stubs out in a dedicated file to a special
directory. Then when you fill them in you can copy them to the compiler
build directory. That will prevent annoying accidents.]

I'm not trying to turn you off on code generation here or even
suggesting your code generator needs more work. I am just pointing out
that you haven't seen any of the downside yet. Commercial code
generators cost Big Bucks for a reason. The work you are doing here
will save you a lot of keystrokes in the future and will probably
improve the reliability of your applications with FSMs. But it is not a
panacea and you are going to encounter some annoyances during maintenance.

> Fascinating. I had read that state machines could be used to create
> "poor man's" threading, but only had a vague notion of how it could be
> done. This makes it clear. Each event queue represents a thread, and the
> round robin rotation switches between each thread to allow its time to
> run. Pretty cool. I'll have to try this sometime. I imagine that this
> approach would enforce cohesiveness in method design. You would want
> each method to do exactly one thing and to do it as quickly as possible
> so as to not slow the system down.

Right. But you tend to get cohesiveness for free with OO abstraction.
(Note I jumped on your SystemBuilder for it lack of cohesiveness.)
Objects tend to be small (few responsibilities) and the responsibilities
are logically indivisible (calling chains are limited to one level of
knowledge access). FSMs just enforce that because FSAs can't rely on
knowing the next state.

If you have followed my other threads here where I talk about Harel,
this is one reason for my assertion that when an object gets complicated
enough to warrant using Harel, it is time to delegate. After such
delegation the poor man's threading is quite likely to have pretty fine
granularity.

Wavemaker

unread,
Jul 22, 2005, 3:21:15 AM7/22/05
to

"H. S. Lahman" wrote:
> Responding to Wavemaker...
>
>> I wanted to mention that I implemented the suggestion you made
>> yesterday to initialize the EventQueue class with a table spec
>> configuration file. The SubsystemBuilder writes the file (yet another
>> responsibility that needs to be delegated out) as an XML file. An
>> EventQueueFactory reads the file, creates the event conversion table
>> we've discussed, and creates and initializes an EventQueue object
>> with the table. Works like a charm. This is my first foray with this
>> approach, and I have to say I like it quite a bit.
>
> I have to warn you that you are likely to encounter a problem that is
> common in code generation if you use your builder to deal with
> incremental changes. Some of your exported definitions will need to
> be re-built if you make certain kinds of changes (e.g., changing an
> event or method name).

<snip>

Thanks for the warning and the advice. Should I pursue this further,
I'll do so with open eyes. At this time, my goal is to refine what I've
done so far. I want to use the heck out of the state machine toolkit and
let it reveal what features I will need to make it more useful.

<snip>

> Right. But you tend to get cohesiveness for free with OO abstraction.
> (Note I jumped on your SystemBuilder for it lack of cohesiveness.)
> Objects tend to be small (few responsibilities) and the
> responsibilities are logically indivisible (calling chains are limited
> to one level of knowledge access). FSMs just enforce that because
> FSAs can't rely on knowing the next state.
>
> If you have followed my other threads here where I talk about Harel,
> this is one reason for my assertion that when an object gets
> complicated enough to warrant using Harel, it is time to delegate.
> After such delegation the poor man's threading is quite likely to have
> pretty fine granularity.

In a sense, this brings the thread full circle. I originally started it
because I was struggling with class cohesiveness. I was sensing that my
state machines were too complicated. I have no comment on the quality of
Harel, I'm really not qualified, but using "flat" FSMs over the past
several days has brought the cohesiveness of my classes, or lack there
of, into sharper focus. I still have questions, but I will wait and try
to gain a little more experience so that the questions will be more
specific.

Anyway, I want to thank you for your guidance in this thread. It's been
very helpful, and I feel like I've taking quite a leap forward in my
progress towards becoming a better software engineer.


H. S. Lahman

unread,
Jul 22, 2005, 12:13:24 PM7/22/05
to
Responding to Wavemaker..

>>Right. But you tend to get cohesiveness for free with OO abstraction.
>>(Note I jumped on your SystemBuilder for it lack of cohesiveness.)
>>Objects tend to be small (few responsibilities) and the
>>responsibilities are logically indivisible (calling chains are limited
>>to one level of knowledge access). FSMs just enforce that because
>>FSAs can't rely on knowing the next state.
>>
>>If you have followed my other threads here where I talk about Harel,
>>this is one reason for my assertion that when an object gets
>>complicated enough to warrant using Harel, it is time to delegate.
>>After such delegation the poor man's threading is quite likely to have
>>pretty fine granularity.
>
>
> In a sense, this brings the thread full circle. I originally started it
> because I was struggling with class cohesiveness. I was sensing that my
> state machines were too complicated. I have no comment on the quality of
> Harel, I'm really not qualified, but using "flat" FSMs over the past
> several days has brought the cohesiveness of my classes, or lack there
> of, into sharper focus. I still have questions, but I will wait and try
> to gain a little more experience so that the questions will be more
> specific.

Cohesiveness is part of the fundamental abstraction of the problem
space. With good OOA/D technique one should have a set of abstractions
that are already cohesive before one gets around to designing state
machines. IOW, overly complex state machines are just a symptom that
one did not get things right in the original identification of objects
and responsibilities.

>
> Anyway, I want to thank you for your guidance in this thread. It's been
> very helpful, and I feel like I've taking quite a leap forward in my
> progress towards becoming a better software engineer.

Many moons ago I did a stint at teaching at the college level. A common
refrain for teachers is, "In every class there are a couple who Get It
and they make it all worthwhile." The downside of that is that most
don't Get It. That's a problem for me because I tend to look at the
half-empty side of things. So I quit teaching because I was frustrated.

I still keep at it partially through forums like this, probably due to
some masochistic streak. FWIW, it is a two-way street and you have made
my day.

0 new messages