Recipe12.2.Generating Switching Code


Recipe 12.2. Generating Switching Code

Problem

You want to generate the switching code that will route incoming messages to their message handlers.

Solution

The message repository stores information about which processes receive which messages. Therefore, given a process name, you can generate a message switch that routes inbound messages to handlers:

<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">       <xsl:output method="text"/>       <xsl:param name="process" select=" '*' "/>       <xsl:variable name="message-dir" select=" 'messages' "/>   <xsl:variable name="directory-sep" select=" '/' "/>   <xsl:variable name="include-ext" select=" '.h' "/>          <xsl:template match="MessageRepository">     <!-- Generate source file preliminaries -->     <xsl:call-template name="file-start"/>          <!-- Generate includes for messages this process receives -->     <xsl:apply-templates select="Messages/                                   Message[Receivers/                                           ProcessRef = $process or                                           $process = '*']"                           mode="includes"/>          <!-- Generate message switch preliminaries -->                                     <xsl:call-template name="switch-start"/>          <!-- Generate switch body -->     <xsl:apply-templates select="Messages/                                   Message[Receivers/                                           ProcessRef = $process or                                            $process = '*']"                           mode="switch"/>          <!-- Generate switch end -->                                     <xsl:call-template name="switch-end"/>          <!-- Generate file end -->     <xsl:call-template name="file-end"/>   </xsl:template>            <!-- Generate an include for each message -->        <xsl:template match="Message" mode="includes">     <xsl:text>#include &lt;</xsl:text>     <xsl:value-of select="$message-dir"/>     <xsl:value-of select="$directory-sep"/>     <xsl:value-of select="Name"/>     <xsl:value-of select="$include-ext"/>     <xsl:text>&gt;&#xa;</xsl:text>   </xsl:template>       <!-- Generate handler case for each message type -->   <xsl:template match="Message" mode="switch">     case <xsl:value-of select="Name"/>_ID:       <xsl:call-template name="case-action"/>   </xsl:template>     <!-- Generate the message handler action --> <xsl:template name="case-action">       return <xsl:value-of select="Name"/>(*static_cast&lt;const <xsl:value-of       select="DataTypeName"/>*&gt;(msg.getData( ))).process( ) ; </xsl:template>       <!-- Do nothing by default. Users will override if necessary -->   <xsl:template name="file-start"/>   <xsl:template name="file-end"/>       <!-- Generate start of switch statement -->    <xsl:template name="switch-start"> #include &lt;transport/Message.h&gt; #include &lt;transport/MESSAGE_IDS.h&gt;     <xsl:text>&#xa;&#xa;</xsl:text> <xsl:call-template name="process-function"/> {   switch (msg.getId( ))   {   </xsl:template>      <xsl:template name="switch-end">     return false ;   } }   </xsl:template>     <!-- Generate signature for message-processing entry point --> <xsl:template name="process-function"> bool processMessage(const Message&amp; msg) </xsl:template>         </xsl:stylesheet>

When applied to your test repository, this example produces the following switching code:

#include <messages/ADD_STOCK_ORDER.h> #include <messages/CANCEL_STOCK_ORDER.h>     #include <transport/Message.h> #include <transport/MESSAGE_IDS.h>     bool processMessage(const Message& msg)     {   switch (msg.getId( ))   {        case ADD_STOCK_ORDER_ID:              return ADD_STOCK_ORDER(*static_cast<const        AddStockOrderData*>(msg.getData( ))).process( ) ;         case CANCEL_STOCK_ORDER_ID:              return CANCEL_STOCK_ORDER(*static_cast<const        CancelStockOrderData*>(msg.getData( ))).process( ) ;         return false ;   } }

Discussion

Applications that process messages always have some form of message switch. The structure of these switches can vary somewhat, but they typically must check for some message identifier and route the message to a handler. The identifier can take the form of an integer (as in this case) or a string. In some cases, multipart identifiers consist of a message type and subtype. On the processing side, the handler can take the form of a simple function or an object that is instantiated to process the message. It is important to make even simple code generators modular so that they can be reused in more than one context. At a company where I once worked, a group decided to create a very interesting Perl-based facility that generated C++ code from an XSD schema. It was of sufficient usefulness that other groups wanted to use it. Unfortunately, the developers thought only in terms of their particular needs, and their otherwise impressive solution was not reusable. Writing even basic code generators is a formidable task, and you should try to make them as general as is practical so others will not have to relive your pain.

