使用 XML/XSLT 与 "C 预处理器"






3.52/5 (6投票s)
如何滥用 XML/XSL 来做即使是
引言
这很可能是在这里最不受欢迎的文章。它是由 "编码噩梦" 帖子引发的 论坛
(本文中的示例实际上比发布的示例更简单。)
它唯一的优点在于如果有人从中了解了一些关于 XML 和 XSLT 的知识。
背景
纯粹的 C 有一个 .NET(特别是 Common Type System)没有的限制。在这方面,这篇文章根本不应该出现在 Code Project 上,它实际上与 .NET 无关。
这种特殊的限制与 enum
有关。在 .NET 中,获取枚举值的文本表示非常简单。在这个 C# 例子中,我定义了一个 enum
,将它的一个值分配给一个变量,并将其显示到控制台。请注意,我实际上必须努力才能获得 enum
成员的数值。
CSenum.cs
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 ) ;
}
}
}
结果C:\>CSenum Hello 1 says "Hello" Bob says "Hello"
现在这里有一个 C 版本,它演示了这个限制。枚举值无法转换为其文本表示。Cenum1.c
# include
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 ) ;
}
结果
C:\>Cenum1 Hello
1 says "Hello"
所以,我们要做的是添加一个字符串数组
Cenum2.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 ) ;
}
结果
C:\>Cenum2 Hello
1 says "Hello"
Bob says "Hello"
通常,enum
、字符串数组和 struct
的定义将在一个或多个头文件 (h) 中。目前,我只分离出 enum
和字符串数组。
Cenum3.h
typedef enum
{
Abe
,
Bob
,
Cat
,
Deb
,
MAX /* Helps to bounds-check the value */
} UserEnum ;
char *UserNames[] =
{
"Abe"
,
"Bob"
,
"Cat"
,
"Deb"
} ;
在 C 文件中,在删除以上内容后,添加
来自 Cenum3.c
# include "Cenum3.h"
将此类定义从使用它们的代码中分离出来,可以更容易地维护 C 程序。但我们剩下两个相关的定义,必须一起维护。可能出现的一些潜在问题是
- 向
enum
添加一个值,但未添加到数组 - 将一个字符串添加到数组中的错误位置
显然,找到一种只维护 一个 列表的方法是可取的。Coding Horrors 帖子中讨论的代码就是这样一种方法,并且它有效,但它相当可怕。
使用 XML 和 XSLT 达到同样的结果
警告:我即将描述的技术只是“稍微”不那么可怕。C 纯粹主义者不应该继续。
我将使用 XML 来存储列表。这样的 XML 文档几乎可以按照你喜欢的方式编写(遵守格式良好的规则)。
格式良好的 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>
我更喜欢第一个,如果仅仅是出于简洁的原因。第三个示例中使用属性似乎也普遍不受欢迎。这样的文件可能是数据库查询或其他进程的输出。
现在我们想要编写一个 XSL 文档,将 XML 文档转换为 Cenum3.h(结果头文件的确切格式并不重要,只要编译器理解它即可。)
Cenum3.xsl
<?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>"<xsl:value-of
select="name()"/>"
</xsl:for-each>} ;
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
我不会详细介绍 XSL 的作用;我只想说它会迭代 User 元素的子元素两次。首先它创建 enum
的定义,然后它创建字符串数组的定义。
在此处学习 XSL here。
结果 Cenum3.h
typedef enum {
Abe,
Bob,
Cat,
Deb,
MAX} UserEnum ;
char *UserNames[] = {
"Abe"
,"Bob"
,"Cat"
,"Deb"
} ;
现在你可能会问自己,“我怎么到这里的?” 意思是,“有什么实用程序可以获取 XML 和 XSL 并实际执行转换?” 好吧,这里有一个非常简单的控制台程序来做到这一点
SimpleXSLT.cs
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 ) ;
}
}
}
结果
C:\>SimpleXSLT Cenum3.xml Cenum3.xsl
typedef enum {
Abe,
Bob,
Cat,
Deb,
MAX} UserEnum ;
char *UserNames[] = {
"Abe"
,"Bob"
,"Cat"
,"Deb"
} ;
(你可以将输出重定向到一个文件。)
C:\>SimpleXSLT Cenum3.xml Cenum3.xsl > Cenum3.h
历史
2007-05-02 原始版本