From c886ef31e4c23f404f1069bf7fdc873806614bf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20M=C3=BCller?= Date: Tue, 14 Nov 2017 18:28:24 +0100 Subject: [PATCH] Add example documentation Fixes #13. --- example/README.md | 36 +++++++++++++++++++++++++++++ example/ast_printer.cpp | 2 ++ example/comparison.cpp | 12 ++++++++++ example/documentation_generator.cpp | 2 ++ example/enum_category.cpp | 10 +++++++- example/enum_to_string.cpp | 3 +++ example/serialization.cpp | 6 +++++ 7 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 example/README.md diff --git a/example/README.md b/example/README.md new file mode 100644 index 0000000..87e15a6 --- /dev/null +++ b/example/README.md @@ -0,0 +1,36 @@ +# cppast - examples + +This directory contains example tools written using cppast. + +***Note:** These are not meant to be production ready tools, just a proof of concept!* + +All example executables get a single parameter, +which is the directory where a `compile_commands.json` file is located. +This file is a compilation database, you can read more about it [here](https://clang.llvm.org/docs/JSONCompilationDatabase.html). +CMake, for example, can generate on when the option `CMAKE_EXPORT_COMPILE_COMMANDS` is `ON`. + +The tools will parse each file in the database and process it. +Output will be written to `stdout`. + +## List of examples: + +### `ast_printer.cpp` + +It is a very simplified implementation of the cppast tool, it will print an AST. +This is the starting example, it showcases entity visitation. + +### `documentation_generator.cpp` + +It is a very simplified documentation generator. +This showcases usage of the `cppast::code_generator`. + +### Attributes example + +* `comparison.cpp` +* `documentation_generator.cpp` +* `enum_category.cpp` +* `enum_to_string.cpp` +* `serialization.cpp` + +Those examples were created as a part of my talk [Fun With (User-Defined) Attributes](http://foonathan.net/meetingcpp2017.html). +Check the talk video to learn more about them. diff --git a/example/ast_printer.cpp b/example/ast_printer.cpp index a06eb31..265669e 100644 --- a/example/ast_printer.cpp +++ b/example/ast_printer.cpp @@ -4,6 +4,8 @@ /// \file /// This is a very primitive version of the cppast tool. +/// +/// Given an input file it will print the AST. #include // visit() diff --git a/example/comparison.cpp b/example/comparison.cpp index 1138571..d566df0 100644 --- a/example/comparison.cpp +++ b/example/comparison.cpp @@ -4,6 +4,8 @@ /// \file /// Generate equality comparisons. +/// +/// Given an input file, it will generate comparison operators for each class that has the [[generate::comparison]] attribute. #include #include @@ -14,6 +16,7 @@ #include "example_parser.hpp" +// whether or not the token string contains the given token bool has_token(const cppast::cpp_token_string& str, const char* token) { auto iter = std::find_if(str.begin(), str.end(), @@ -21,6 +24,7 @@ bool has_token(const cppast::cpp_token_string& str, const char* token) return iter != str.end(); } +// generates equality operator for a class void generate_op_equal(std::ostream& out, const cppast::cpp_class& c) { out << "inline bool operator==(const " << c.name() << "& lhs, const " << c.name() @@ -29,9 +33,11 @@ void generate_op_equal(std::ostream& out, const cppast::cpp_class& c) auto first = true; + // compare bases for (auto& base : c.bases()) { if (cppast::has_attribute(base, "generate::transient")) + // if they are not marked not to be compared continue; if (first) @@ -42,6 +48,7 @@ void generate_op_equal(std::ostream& out, const cppast::cpp_class& c) << "&>(rhs)\n"; } + // compare members for (auto& member : c) if (member.kind() == cppast::cpp_entity_kind::member_variable_t && !cppast::has_attribute(member, "generate::transient")) @@ -59,14 +66,17 @@ void generate_op_equal(std::ostream& out, const cppast::cpp_class& c) out << "}\n\n"; } +// generate non equality operator for a class void generate_op_non_equal(std::ostream& out, const cppast::cpp_class& c) { + // just forwards out << "inline bool operator!=(const " << c.name() << "& lhs, const " << c.name() << "& rhs) {\n"; out << " return !(lhs == rhs);\n"; out << "}\n\n"; } +// generate comparison operators for all classes in the file void generate_comparison(const cppast::cpp_file& file) { cppast::visit(file, @@ -82,10 +92,12 @@ void generate_comparison(const cppast::cpp_file& file) [](const cppast::cpp_entity& e, const cppast::visitor_info& info) { if (e.kind() == cppast::cpp_entity_kind::class_t && !info.is_old_entity()) { + // it is a new class auto& class_ = static_cast(e); auto& attribute = cppast::has_attribute(e, "generate::comparison").value(); + // generate requested operators if (attribute.arguments()) { if (has_token(attribute.arguments().value(), "==")) diff --git a/example/documentation_generator.cpp b/example/documentation_generator.cpp index b1278d9..3ed0d36 100644 --- a/example/documentation_generator.cpp +++ b/example/documentation_generator.cpp @@ -4,6 +4,8 @@ /// \file /// A primitive documentation generator. +/// +/// Given an input file, it will print its documentation including documentation comments. #include // code_generator, generate_code() #include // visit() diff --git a/example/enum_category.cpp b/example/enum_category.cpp index 8911b62..03c062d 100644 --- a/example/enum_category.cpp +++ b/example/enum_category.cpp @@ -4,6 +4,9 @@ /// \file /// Generates enum category functions. +/// +/// Given an input file, it will generate definitions for functions marked with [[generate::enum_category(name)]]. +/// The function takes an enumerator and will return true if it is marked with the same category. #include #include @@ -15,10 +18,12 @@ #include "example_parser.hpp" +// returns whether or not the given enumerator has the given category bool is_category(const cppast::cpp_enum_value& e, const std::string& name) { if (auto attr = cppast::has_attribute(e, "generate::enum_category")) { + // ... by looking for the token auto iter = std::find_if(attr.value().arguments().value().begin(), attr.value().arguments().value().end(), @@ -29,12 +34,14 @@ bool is_category(const cppast::cpp_enum_value& e, const std::string& name) return false; } +// returns the enum the parameter type refers to const cppast::cpp_enum& get_enum(const cppast::cpp_entity_index& index, const cppast::cpp_function_parameter& param) { auto& param_type = param.type(); - // is an enum + // it is an enum assert(param_type.kind() == cppast::cpp_type_kind::user_defined_t); + // lookup definition auto& definition = static_cast(param_type).entity().get(index)[0u].get(); @@ -42,6 +49,7 @@ const cppast::cpp_enum& get_enum(const cppast::cpp_entity_index& index, return static_cast(definition); } +// generates the function definitions void generate_enum_category(const cppast::cpp_entity_index& index, const cppast::cpp_file& file) { cppast::visit(file, diff --git a/example/enum_to_string.cpp b/example/enum_to_string.cpp index 04bab5d..ca4d481 100644 --- a/example/enum_to_string.cpp +++ b/example/enum_to_string.cpp @@ -4,6 +4,8 @@ /// \file /// Generates enum `to_string()` code. +/// +/// Given an input file, it will generate a to_string() function for all enums marked with [[generate::to_string]]. #include @@ -40,6 +42,7 @@ void generate_to_string(const cppast::cpp_file& file) std::cout << " case " << enum_.name() << "::" << enumerator.name() << ":\n"; + // attribute can be used to override the string if (auto attr = cppast::has_attribute(enumerator, "generate::to_string")) std::cout << " return " diff --git a/example/serialization.cpp b/example/serialization.cpp index ec7d7f3..cb26940 100644 --- a/example/serialization.cpp +++ b/example/serialization.cpp @@ -4,6 +4,8 @@ /// \file /// Serialization code generation. +/// +/// Given an input file, it will generate a serialize() function for each class marked with [[generate::serialize]]. #include @@ -20,15 +22,18 @@ 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()); @@ -60,6 +65,7 @@ void generate_serialize_member(std::ostream& out, const cppast::cpp_member_varia throw std::invalid_argument("cannot serialize member " + member.name()); } +// generate serialization function void generate_serialize(const cppast::cpp_file& file) { cppast::visit(file,