The particular solution shown provides several hooks for customizing what appears at the start of the generated file, the start of the switching code, the end of the switching code, and the end of the file. This flexibility allows Recipe 12.5 to reuse this generator. Still, it could use further improvement. For example, this generator assumes that a C-switch statement performs message switching. However, some applications may use an if-else style switch, especially if the message IDs are strings. Others may use a table lookup from the message ID to a function pointer.

Can a single generator be made general enough to handle all these possibilities? Maybe, but the result will probably be extremely complex. A better approach would create generation components that could be reused in more complex generation scenarios. Here is what a generic C-switch statement generator might look like:

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE xslt [   <!--Used to control code intenting -->   <!ENTITY INDENT "    ">   <!ENTITY INDENT2 "&INDENT;&INDENT;"> ]> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">       <xsl:template name="gen-C-Switch">     <xsl:param name="variable"/>     <xsl:param name="cases" select="/.."/>     <xsl:param name="actions" select="/.."/>     <xsl:param name="default"/>     <xsl:param name="baseIndent" select="'&INDENT;'"/>     <xsl:param name="genBreak" select="true( )"/>         <xsl:value-of select="$baseIndent"/>     <xsl:text>switch (</xsl:text>     <xsl:value-of select="$variable"/>     <xsl:text>)&#xa;</xsl:text>     <xsl:value-of select="$baseIndent"/>     <xsl:text>{&#xa;</xsl:text>          <xsl:for-each select="$cases">       <xsl:variable name="pos" select="position( )"/>           <xsl:value-of select="$baseIndent"/>       <xsl:text>&INDENT;case </xsl:text>       <xsl:value-of select="."/>       <xsl:text>:&#xa;</xsl:text>       <xsl:call-template name="gen-C-Switch-caseBody">         <xsl:with-param name="case" select="."/>         <xsl:with-param name="action" select="$actions[$pos]"/>         <xsl:with-param name="baseIndent"                                       select="concat('&INDENT2;',$baseIndent)"/>       </xsl:call-template>       <xsl:if test="$genBreak">         <xsl:value-of select="$baseIndent"/>         <xsl:text>&INDENT2;break;</xsl:text>       </xsl:if>             <xsl:text>&#xa;</xsl:text>     </xsl:for-each>        <xsl:if test="$default">       <xsl:value-of select="$baseIndent"/>       <xsl:text>&INDENT;default:</xsl:text>       <xsl:text>:&#xa;</xsl:text>       <xsl:call-template name="gen-C-Switch-default-caseBody">         <xsl:with-param name="action" select="$default"/>         <xsl:with-param name="baseIndent"               select="concat('&INDENT2;',$baseIndent)"/>       </xsl:call-template>       <xsl:text>&#xa;</xsl:text>     </xsl:if>     <xsl:value-of select="$baseIndent"/>     <xsl:text>}&#xa;</xsl:text>   </xsl:template>                 <!-- This generates a null statement by default. -->   <!-- Override to generate code for the case -->    <xsl:template name="gen-C-Switch-caseBody">     <xsl:param name="case"/>     <xsl:param name="action"/>     <xsl:param name="baseIndent"/>          <xsl:value-of select="$baseIndent"/>     <xsl:text>;</xsl:text>   </xsl:template>       <!-- This invokes the regular case body generator. -->    <!-- Overide to do something special for the default case. -->   <xsl:template name="gen-C-Switch-default-caseBody">     <xsl:param name="action"/>     <xsl:param name="baseIndent"/>          <xsl:call-template name="gen-C-Switch-caseBody">       <xsl:with-param name="action" select="$action"/>       <xsl:with-param name="baseIndent" select="$baseIndent"/>     </xsl:call-template>   </xsl:template>     </xsl:stylesheet>

Chapter 14 demonstrates techniques that make generators like this even more generic.




XSLT Cookbook
XSLT Cookbook: Solutions and Examples for XML and XSLT Developers, 2nd Edition
ISBN: 0596009747
EAN: 2147483647
Year: 2003
Pages: 208
Authors: Sal Mangano

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net