如何以优雅的方式转储XML



我想将解析后的ttf文件中的一些信息转储到XML文件中。ttf中有几个表,例如cmap、head、hhea。我已经定义了这些表的结构,例如:

class Font_Header{
public:
    FIXED   table_version_number;
    FIXED   font_revision;
    ULONG   checksum_adjustment;
// some other field...
    SHORT   index_to_loc_format;
    SHORT   glygh_data_format;
// some member functions...
};

现在我想写一个名为dump_info的函数来转储这个结构的内存布局。

void Font_Header::dump_info(FILE *fp, size_t indent){
    INDENT(fp, indent); fprintf(fp, "<head>n");
    ++indent;
    INDENT(fp, indent); fprintf(fp, "<tableVersion value="0x%08x"/>n", table_version_number);
// some other lines...
    INDENT(fp, indent); fprintf(fp, "<glyphDataFormat value="%d"/>n", glygh_data_format);
    --indent;
    INDENT(fp, indent); fprintf(fp, "</head>n");
}

我的问题是:

  1. 有没有更好的解决方案来实现这一目标?我已经写了N行来定义结构,现在我必须再写一行N行到dump_info。这不酷。我想要的是:

    foreach field in fields
        dump(indent);
        dumpLn("<$1 value="$2">", field.name, field.value);
        // Fields of different type are dumped in different format!
    end
    
  2. 如何明智地完成缩进?我定义了以下宏

    #define INDENT(fp, indent) for(size_t i = 0; i < (indent); ++i) fprintf((fp), "t")
    

并将此宏附加到每一行。。。我想知道是否有一种优雅的方式来完成这项任务。

如果您只做少数情况,只需手动完成,并接受重复。

如果您只是为lot的简单结构执行此操作,我会考虑使用一个简单的解析器将给定头文件中的结构转换为用于转储结构的源。

如果我想更多地自定义XML输出(这似乎很可能),我会从一些元数据文件中生成结构和结构打印函数。

如果你这样做不仅仅是为了简单的结构,那么我会把我的数据部分包装在一个简单的结构中,并按照上面的方法进行。

例如,你可以使用这样的东西:(我可能会使用XML或JSON元数据,因为它更容易解析——这取决于你想用什么语言编写生成器)

FontHeaderData.crazymeta

struct_name : Font_Header_data
dumper_name : Font_Header_data_to_xml
xml_root_node : head
FIXED table_version_number tableVersionNumber 0x%08x
...

然后你的主文件会像这样:

Font_Header.h

#include "Font_Header_data.h"
#include "Font_Header_data_to_xml.h"
class Font_Header {
    Font_Header_data data;
    void dump_info(FILE *fp, size_t indent){
        Font_Header_data_to_xml(fp,indent);
    }
};

其中Font_Header_data.hFont_Header_data_to_xml.hFont_Header_data_to_xml.c是由您的解析器生成的。

请记住,使用正确的依赖关系将生成的文件挂接到构建过程中,以便在正确的时间重建它们。

"有趣"的部分是将元数据写入.c.h转换器。

在过去,我做过类似的事情,使大量MySQL表与C++表保持同步,并生成了正确的C API INSERT/UPDATE命令来桥接这两者。虽然一开始就要做很多工作,但这无疑为我节省了很多小时的痛苦。

下面的代码是我的尝试。我将类的字段信息存储到一个Notation数组中。我假设ULL,即unsigned long long,是消耗最大内存的类型(8B),并且总是从每个字段的指针中得到8B。需要特别注意的一点是,我应该始终确保在Notation中正确分配了format。例如,如果我想打印一个short值(我只需要2B),但我用"%d"分配了format(为我获取4B),我会得到错误的答案。

两个问题仍然困扰着我:

  1. 该方法不可移植。不同的平台可能会为CCD_ 20分配不同大小的内存
  2. 由于我总是从内存中获得8B,抛开效率不谈,这可能会导致读取内存冲突

好吧,更新。。。我将mask添加到Notation以获得正确的值,即使format没有被正确分配。

