Section 11.6.  Analysis
Team LiB
Previous Section Next Section

11.6. Analysis

By now you should have a good sense of the DSEL development process. The steps mirror the ones we used to analyze each of the domain-specific languages in Chapter 10.

  1. Identify the domain abstractions.

  2. Experiment with representing those abstractions in code.

  3. Build a prototype.

  4. Iterate.

So how well did it work out this time? Did we achieve our design goals?

  1. Interoperability. Interoperability with other DSLs is achieved because we have created our DSL as a library, that is, a domain-specific embedded language. We can embed types from other domain libraries in our events, invoke arbitrary functions in other domains from within the transition actions, or operate the FSM from code written in other DSELs.

  2. Declarativeness. Looking back at our player implementation, the bulk of the code written by its author is in the transition table itself, and almost everything in the declaration of player is essential to its meaning. It does appear to be a very direct translation of the domain language into C++. Furthermore, it is possible to completely replace the framework's implementation without altering the state machine declaration. In the examples directory of the code that accompanies this book, player2.cpp illustrates a state_machine that dispatches using O(1) lookup into a static table of function pointers.

  3. Expressiveness. The STT as declared in player does look very much like a table should be expected to, particularly with the formatting conventions we've used.

  4. Efficiency. The code generated for process_event avoids all runtime dispatch other than switching on the current state, and that doesn't require any memory accesses or table lookups, since event_dispatcher uses compile-time constants for comparison. The design is efficient because we ruthlessly kept everything in the compile-time world of metadata as long as possible.

    The authors analyzed the assembly language output for this example by two different compilers, and the generated code appears to rival that of a hand-coded state machine. That said, if you were going to use this framework in a system where every cycle counts, you'd probably want to throw the example at your target compiler and inspect the results. You'd probably also want to expand the STT with more events and transitions to see whether the efficiency of the code scales well with the size of the state machine.

  5. Static Type Safety. The framework is fairly typesafe. There are only two static_casts in the whole system (in process_event and call_no_transition), and the potential for damage is limited because they will only compile if Derived is indeed derived from the state_machine.

  6. Maintainability. New events can be added to the system by simply creating a new type. New states can be added similarly, by extending the states enum. That enum could even be defined outside player, if we cared to do so. It wouldn't do much to reduce coupling, though, since the transition table must contain state names and must be visible from the FSM declaration. Transitions are easy to add by writing new rows in the transition_table.

    One point that might be of some concern is the cost of maintaining the visual alignment of the table as the FSM evolves. That cost does appear to be inevitable if we want to closely match the domain abstraction. Although we have the flexibility to throw out strict alignment if the cost of maintenance grows too high, experiments appear to show that the table representation makes a big difference in its understandability, so we wouldn't want to take this step lightly.

  7. Scalability. It's a bit harder to evaluate the framework's extensibility from what we've seen here. One thing we can say at this point is that the design seems sufficiently modular to make adding new features reasonably easy. You'll get a chance to explore just how easy it actually is if you do some of this chapter's exercises. Due to the DSL's declarativeness, we can at least be fairly sure that features can be added without breaking existing user code.

    Team LiB
    Previous Section Next Section