diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 49b01a2..2f83c21 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -8,6 +8,7 @@ function(_cppast_example name) endfunction() _cppast_example(ast_printer) +_cppast_example(comparison) _cppast_example(documentation_generator) _cppast_example(enum_to_string) _cppast_example(serialization) diff --git a/example/comparison.cpp b/example/comparison.cpp new file mode 100644 index 0000000..f2a0468 --- /dev/null +++ b/example/comparison.cpp @@ -0,0 +1,117 @@ +// 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 +/// Generate equality comparisons. + +#include +#include + +#include +#include +#include + +#include "example_parser.hpp" + +bool has_token(const cppast::cpp_token_string& str, const char* token) +{ + auto iter = std::find_if(str.begin(), str.end(), + [&](const cppast::cpp_token& tok) { return tok.spelling == token; }); + return iter != str.end(); +} + +void generate_op_equal(std::ostream& out, const cppast::cpp_class& c) +{ + out << "inline bool operator==(const " << c.name() << "& lhs, const " << c.name() + << "& rhs) {\n"; + out << " return "; + + auto first = true; + + for (auto& base : c.bases()) + { + if (cppast::has_attribute(base, "generate::transient")) + continue; + + if (first) + first = false; + else + out << " && "; + out << "static_cast(lhs) == static_cast(rhs)\n"; + } + + for (auto& member : c) + if (member.kind() == cppast::cpp_entity_kind::member_variable_t + && !cppast::has_attribute(member, "generate::transient")) + { + // generate comparison code for non-transient member variables + if (first) + first = false; + else + out << " && "; + out << "lhs." << member.name() << " == " + << "rhs." << member.name() << "\n"; + } + + out << " ;\n"; + out << "}\n\n"; +} + +void generate_op_non_equal(std::ostream& out, const cppast::cpp_class& c) +{ + out << "inline bool operator!=(const " << c.name() << "& lhs, const " << c.name() + << "& rhs) {\n"; + out << " return !(lhs == rhs);\n"; + out << "}\n\n"; +} + +void generate_comparison(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::comparison")) + // 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); + auto& attribute = + cppast::has_attribute(e, "generate::comparison").value(); + + if (attribute.arguments()) + { + if (has_token(attribute.arguments().value(), "==")) + generate_op_equal(std::cout, class_); + else if (has_token(attribute.arguments().value(), "!=")) + generate_op_non_equal(std::cout, class_); + } + else + { + generate_op_equal(std::cout, class_); + generate_op_non_equal(std::cout, class_); + } + } + 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_comparison); +} diff --git a/example/enum_to_string.cpp b/example/enum_to_string.cpp index 77065ff..02e0d0b 100644 --- a/example/enum_to_string.cpp +++ b/example/enum_to_string.cpp @@ -30,7 +30,8 @@ void generate_to_string(const cppast::cpp_file& file) auto& enum_ = static_cast(e); // write function header - std::cout << "const char* to_string(const " << enum_.name() << "& e) {\n"; + std::cout << "inline const char* to_string(const " << enum_.name() + << "& e) {\n"; // generate switch std::cout << " switch (e) {\n"; diff --git a/example/serialization.cpp b/example/serialization.cpp index af07bcf..ec7d7f3 100644 --- a/example/serialization.cpp +++ b/example/serialization.cpp @@ -77,13 +77,14 @@ void generate_serialize(const cppast::cpp_file& file) { auto& class_ = static_cast(e); - std::cout << "void serialize(const foo::serializer& s, const " + std::cout << "inline 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"; + if (!cppast::has_attribute(base, "generate::transient")) + std::cout << " serialize(s, static_cast(obj));\n"; // serialize member variables for (auto& member : class_) diff --git a/include/cppast/visitor.hpp b/include/cppast/visitor.hpp index 9f659ce..72f4f11 100644 --- a/include/cppast/visitor.hpp +++ b/include/cppast/visitor.hpp @@ -182,6 +182,7 @@ namespace cppast else return continue_visit; } + return continue_visit; }); }