You need tools to help debug your application. In particular, you want the ability to render binary messages in human-readable form.
When developing messaging applications, developers often hand-code pretty printers because they make debugging these applications considerably easier. However, this kind of code can be generated if you have a message repository. This solution shows how to reuse the message switch generator from Recipe 10.2:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE xslt [ <!--Used to control code intenting --> <!ENTITY INDENT " "> <!ENTITY INDENT2 "&INDENT;&INDENT;"> <!ENTITY LS "<<"> ]> <xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- This pretty-printer generator needs a message switch so we --> <!-- reuse the one we already wrote. --> <xsl:import href="messageSwitch.xslt"/> <!--The directory to generate code --> <xsl:param name="generationDir" select=" 'src/' "/> <!--The C++ header file name --> <xsl:param name="prettyPrintHeader" select=" 'prettyPrint.h' "/> <!--The C++ source file name --> <xsl:param name="prettyPrintSource" select=" 'prettyPrint.C' "/> <!--Key to locate data types by name --> <xsl:key name="dataTypes" match="Structure" use="Name" /> <xsl:key name="dataTypes" match="Primitive" use="Name" /> <xsl:key name="dataTypes" match="Array" use="Name" /> <xsl:key name="dataTypes" match="Enumeration" use="Name" /> <xsl:template match="MessageRepository"> <xsl:document href="{concat($generationDir,$prettyPrintHeader)}"> <xsl:text>void prettyPrintMessage</xsl:text> <xsl:text>(ostream& stream, const Message& msg);
</xsl:text> <xsl:apply-templates select="DataTypes/Structure" mode="declare"/> </xsl:document> <xsl:document href="{concat($generationDir,$prettyPrintSource)}"> <xsl:apply-imports/> <xsl:apply-templates select="DataTypes/Structure" mode="printers"/> </xsl:document> </xsl:template> <!--Override the message processing function name from --> <!-- messageSwitch.xslt to customize the function --> <!-- signiture to take a stream --> <xsl:template name="process-function"> <xsl:text>void prettyPrintMessage</xsl:text> <xsl:text>(ostream& stream, const Message& msg)</xsl:text> </xsl:template> <!--Override case action from messageSwitch.xslt to generate --> <!-- call to prettyPrinter for message data --> <xsl:template name="case-action"> <xsl:text> prettyPrint(stream, *static_cast<const </xsl:text> <xsl:value-of select="DataTypeName"/> <xsl:text>*>(msg.getData( ))) ; break;</xsl:text> </xsl:template> <!--Generate declarations for each message data type --> <xsl:template match="Structure" mode="declare"> <!--Forward declare the message data class --> <xsl:text>class </xsl:text> <xsl:value-of select="Name"/> <xsl:text> ;
</xsl:text> <!--Forward declare the message prettyPrint function --> <xsl:text>ostream prettyPrint(ostream & stream, const </xsl:text> <xsl:value-of select="Name"/> <xsl:text>& data);
</xsl:text> </xsl:template> <!--Generate the body of a pretty-printer --> <xsl:template match="Structure" mode="printers"> <xsl:text>ostream prettyPrint(ostream & stream, const </xsl:text> <xsl:value-of select="Name"/> <xsl:text>& data)
</xsl:text> <xsl:text>{
</xsl:text> <xsl:text>&INDENT;stream 
</xsl:text> <xsl:text>&INDENT2;&LS; "</xsl:text> <xsl:value-of select="Name"/> <xsl:text>" &LS; endl &LS; "{" &LS; endl 
</xsl:text> <xsl:for-each select="Members/Member"> <xsl:text>&INDENT2;&LS; "</xsl:text> <xsl:value-of select="Name"/>: " &LS; <xsl:text/> <xsl:apply-templates select="key('dataTypes',DataTypeName)" mode="print"> <xsl:with-param name="name" select="Name"/> </xsl:apply-templates> <xsl:text>
</xsl:text> </xsl:for-each> <xsl:text>&INDENT2;&LS; "}" &LS; endl ; 
</xsl:text> <xsl:text>&INDENT;return stream ;
</xsl:text> <xsl:text>}

