Click here to Skip to main content
15,867,307 members
Articles / Programming Languages / XSLT
Article

Using XML/XSLT with the "C Preprocessor"

Rate me:
Please Sign up or sign in to vote.
3.52/5 (6 votes)
7 May 2007CPOL3 min read 40.8K   75   7   6
How to misuse XML/XSL to do what not even the

Introduction

This is likely going to be the least popular article here. It was prompted by a posting in the "Coding horrors" forum

(The example in this article is actually simpler than the one posted.)

Its only saving grace will be if anyone learns a little about XML and XSLT from it.

Background

Plain old C has a limitation that .NET (in particular, the Common Type System) doesn't have. In this respect, this article shouldn't be on Code Project at all, it doesn't really relate to .NET.

This particular limitation concerns enums. In .NET, getting the textual representation of an enumerated value is simple. In this C# example I define an enum, assign one of its values to a variable, and display it to the console. Note that I actually have to make an effort to get the numeric value of the enum member.

CSenum.cs

C#
namespace CSenum
{
    public partial class
    CSenum
    {
        public enum UserEnum
        {
            Abe
        ,
            Bob
        ,
            Cat
        ,
            Deb
        }

        public struct
        Message
        {
            public UserEnum From ;
            public string Text ;
        }

        [System.STAThreadAttribute]
        public static int
        Main
        (
            string[] args
        )
        {
            try
            {
                if ( args.Length > 0 )
                {
                    Message m ;

                    m.From = UserEnum.Bob ;
                    m.Text = args [ 0 ] ;

                    System.Console.WriteLine ( "{0} says \"{1}\"" , 
                        (int) m.From , m.Text ) ;
                    System.Console.WriteLine ( "{0} says \"{1}\"" , 
                        m.From , m.Text ) ;
                }
            }
            catch ( System.Exception err )
            {
                System.Console.Write ( err.Message ) ;
            }

            return ( 0 ) ;
        }
    }
}
Result:
C:\>CSenum Hello
1 says "Hello"
Bob says "Hello"

Now here's a C version that demonstrates the limitation. The enumerated value can't be converted to its textual representation. Cenum1.c

C
# include <stdio.h />

typedef enum
{
    Abe
,
    Bob
,
    Cat
,
    Deb
,
    MAX  /* Helps to bounds-check the value */
} UserEnum ;

typedef struct
{
    UserEnum From ;
    char*    Text ;
} Message ;

int
main
(
    int   argc
,
    char* argv[]
)
{
    int result = 0 ;

    if ( argc > 1 )
    {
        Message m ;

        m.From = Bob ;
        m.Text = argv [ 1 ] ;
       
        printf ( "%d says \"%s\"\n" , m.From , m.Text ) ;

        /* This line would cause an error */
//      printf ( "%s says \"%s\"\n" , m.From , m.Text ) ;
    }

    return ( result ) ;
}
</stdio.h />

Result:

C:\>Cenum1 Hello
1 says "Hello"

So, what we do is add an array of strings:

Cenum2.c

C++
# include <stdio.h>

typedef enum
{
    Abe
,
    Bob
,
    Cat
,
    Deb
,
    MAX  /* Helps to bounds-check the value */
} UserEnum ;

char *UserNames[] =
{
    "Abe"
,
    "Bob"
,
    "Cat"
,
    "Deb"
} ;

typedef struct
{
    UserEnum From ;
    char*    Text ;
} Message ;

int
main
(
    int   argc
,
    char* argv[]
)
{
    int result = 0 ;

    if ( argc > 1 )
    {
        Message m ;

        m.From = Bob ;
        m.Text = argv [ 1 ] ;
       
        printf ( "%d says \"%s\"\n" , m.From , m.Text ) ;

        /* This line would cause an error */
//      printf ( "%s says \"%s\"\n" , m.From , m.Text ) ;

        /* We use the numeric value as an index into the array of strings */
        printf ( "%s says \"%s\"\n" , UserNames [ m.From ] , m.Text ) ;
    }

    return ( result ) ;
}

Result:

C:\>Cenum2 Hello
1 says "Hello"
Bob says "Hello"

Ordinarily the definitions of the enum, the array of strings, and the struct would be in one or more header (h) files. Currently, I'll only split out the enum and the array of strings.

Cenum3.h

C++
typedef enum
{
    Abe
,
    Bob
,
    Cat
,
    Deb
,
    MAX  /* Helps to bounds-check the value */
} UserEnum ;

char *UserNames[] =
{
    "Abe"
,
    "Bob"
,
    "Cat"
,
    "Deb"
} ;

In the C file, after removing the above, add:

From Cenum3.c

C++
# include "Cenum3.h"

Breaking such definitions from the code that uses them allows for easier maintenance of C programs. But we're left with two definitions that are related and must be maintained together. Some potential problems that can arise are:

  1. Adding a value to the enum, but not to the array
  2. Adding a string to the array in the wrong place

Obviously, finding a way of having only one list to maintain is desirable. The code that is the topic of the Coding Horrors post is one such way, and it works, but it's rather horrific.

Using XML and XSLT to achieve the same result

WARNING: The technique I'm about to describe is only 'somewhat' less horrific. C purists shouldn't proceed.

I'll use XML to store the list. Such an XML document can pretty much be written any way you like (obeying the rules of well-formedness).

Well-formed XML documents include:

XML
<User>
  <Abe/>
  <Bob/>
  <Cat/>
  <Deb/>
</User>