请参阅ttf中的OS/2表。它由近40个字段组成。现在我已经写了40行来定义表,40行来读取表,还有40行来转储表的信息!天啊。也许在未来我必须添加其他40行、40行和40行。。。如果我不能自动完成这项任务,就杀了我。


#include <stdio.h>
class X{
public:
  char      a;
  short     b;
  int       c;
  double    d;
  X(char a, short b, int c, double d) : a(a), b(b), c(c), d(d) {}
};
typedef unsigned long long ULL;
#define FIELD(c, x)             (((c*)0)->x)
#define OFFSET(c, x)            ((size_t)&FIELD(c, x))
#define SIZE(c, x)              (sizeof(FIELD(c, x)))
#define MASK(c, x)              (((ULL)~0) >> ((sizeof(ULL) - SIZE(c, x)) << 3))
#define NOTATION(c, x, s)       { SIZE(c, x), OFFSET(c, x), #x, s, MASK(c, x) }
#define PTR(c, f)               ((void*)((size_t)&c + f->offset))
#define VALUE(c, f)             (f->mask & *(ULL*)PTR(x, f))
struct Notation{
  size_t        size;
  size_t        offset;
  const char    *name;
  const char    *format;
  ULL           mask;
};
Notation X_field[] = {
  NOTATION(X, a, "%c"),
  NOTATION(X, b, "%d"),  // The right 'format' of short should be %hd. I intentionally set it wrong.
  NOTATION(X, c, "%d"),
  NOTATION(X, d, "%lf")
};
#define PRINT(x, f, s) 
  printf("name: %s, size: %u, ptr: %p, value: " s "n", #f, sizeof(x.f), &x.f, x.f)
int main(){
  X x('z', 3, 2, 1.5);
  printf("--------------------MANUAL--------------------n");
  PRINT(x, a, "%c");
  PRINT(x, b, "%hd");
  PRINT(x, c, "%d");
  PRINT(x, d, "%lf");
  printf("--------------------MASK--------------------n");
  printf("0x%016hhx, %hhun", (char)~0, (char)~0);
  printf("0x%016hx, %hun", (short)~0, (short)~0);
  printf("0x%016x, %un", (int)~0, (int)~0);
  printf("0x%016llx, %llun", (ULL)~0, (ULL)~0);
  printf("--------------------FIELD--------------------n");
  Notation *field = NULL;
  int i = 0;
  for(i = 0, field = X_field; i < 4; ++i, ++field){
    printf("size: %u, offset: %u, name: %s, format: %s, mask: 0x%016llxn",
      field->size, field->offset, field->name, field->format, field->mask);
  }
  printf("--------------------AUTO--------------------n");
  for(i = 0, field = X_field; i < 4; ++i, ++field){
    printf("name: %s, size: %u, ptr: %p, value: ", field->name, field->size, PTR(x, field));
    printf(field->format, VALUE(x, field));
    printf("n");
  }
  return 0;
}

输出:

--------------------MANUAL--------------------
name: a, size: 1, ptr: 0x22ac18, value: z
name: b, size: 2, ptr: 0x22ac1a, value: 3
name: c, size: 4, ptr: 0x22ac1c, value: 2
name: d, size: 8, ptr: 0x22ac20, value: 1.500000
--------------------MASK--------------------
0x00000000000000ff, 255
0x000000000000ffff, 65535
0x00000000ffffffff, 4294967295
0xffffffffffffffff, 18446744073709551615
--------------------FIELD--------------------
size: 1, offset: 0, name: a, format: %c, mask: 0x00000000000000ff
size: 2, offset: 2, name: b, format: %d, mask: 0x000000000000ffff
size: 4, offset: 4, name: c, format: %d, mask: 0x00000000ffffffff
size: 8, offset: 8, name: d, format: %lf, mask: 0xffffffffffffffff
--------------------AUTO--------------------
name: a, size: 1, ptr: 0x22ac18, value: z
name: b, size: 2, ptr: 0x22ac1a, value: 3
name: c, size: 4, ptr: 0x22ac1c, value: 2
name: d, size: 8, ptr: 0x22ac20, value: 1.500000

最新更新