</xsl:text> </xsl:template> <!--Nested structures invoke the pretty-printer for that structure --> <xsl:template match="Structure" mode="print"> <xsl:param name="name"/> <xsl:text>prettyPrint(stream, data.get_</xsl:text> <xsl:value-of select="$name"/><xsl:text>( ))</xsl:text> </xsl:template> <!--We assume there is a get function for each --> <!-- primitive component of the message --> <xsl:template match="*" mode="print"> <xsl:param name="name"/> <xsl:text>data.get_</xsl:text> <xsl:value-of select="$name"/>( ) << endl<xsl:text/> </xsl:template> </xsl:stylesheet>
The following source file is generated. We omit the header since it contains only declarations:
#include <messages/ADD_STOCK_ORDER.h> #include <messages/ADD_STOCK_ORDER_ACK.h> #include <messages/ADD_STOCK_ORDER_NACK.h> #include <messages/CANCEL_STOCK_ORDER.h> #include <messages/CANCEL_STOCK_ORDER_ACK.h> #include <messages/CANCEL_STOCK_ORDER_NACK.h> #include <messages/TEST.h> #include <transport/Message.h> #include <transport/MESSAGE_IDS.h> void prettyPrintMessage(ostream& stream, const Message& msg) { switch (msg.getId( )) { case ADD_STOCK_ORDER_ID: prettyPrint(stream, *static_cast<const AddStockOrderData*>(msg.getData( ))) ; break; case ADD_STOCK_ORDER_ACK_ID: prettyPrint(stream, *static_cast<const AddStockOrderAckData*>(msg.getData( ))) ; break; case ADD_STOCK_ORDER_NACK_ID: prettyPrint(stream, *static_cast<const AddStockOrderNackData*>(msg.getData( ))) ; break; case CANCEL_STOCK_ORDER_ID: prettyPrint(stream, *static_cast<const CancelStockOrderData*>(msg.getData( ))) ; break; case CANCEL_STOCK_ORDER_ACK_ID: prettyPrint(stream, *static_cast<const CancelStockOrderAckData*>(msg.getData( ))) ; break; case CANCEL_STOCK_ORDER_NACK_ID: prettyPrint(stream, *static_cast<const CancelStockOrderNackData*>(msg.getData( ))) ; break; case TEST_ID: prettyPrint(stream, *static_cast<const TestData*>(msg.getData( ))) ; break; return false ; } } ostream prettyPrint(ostream & stream, const TestData& data) { stream << "TestData" << endl << "{" << endl << "order: " << prettyPrint(stream, data.get_order( )) << "cancel: " << prettyPrint(stream, data.get_cancel( )) << "}" << endl ; return stream ; } ostream prettyPrint(ostream & stream, const AddStockOrderData& data) { stream << "AddStockOrderData" << endl << "{" << endl << "symbol: " << data.get_symbol( ) << endl << "quantity: " << data.get_quantity( ) << endl << "side: " << data.get_side( ) << endl << "type: " << data.get_type( ) << endl << "price: " << data.get_price( ) << endl << "}" << endl ; return stream ; } ostream prettyPrint(ostream & stream, const AddStockOrderAckData& data) { stream << "AddStockOrderAckData" << endl << "{" << endl << "orderId: " << data.get_orderId( ) << endl << "}" << endl ; return stream ; } ostream prettyPrint(ostream & stream, const AddStockOrderNackData& data) { stream << "AddStockOrderNackData" << endl << "{" << endl << "reason: " << data.get_reason( ) << endl << "}" << endl ; return stream ; } ostream prettyPrint(ostream & stream, const CancelStockOrderData& data) { stream << "CancelStockOrderData" << endl << "{" << endl << "orderId: " << data.get_orderId( ) << endl << "quantity: " << data.get_quantity( ) << endl << "}" << endl ; return stream ; } ostream prettyPrint(ostream & stream, const CancelStockOrderAckData& data) { stream << "CancelStockOrderAckData" << endl << "{" << endl << "orderId: " << data.get_orderId( ) << endl << "quantityRemaining: " << data.get_quantityRemaining( ) << endl << "}" << endl ; return stream ; } ostream prettyPrint(ostream & stream, const CancelStockOrderNackData& data) { stream << "CancelStockOrderNackData" << endl << "{" << endl << "orderId: " << data.get_orderId( ) << endl << "reason: " << data.get_reason( ) << endl << "}" << endl ; return stream ; }
This code-generation recipe attacks the pretty-printing problem head on by literally generating the pretty-print code for each message. Following this example is simple, and the results are effective. However, you could approach the problem more generally, and in the process create a more useful code generator.
Specifically, you can break the pretty-printing process into two stages. One stage is the process of parsing a monolithic message into its constituent parts. The other is the process of taking those parts and formatting them into human-readable text.
Looking at the problem in this way changes the solution from the generation of a single-purpose set of functions (a pretty printer) to the generation of a more generic message parser. Such parsers are usually event driven. Readers familiar with the Simple API for XML (SAX) will recognize this style of processing. The stylesheet used to generate a message parser is a variation of the pretty-print generator. Instead of sending message components to a stream, it sends parse events to a handler:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE xslt [ <!--Used to control code intenting --> <!ENTITY INDENT " "> <!ENTITY INDENT2 "&INDENT;&INDENT;"> <!ENTITY LS "<<"> ]> <xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- This mesage parse generator needs a message switch so we --> <!-- reuse the one we already wrote. --> <xsl:import href="messageSwitch.xslt"/> <!--The directory to generate code --> <xsl:param name="generationDir" select=" 'src/' "/> <!--The C++ header file name --> <xsl:param name="msgParseHeader" select=" 'msgParse.h' "/> <!--The C++ source file name --> <xsl:param name="msgParseSource" select=" 'msgParse.C' "/> <!--Key to locate data types by name --> <xsl:key name="dataTypes" match="Structure" use="Name" /> <xsl:key name="dataTypes" match="Primitive" use="Name" /> <xsl:key name="dataTypes" match="Array" use="Name" /> <xsl:key name="dataTypes" match="Enumeration" use="Name" /> <xsl:template match="MessageRepository"> <xsl:document href="{concat($generationDir,$msgParseHeader)}"> <xsl:text>void parseMessage</xsl:text> <xsl:text>(MessageHandler& handler, const Message& msg);
 </xsl:text> <xsl:apply-templates select="DataTypes/Structure" mode="declare"/> </xsl:document> <xsl:document href="{concat($generationDir,$msgParseSource)}"> <xsl:apply-imports/> <xsl:apply-templates select="DataTypes/Structure" mode="parsers"/> </xsl:document> </xsl:template> <!--Override the message processing function name from --> <!-- messageSwitch.xslt to customize the function signiture --> <!-- to take a handler --> <xsl:template name="process-function"> <xsl:text>void parseMessage</xsl:text> <xsl:text>(MessageHandler& handler, const Message& msg)</xsl:text> </xsl:template> <!--Override case action from messageSwitch.xslt to generate --> <!-- call to parse for message data --> <xsl:template name="case-action"> <xsl:text> parse(handler, *static_cast<const </xsl:text> <xsl:value-of select="DataTypeName"/> <xsl:text>*>(msg.getData( ))) ; break;</xsl:text> </xsl:template> <!--Generate declarations for each message data type --> <xsl:template match="Structure" mode="declare"> <!--Forward declare the message data class --> <xsl:text>class </xsl:text> <xsl:value-of select="Name"/> <xsl:text> ;
