// Copyright (C) 2017-2022 Jonathan Müller and cppast contributors // SPDX-License-Identifier: MIT /// \file /// Serialization code generation. /// /// Given an input file, it will generate a serialize() function for each class marked with /// [[generate::serialize]]. #include #include #include #include #include #include "example_parser.hpp" // whether or not a type is a C string, i.e. char pointer bool is_c_string(const cppast::cpp_type& type) { if (type.kind() != cppast::cpp_type_kind::pointer_t) return false; // get the pointee auto& pointee = cppast::remove_cv(static_cast(type).pointee()); if (pointee.kind() != cppast::cpp_type_kind::builtin_t) return false; // check the builtin type kind auto builtin = static_cast(pointee).builtin_type_kind(); return builtin == cppast::cpp_char || builtin == cppast::cpp_char16 || builtin == cppast::cpp_char32 || builtin == cppast::cpp_wchar; } // generate a serialization call for a member void generate_serialize_member(std::ostream& out, const cppast::cpp_member_variable& member) { auto& type = cppast::remove_cv(member.type()); if (cppast::has_attribute(member, "generate::transient")) // don't serialize transient members return; else if (auto attr = cppast::has_attribute(member, "generate::serialize")) { // generate code as specified by the attributes out << " " << attr.value().arguments().value().as_string() << ";\n"; } else if (type.kind() == cppast::cpp_type_kind::builtin_t) { // generate hypothetical member function call for builtin types out << " s.serialize(obj." << member.name() << ");\n"; } else if (type.kind() == cppast::cpp_type_kind::user_defined_t) { // generate ADL call out << " serialize(s, obj." << member.name() << ");\n"; } else if (is_c_string(type)) { // generate another hypothetical member function call out << " s.serialize_string(obj." << member.name() << ");\n"; } else throw std::invalid_argument("cannot serialize member " + member.name()); } // generate serialization function void generate_serialize(const cppast::cpp_file& file) { cppast::visit( file, [](const cppast::cpp_entity& e) { // only visit non-templated class definitions that have the attribute set return (!cppast::is_templated(e) && e.kind() == cppast::cpp_entity_kind::class_t && cppast::is_definition(e) && cppast::has_attribute(e, "generate::serialize")) // or all namespaces || e.kind() == cppast::cpp_entity_kind::namespace_t; }, [](const cppast::cpp_entity& e, const cppast::visitor_info& info) { if (e.kind() == cppast::cpp_entity_kind::class_t && !info.is_old_entity()) { auto& class_ = static_cast(e); std::cout << "inline void serialize(const foo::serializer& s, const " << class_.name() << "& obj) {\n"; // serialize base classes for (auto& base : class_.bases()) if (!cppast::has_attribute(base, "generate::transient")) std::cout << " serialize(s, static_cast(obj));\n"; // serialize member variables for (auto& member : class_) { if (member.kind() == cppast::cpp_entity_kind::member_variable_t) generate_serialize_member(std::cout, static_cast( member)); } std::cout << "}\n\n"; } else if (e.kind() == cppast::cpp_entity_kind::namespace_t) { if (info.event == cppast::visitor_info::container_entity_enter) // open namespace std::cout << "namespace " << e.name() << " {\n\n"; else // if (info.event == cppast::visitor_info::container_entity_exit) // close namespace std::cout << "}\n"; } }); } int main(int argc, char* argv[]) { return example_main(argc, argv, {}, generate_serialize); }