以下的 Ruby 脚本尝试解析标头并构建所需的源代码,以及原始的标头。
#! /usr/bin/env ruby
GLOBS = [
"toto/*.h",
"tutu/*.h",
"tutu/*.hxx"
]
enums = {}
GLOBS.each { |glob|
Dir[glob].each { |header|
enums[header] = File.open(header, 'rb') { |f|
f.read
}.scan(/enum\s+(\w+)\s+\{\s*([^}]+?)\s*\}/m).collect { |enum_name, enum_key_and_values|
[
enum_name, enum_key_and_values.split(/\s*,\s*/).collect { |enum_key_and_value|
enum_key_and_value.split(/\s*=\s*/).first
}
]
}
}
}
require 'erb'
template_h = ERB.new <<-EOS
#ifndef <%= enum_name %>_to_string_h_
#define <%= enum_name %>_to_string_h_ 1
#include "<%= header %>"
char* enum_to_string(<%= enum_name %> e);
#endif
EOS
template_cpp = ERB.new <<-EOS
#include "<%= enum_name %>_to_string.h"
char* enum_to_string(<%= enum_name %> e)
{
switch (e)
{<% enum_keys.each do |enum_key| %>
case <%= enum_key %>: return "<%= enum_key %>";<% end %>
default: return "INVALID <%= enum_name %> VALUE";
}
}
EOS
enums.each { |header, enum_name_and_keys|
enum_name_and_keys.each { |enum_name, enum_keys|
File.open("#{File.dirname(header)}/#{enum_name}_to_string.h", 'wb') { |built_h|
built_h.write(template_h.result(binding))
}
File.open("#{File.dirname(header)}/#{enum_name}_to_string.cpp", 'wb') { |built_cpp|
built_cpp.write(template_cpp.result(binding))
}
}
}
使用正则表达式使得这个“解析器”非常脆弱,它可能无法优雅地处理您特定的标头。
假设您有一个名为toto/a.h的标头,其中包含枚举MyEnum和MyEnum2的定义。脚本将构建:
toto/MyEnum_to_string.h
toto/MyEnum_to_string.cpp
toto/MyEnum2_to_string.h
toto/MyEnum2_to_string.cpp
更加健壮的解决方案可能包括:
- 从另一个源构建定义枚举及其操作的所有源。这意味着您将在 XML/YML/任何易于解析的文件中定义枚举,而不是在 C/C++ 中。
- 使用像 Avdi 建议的真正编译器。
- 使用预处理器宏,带或不带模板。