</xsl:text> <!--Forward declare the message parse function --> <xsl:text>void parse(MessageHandler & handler, const </xsl:text> <xsl:value-of select="Name"/> <xsl:text>& data);
</xsl:text> </xsl:template> <!--Generate the body of a parser --> <xsl:template match="Structure" mode="parsers"> <xsl:text>void parse(MessageHandler & handler, const </xsl:text> <xsl:value-of select="Name"/> <xsl:text>& data)
</xsl:text> <xsl:text>{
</xsl:text> <xsl:text>&INDENT;handler.beginStruct("</xsl:text> <xsl:value-of select="Name"/> <xsl:text>") ;
</xsl:text> <xsl:for-each select="Members/Member"> <xsl:apply-templates select="key('dataTypes',DataTypeName)" mode="parse"> <xsl:with-param name="name" select="Name"/> </xsl:apply-templates> </xsl:for-each> <xsl:text>&INDENT;handler.endStruct("</xsl:text> <xsl:value-of select="Name"/> <xsl:text>") ;
</xsl:text> <xsl:text>}

</xsl:text> </xsl:template> <!--Nested structures invoke the parser for that structure --> <xsl:template match="Structure" mode="parse"> <xsl:param name="name"/> <xsl:text>&INDENT;parse(handler, data.get_</xsl:text> <xsl:value-of select="$name"/><xsl:text>( ));
</xsl:text> </xsl:template> <!--We assume there is a get function for each --> <!-- primitive component of the message --> <xsl:template match="*" mode="parse"> <xsl:param name="name"/> <xsl:text>&INDENT;handler.field("</xsl:text> <xsl:value-of select="$name"/>","<xsl:text/> <xsl:value-of select="Name"/>",<xsl:text/> <xsl:text>data.get_</xsl:text> <xsl:value-of select="$name"/>( )<xsl:text/> <xsl:text>);
</xsl:text> </xsl:template> </xsl:stylesheet>
It produces parse functions that look like the following code:
void parse(MessageHandler & handler, const AddStockOrderData& data) { handler.beginStruct("AddStockOrderData") ; handler.field("symbol","StkSymbol",data.get_symbol( )); handler.field("quantity","Shares",data.get_quantity( )); handler.field("side","BuyOrSell",data.get_side( )); handler.field("type","OrderType",data.get_type( )); handler.field("price","Real",data.get_price( )); handler.endStruct("AddStockOrderData") ; }