<Enum>
  <Name>User</Name>
  <Value>Abe</Value>
  <Value>Bob</Value>
  <Value>Cat</Value>
  <Value>Deb</Value>
</Enum>

<Enum Name="User">
  <Value Name="Abe"/>
  <Value Name="Bob"/>
  <Value Name="Cat"/>
  <Value Name="Deb"/>
</Enum>

I prefer the first of these, if for no other reason than brevity. There also appears to be a general dislike of attributes as used in the third example. Such a file may be the output from a database query or other process.

Now we want to write an XSL document to transform the XML document into Cenum3.h (The exact format of the resultant header file won't be very important as long as the compiler understands it.)

Cenum3.xsl

XML
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
  <xsl:output omit-xml-declaration="yes" method="text"/>
  <xsl:template match="/">
    <xsl:for-each select="*">
      typedef enum {
      <xsl:for-each select="*">
    <xsl:value-of select="name()"/>,
        <xsl:if test="position()=last()">MAX</xsl:if>
      </xsl:for-each>} <xsl:value-of select="name()"/>Enum ;
      char *<xsl:value-of select="name()"/>Names[] = {
      <xsl:for-each select="*">
    <xsl:if test="position()!=1">,</xsl:if>&quot;<xsl:value-of 
      select="name()"/>&quot;
      </xsl:for-each>} ;
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

I won't go into detail as to what the XSL does; I'll just say that it iterates through the children of the User element twice. First it creates the definition of the enum, then it creates the definition of the string array.

Learn XSL here.

Resultant Cenum3.h

typedef enum {
Abe,
  Bob,
  Cat,
  Deb,
  MAX} UserEnum ;
char *UserNames[] = {
"Abe"
,"Bob"
,"Cat"
,"Deb"
} ;

Now you may ask yourself, "How did I get here?" By which I mean, "What utility exists to take the XML and the XSL and actually perform the transform?" Well, here's a very simple console program to do it:

SimpleXSLT.cs

C#
namespace SimpleXSLT
{
    public partial class SimpleXSLT
    {
        [System.STAThreadAttribute]
        public static int
        Main
        (
            string[] args
        )
        {
            int result = 0 ;

            try
            {
                System.Xml.Xsl.XslCompiledTransform xslt = new 
                    System.Xml.Xsl.XslCompiledTransform() ;

                if ( args.Length == 2 )
                {
                    xslt.Load ( args [ 1 ] ) ;

                    xslt.Transform
                    (
                        args [ 0 ]
                    ,
                        null
                    ,
                        System.Console.Out
                    ) ;
                }
                else
                {
                    System.Console.Write ( "Syntax: XSLT xmlfile xslfile" ) ;
                } 
            }
            catch ( System.Exception err )
            {
                System.Console.Write ( err.Message ) ;
            }

            return ( result ) ;
        }
    }
}

Result:

C:\>SimpleXSLT Cenum3.xml Cenum3.xsl

      typedef enum {
      Abe,
        Bob,
        Cat,
        Deb,
        MAX} UserEnum ;
      char *UserNames[] = {
      "Abe"
      ,"Bob"
      ,"Cat"
      ,"Deb"
      } ;

(And you may redirect the output to a file.)

C:\>SimpleXSLT Cenum3.xml Cenum3.xsl > Cenum3.h

History

2007-05-02 Original version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
BSCS 1992 Wentworth Institute of Technology

Originally from the Boston (MA) area. Lived in SoCal for a while. Now in the Phoenix (AZ) area.

OpenVMS enthusiast, ISO 8601 evangelist, photographer, opinionated SOB, acknowledged pedant and contrarian

---------------

"I would be looking for better tekkies, too. Yours are broken." -- Paul Pedant

"Using fewer technologies is better than using more." -- Rico Mariani

"Good code is its own best documentation. As you’re about to add a comment, ask yourself, ‘How can I improve the code so that this comment isn’t needed?’" -- Steve McConnell

"Every time you write a comment, you should grimace and feel the failure of your ability of expression." -- Unknown

"If you need help knowing what to think, let me know and I'll tell you." -- Jeffrey Snover [MSFT]

"Typing is no substitute for thinking." -- R.W. Hamming

"I find it appalling that you can become a programmer with less training than it takes to become a plumber." -- Bjarne Stroustrup

ZagNut’s Law: Arrogance is inversely proportional to ability.

"Well blow me sideways with a plastic marionette. I've just learned something new - and if I could award you a 100 for that post I would. Way to go you keyboard lovegod you." -- Pete O'Hanlon

"linq'ish" sounds like "inept" in German -- Andreas Gieriet

"Things would be different if I ran the zoo." -- Dr. Seuss

"Wrong is evil, and it must be defeated." –- Jeff Ello

"A good designer must rely on experience, on precise, logical thinking, and on pedantic exactness." -- Nigel Shaw

“It’s always easier to do it the hard way.” -- Blackhart

“If Unix wasn’t so bad that you can’t give it away, Bill Gates would never have succeeded in selling Windows.” -- Blackhart

"Use vertical and horizontal whitespace generously. Generally, all binary operators except '.' and '->' should be separated from their operands by blanks."

"Omit needless local variables." -- Strunk... had he taught programming

Comments and Discussions

 
GeneralHummmm Pin
Miguel Barros30-Aug-09 14:19
Miguel Barros30-Aug-09 14:19 
GeneralRe: Hummmm Pin
PIEBALDconsult31-Aug-09 6:21
mvePIEBALDconsult31-Aug-09 6:21 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.