C++構造体のXmlシリアライザを自動生成したい
C#だったら属性を書くだけで済むけれど、C++はそういうわけにはいかないのでXMLで定義したフォーマットからコードを生成するスクリプトを書いた。
まずはデータフォーマットをXmlで表記する。
<?xml version='1.0' encoding='UTF-8'?> <Root> <Type name="Scene"> <Type name="Material"> <Type name="Property"> <value name="Name" type="String"/> <value name="Value" type="Float[]"/> </Type> <value name="Name" type="String"/> <value name="Properties" type="Property{}"/> </Type> <Type name="Mesh"> <Type name="Vertex"> <value name="Position" type="Float[]"/> </Type> <value name="Name" type="String"/> <value name="Vertices" type="Vertex[]"/> <value name="Indices" type="Integer[]"/> </Type> <Type name="Model"> <value name="Name" type="String"/> <value name="Material" type="String"/> <value name="Mesh" type="String"/> </Type> <value name="Name" type="String"/> <value name="Materials" type="Material{}"/> <value name="Meshes" type="Mesh{}"/> <value name="Models" type="Model{}"/> </Type> </Root>
struct Scene { struct Material { struct Property { std::string name; std::vector< float > value; }; std::string name; std::map< std::string, Property > properties; }; struct Mesh { struct Vertex { std::vector< float > position; }; std::string name; std::vector< Vertex > vertices; std::vector< int > indices; }; struct Model { std::string name; std::string material; std::string mesh; }; std::string name; std::map< std::string, Material > materials; std::map< std::string, Mesh > meshes; std::map< std::string, Model > models; }; void serialize(tinyxml2::XMLElement* out_xml, const Scene::Material::Property& in_value); void deserialize(Scene::Material::Property& out_value, const tinyxml2::XMLElement* in_xml); void serialize(tinyxml2::XMLElement* out_xml, const Scene::Material& in_value); void deserialize(Scene::Material& out_value, const tinyxml2::XMLElement* in_xml); void serialize(tinyxml2::XMLElement* out_xml, const Scene::Mesh::Vertex& in_value); void deserialize(Scene::Mesh::Vertex& out_value, const tinyxml2::XMLElement* in_xml); void serialize(tinyxml2::XMLElement* out_xml, const Scene::Mesh& in_value); void deserialize(Scene::Mesh& out_value, const tinyxml2::XMLElement* in_xml); void serialize(tinyxml2::XMLElement* out_xml, const Scene::Model& in_value); void deserialize(Scene::Model& out_value, const tinyxml2::XMLElement* in_xml); void serialize(tinyxml2::XMLElement* out_xml, const Scene& in_value); void deserialize(Scene& out_value, const tinyxml2::XMLElement* in_xml);
で、次のようなデータを読み込むことが可能になり
<?xml version='1.0' encoding='UTF-8'?> <Scene> <Name>PRIMITIVES</Name> <Materials> <Material> <Name>RED</Name> <Properties> <Property> <Name>Diffuse</Name> <value> <Float>1.0</Float> <Float>0.0</Float> <Float>0.0</Float> <Float>1.0</Float> </value> </Property> </Properties> </Material> </Materials> <Meshes> <Mesh> <Name>TRIANGLE</Name> <Vertices> <Vertex> <Position> <Float>0.0</Float> <Float>1.0</Float> <Float>0.0</Float> </Position> <Position> <Float>-0.866</Float> <Float>-0.5</Float> <Float>0.0</Float> </Position> <Position> <Float>0.866</Float> <Float>-0.5</Float> <Float>0.0</Float> </Position> </Vertex> </Vertices> <Indices> <Uint>0</Uint> <Uint>1</Uint> <Uint>2</Uint> </Indices> </Mesh> </Meshes> <Models> <Model> <Name>TRIANGLE_RED</Name> <Material>RED</Material> <Mesh>TRIANGLE</Mesh> </Model> </Models> </Scene>
次のように使う。
int main() { Scene scene; // 読み込み tinyxml2::XMLDocument doc; doc.LoadFile("deserialize_test.xml"); deserialize(scene, doc.FirstChildElement("Scene")); // 書き込み tinyxml2::XMLDocument doc2; auto* elem = doc2.NewElement("Scene"); doc2.InsertFirstChild(elem); serialize(elem, scene); doc2.SaveFile("serialize_test.xml"); return 0; }
おとなしくflatbuffersを使ってね