diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index a831176..49b01a2 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -10,3 +10,4 @@ endfunction() _cppast_example(ast_printer) _cppast_example(documentation_generator) _cppast_example(enum_to_string) +_cppast_example(serialization) diff --git a/example/enum_to_string.cpp b/example/enum_to_string.cpp index ec1428a..77065ff 100644 --- a/example/enum_to_string.cpp +++ b/example/enum_to_string.cpp @@ -16,8 +16,9 @@ void generate_to_string(const cppast::cpp_file& file) { cppast::visit(file, [](const cppast::cpp_entity& e) { - // only visit enums that have the attribute set + // only visit enum definitions that have the attribute set return (e.kind() == cppast::cpp_entity_kind::enum_t + && cppast::is_definition(e) && cppast::has_attribute(e, "generate::to_string")) // or all namespaces || e.kind() == cppast::cpp_entity_kind::namespace_t; diff --git a/example/example_parser.hpp b/example/example_parser.hpp index ab200a7..b91c12c 100644 --- a/example/example_parser.hpp +++ b/example/example_parser.hpp @@ -13,7 +13,7 @@ // parses all files in that directory // and invokes the callback for each of them template -int example_main(int argc, char* argv[], const cppast::cpp_entity_index& index, Callback cb) +int example_main(int argc, char* argv[], const cppast::cpp_entity_index& index, Callback cb) try { if (argc != 2) { @@ -47,5 +47,10 @@ int example_main(int argc, char* argv[], const cppast::cpp_entity_index& index, return 0; } +catch (std::exception& ex) +{ + std::cerr << ex.what() << '\n'; + return 1; +} #endif // CPPAST_EXAMPLE_PARSER_HPP_INCLUDED diff --git a/example/serialization.cpp b/example/serialization.cpp new file mode 100644 index 0000000..af07bcf --- /dev/null +++ b/example/serialization.cpp @@ -0,0 +1,115 @@ +// Copyright (C) 2017 Jonathan Müller +// This file is subject to the license terms in the LICENSE file +// found in the top-level directory of this distribution. + +/// \file +/// Serialization code generation. + +#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; + + auto& pointee = cppast::remove_cv(static_cast(type).pointee()); + if (pointee.kind() != cppast::cpp_type_kind::builtin_t) + return false; + + 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; +} + +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()); +} + +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 << "void serialize(const foo::serializer& s, const " + << class_.name() << "& obj) {\n"; + + // serialize base classes + for (auto& base : class_.bases()) + 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< + const cppast::cpp_member_variable&>( + 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); +} diff --git a/include/cppast/cpp_type.hpp b/include/cppast/cpp_type.hpp index 8325baa..9bf3e02 100644 --- a/include/cppast/cpp_type.hpp +++ b/include/cppast/cpp_type.hpp @@ -74,17 +74,13 @@ namespace cppast } protected: - cpp_type() noexcept : user_data_(nullptr) - { - } + cpp_type() noexcept : user_data_(nullptr) {} private: /// \returns The [cppast::cpp_type_kind](). virtual cpp_type_kind do_get_kind() const noexcept = 0; - void on_insert(const cpp_type&) - { - } + void on_insert(const cpp_type&) {} mutable std::atomic user_data_; @@ -112,9 +108,7 @@ namespace cppast } private: - cpp_unexposed_type(std::string name) : name_(std::move(name)) - { - } + cpp_unexposed_type(std::string name) : name_(std::move(name)) {} cpp_type_kind do_get_kind() const noexcept override { @@ -180,9 +174,7 @@ namespace cppast } private: - cpp_builtin_type(cpp_builtin_type_kind kind) : kind_(kind) - { - } + cpp_builtin_type(cpp_builtin_type_kind kind) : kind_(kind) {} cpp_type_kind do_get_kind() const noexcept override { @@ -224,9 +216,7 @@ namespace cppast } private: - cpp_user_defined_type(cpp_type_ref entity) : entity_(std::move(entity)) - { - } + cpp_user_defined_type(cpp_type_ref entity) : entity_(std::move(entity)) {} cpp_type_kind do_get_kind() const noexcept override { @@ -361,6 +351,15 @@ namespace cppast cpp_cv cv_; }; + /// \returns The type without top-level const/volatile qualifiers. + const cpp_type& remove_cv(const cpp_type& type) noexcept; + + /// \returns The type without top-level const qualifiers. + const cpp_type& remove_const(const cpp_type& type) noexcept; + + /// \returns The type without top-level volatile qualifiers. + const cpp_type& remove_volatile(const cpp_type& type) noexcept; + /// A pointer to a [cppast::cpp_type](). class cpp_pointer_type final : public cpp_type { @@ -378,9 +377,7 @@ namespace cppast } private: - cpp_pointer_type(std::unique_ptr pointee) : pointee_(std::move(pointee)) - { - } + cpp_pointer_type(std::unique_ptr pointee) : pointee_(std::move(pointee)) {} cpp_type_kind do_get_kind() const noexcept override { diff --git a/src/cpp_type.cpp b/src/cpp_type.cpp index 10636f5..a624898 100644 --- a/src/cpp_type.cpp +++ b/src/cpp_type.cpp @@ -76,6 +76,39 @@ const char* cppast::to_string(cpp_builtin_type_kind kind) noexcept return "__ups"; } +const cpp_type& cppast::remove_cv(const cpp_type& type) noexcept +{ + if (type.kind() == cpp_type_kind::cv_qualified_t) + { + auto& cv = static_cast(type); + return cv.type(); + } + + return type; +} + +const cpp_type& cppast::remove_const(const cpp_type& type) noexcept +{ + if (type.kind() == cpp_type_kind::cv_qualified_t) + { + auto& cv = static_cast(type); + if (is_const(cv.cv_qualifier())) + return cv.type(); + } + return type; +} + +const cpp_type& cppast::remove_volatile(const cpp_type& type) noexcept +{ + if (type.kind() == cpp_type_kind::cv_qualified_t) + { + auto& cv = static_cast(type); + if (is_volatile(cv.cv_qualifier())) + return cv.type(); + } + return type; +} + bool detail::cpp_type_ref_predicate::operator()(const cpp_entity& e) { switch (e.kind()) @@ -559,13 +592,9 @@ std::string detail::to_string(const cpp_type& type) } private: - void do_indent() override - { - } + void do_indent() override {} - void do_unindent() override - { - } + void do_unindent() override {} void do_write_token_seq(string_view tokens) override {