Add example documentation

Fixes #13.
This commit is contained in:
Jonathan Müller 2017-11-14 18:28:24 +01:00
commit c886ef31e4
7 changed files with 70 additions and 1 deletions

36
example/README.md Normal file
View file

@ -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.

View file

@ -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 <cppast/visitor.hpp> // visit()

View file

@ -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 <algorithm>
#include <iostream>
@ -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<const cppast::cpp_class&>(e);
auto& attribute =
cppast::has_attribute(e, "generate::comparison").value();
// generate requested operators
if (attribute.arguments())
{
if (has_token(attribute.arguments().value(), "=="))

View file

@ -4,6 +4,8 @@
/// \file
/// A primitive documentation generator.
///
/// Given an input file, it will print its documentation including documentation comments.
#include <cppast/code_generator.hpp> // code_generator, generate_code()
#include <cppast/visitor.hpp> // visit()

View file

@ -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 <algorithm>
#include <cassert>
@ -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<const cppast::cpp_user_defined_type&>(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<const cppast::cpp_enum&>(definition);
}
// generates the function definitions
void generate_enum_category(const cppast::cpp_entity_index& index, const cppast::cpp_file& file)
{
cppast::visit(file,

View file

@ -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 <iostream>
@ -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 "

View file

@ -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 <iostream>
@ -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<const cppast::cpp_pointer_type&>(type).pointee());
if (pointee.kind() != cppast::cpp_type_kind::builtin_t)
return false;
// check the builtin type kind
auto builtin = static_cast<const cppast::cpp_builtin_type&>(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,