Add examples from Meeting C++ talk
This commit is contained in:
commit
f73e4b420f
20 changed files with 964 additions and 85 deletions
|
|
@ -15,6 +15,7 @@ option(CPPAST_ENABLE_ASSERTIONS "whether or not to enable internal assertions fo
|
|||
option(CPPAST_ENABLE_PRECONDITION_CHECKS "whether or not to enable precondition checks" ON)
|
||||
|
||||
option(CPPAST_BUILD_TEST "whether or not to build the tests" ON)
|
||||
option(CPPAST_BUILD_EXAMPLE "whether or not to build the examples" ON)
|
||||
option(CPPAST_BUILD_TOOL "whether or not to build the tool" ON)
|
||||
option(BUILD_TESTING "build test" OFF) # The ctest variable for building tests
|
||||
|
||||
|
|
@ -24,6 +25,12 @@ else()
|
|||
set(build_test OFF)
|
||||
endif()
|
||||
|
||||
if(${CPPAST_BUILD_EXAMPLE} OR (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR))
|
||||
set(build_example ON)
|
||||
else()
|
||||
set(build_example OFF)
|
||||
endif()
|
||||
|
||||
if(${CPPAST_BUILD_TOOL} OR (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR))
|
||||
set(build_tool ON)
|
||||
else()
|
||||
|
|
@ -43,6 +50,9 @@ add_subdirectory(src)
|
|||
if(${build_test})
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
if(${build_example})
|
||||
add_subdirectory(example)
|
||||
endif()
|
||||
if(${build_tool})
|
||||
add_subdirectory(tool)
|
||||
endif()
|
||||
|
|
|
|||
16
example/CMakeLists.txt
Normal file
16
example/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# Copyright (C) 2017 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
# This file is subject to the license terms in the LICENSE file
|
||||
# found in the top-level directory of this distribution.
|
||||
|
||||
function(_cppast_example name)
|
||||
add_executable(cppast_example_${name} ${name}.cpp example_parser.hpp)
|
||||
target_link_libraries(cppast_example_${name} PUBLIC cppast)
|
||||
set_target_properties(cppast_example_${name} PROPERTIES CXX_STANDARD 11)
|
||||
endfunction()
|
||||
|
||||
_cppast_example(ast_printer)
|
||||
_cppast_example(comparison)
|
||||
_cppast_example(documentation_generator)
|
||||
_cppast_example(enum_category)
|
||||
_cppast_example(enum_to_string)
|
||||
_cppast_example(serialization)
|
||||
33
example/ast_printer.cpp
Normal file
33
example/ast_printer.cpp
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
// Copyright (C) 2017 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
/// \file
|
||||
/// This is a very primitive version of the cppast tool.
|
||||
|
||||
#include <cppast/visitor.hpp> // visit()
|
||||
|
||||
#include "example_parser.hpp"
|
||||
|
||||
void print_ast(const cppast::cpp_file& file)
|
||||
{
|
||||
std::string prefix;
|
||||
// visit each entity in the file
|
||||
cppast::visit(file, [&](const cppast::cpp_entity& e, cppast::visitor_info info) {
|
||||
if (info.event == cppast::visitor_info::container_entity_exit) // exiting an old container
|
||||
prefix.pop_back();
|
||||
else if (info.event == cppast::visitor_info::container_entity_enter)
|
||||
// entering a new container
|
||||
{
|
||||
std::cout << prefix << "'" << e.name() << "' - " << cppast::to_string(e.kind()) << '\n';
|
||||
prefix += "\t";
|
||||
}
|
||||
else // if (info.event == cppast::visitor_info::leaf_entity) // a non-container entity
|
||||
std::cout << prefix << "'" << e.name() << "' - " << cppast::to_string(e.kind()) << '\n';
|
||||
});
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
return example_main(argc, argv, {}, &print_ast);
|
||||
}
|
||||
117
example/comparison.cpp
Normal file
117
example/comparison.cpp
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
// Copyright (C) 2017 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// 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 <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
#include <cppast/cpp_class.hpp>
|
||||
#include <cppast/cpp_member_variable.hpp>
|
||||
#include <cppast/visitor.hpp>
|
||||
|
||||
#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<const " << base.name() << "&>(lhs) == static_cast<const " << base.name()
|
||||
<< "&>(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<const cppast::cpp_class&>(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_);
|
||||
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);
|
||||
}
|
||||
160
example/documentation_generator.cpp
Normal file
160
example/documentation_generator.cpp
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
// Copyright (C) 2017 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
/// \file
|
||||
/// A primitive documentation generator.
|
||||
|
||||
#include <cppast/code_generator.hpp> // code_generator, generate_code()
|
||||
#include <cppast/visitor.hpp> // visit()
|
||||
|
||||
#include "example_parser.hpp"
|
||||
|
||||
// don't show in code generation
|
||||
bool is_excluded_synopsis(const cppast::cpp_entity& e, cppast::cpp_access_specifier_kind access)
|
||||
{
|
||||
// exclude privates and those marked for exclusion
|
||||
return access == cppast::cpp_private || cppast::has_attribute(e, "documentation::exclude");
|
||||
}
|
||||
|
||||
// don't show in documentation
|
||||
bool is_excluded_documentation(const cppast::cpp_entity& e,
|
||||
const cppast::cpp_access_specifier_kind access)
|
||||
{
|
||||
// exclude uninteresting entities
|
||||
return e.kind() == cppast::cpp_entity_kind::access_specifier_t
|
||||
|| e.kind() == cppast::cpp_entity_kind::using_declaration_t
|
||||
|| e.kind() == cppast::cpp_entity_kind::using_directive_t
|
||||
|| e.kind() == cppast::cpp_entity_kind::static_assert_t
|
||||
|| e.kind() == cppast::cpp_entity_kind::include_directive_t
|
||||
// and all excluded in synopsis
|
||||
|| is_excluded_synopsis(e, access);
|
||||
}
|
||||
|
||||
// generates synopsis of an entity
|
||||
std::string generate_synopsis(const cppast::cpp_entity& e)
|
||||
{
|
||||
// the generator for the synopsis
|
||||
class synopsis_generator final : public cppast::code_generator
|
||||
{
|
||||
public:
|
||||
// get the resulting string
|
||||
std::string result()
|
||||
{
|
||||
return std::move(str_);
|
||||
}
|
||||
|
||||
private:
|
||||
// whether or not the entity is the main entity that is being documented
|
||||
bool is_main_entity(const cppast::cpp_entity& e)
|
||||
{
|
||||
if (cppast::is_templated(e) || cppast::is_friended(e))
|
||||
// need to ask the real entity
|
||||
return is_main_entity(e.parent().value());
|
||||
else
|
||||
return &e == &this->main_entity();
|
||||
}
|
||||
|
||||
// get some nicer formatting
|
||||
cppast::formatting do_get_formatting() const override
|
||||
{
|
||||
return cppast::formatting_flags::brace_nl | cppast::formatting_flags::comma_ws
|
||||
| cppast::formatting_flags::operator_ws;
|
||||
}
|
||||
|
||||
// calculate generation options
|
||||
generation_options do_get_options(const cppast::cpp_entity& e,
|
||||
cppast::cpp_access_specifier_kind access) override
|
||||
{
|
||||
if (is_excluded_synopsis(e, access))
|
||||
return cppast::code_generator::exclude;
|
||||
else if (!is_main_entity(e))
|
||||
// only generation declaration for the non-documented entity
|
||||
return cppast::code_generator::declaration;
|
||||
else
|
||||
// default options
|
||||
return {};
|
||||
}
|
||||
|
||||
// update indendation level
|
||||
void do_indent() override
|
||||
{
|
||||
++indent_;
|
||||
}
|
||||
void do_unindent() override
|
||||
{
|
||||
if (indent_)
|
||||
--indent_;
|
||||
}
|
||||
|
||||
// write specified tokens
|
||||
// need to change indentation for each newline
|
||||
void do_write_token_seq(cppast::string_view tokens) override
|
||||
{
|
||||
if (was_newline_)
|
||||
{
|
||||
str_ += std::string(indent_ * 2u, ' ');
|
||||
was_newline_ = false;
|
||||
}
|
||||
|
||||
str_ += tokens.c_str();
|
||||
}
|
||||
|
||||
// write + remember newline
|
||||
void do_write_newline() override
|
||||
{
|
||||
str_ += "\n";
|
||||
was_newline_ = true;
|
||||
}
|
||||
|
||||
std::string str_;
|
||||
unsigned indent_ = 0;
|
||||
bool was_newline_ = false;
|
||||
} generator;
|
||||
cppast::generate_code(generator, e);
|
||||
return generator.result();
|
||||
}
|
||||
|
||||
void generate_documentation(const cppast::cpp_file& file)
|
||||
{
|
||||
// visit each entity
|
||||
cppast::visit(file,
|
||||
[](const cppast::cpp_entity& e, cppast::cpp_access_specifier_kind access) {
|
||||
if (is_excluded_documentation(e, access))
|
||||
// exclude this and all children
|
||||
return cppast::visit_filter::exclude_and_children;
|
||||
else if (cppast::is_templated(e) || cppast::is_friended(e))
|
||||
// continue on with children for a dummy entity
|
||||
return cppast::visit_filter::exclude;
|
||||
else
|
||||
return cppast::visit_filter::include;
|
||||
},
|
||||
[](const cppast::cpp_entity& e, const cppast::visitor_info& info) {
|
||||
if (info.is_old_entity())
|
||||
// already done
|
||||
return;
|
||||
|
||||
// print name
|
||||
std::cout << "## " << cppast::to_string(e.kind()) << " '" << e.name()
|
||||
<< "'\n";
|
||||
std::cout << '\n';
|
||||
|
||||
// print synopsis
|
||||
std::cout << "```\n";
|
||||
std::cout << generate_synopsis(e);
|
||||
std::cout << "```\n\n";
|
||||
|
||||
// print documentation comment
|
||||
if (e.comment())
|
||||
std::cout << e.comment().value() << '\n';
|
||||
|
||||
// print separator
|
||||
std::cout << "\n---\n\n";
|
||||
});
|
||||
std::cout << "\n\n";
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
return example_main(argc, argv, {}, &generate_documentation);
|
||||
}
|
||||
116
example/enum_category.cpp
Normal file
116
example/enum_category.cpp
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
// Copyright (C) 2017 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
/// \file
|
||||
/// Generates enum category functions.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
|
||||
#include <cppast/cpp_enum.hpp> // cpp_enum
|
||||
#include <cppast/cpp_function.hpp> // cpp_function
|
||||
#include <cppast/visitor.hpp> // visit()
|
||||
|
||||
#include "example_parser.hpp"
|
||||
|
||||
bool is_category(const cppast::cpp_enum_value& e, const std::string& name)
|
||||
{
|
||||
if (auto attr = cppast::has_attribute(e, "generate::enum_category"))
|
||||
{
|
||||
auto iter =
|
||||
std::find_if(attr.value().arguments().value().begin(),
|
||||
attr.value().arguments().value().end(),
|
||||
[&](const cppast::cpp_token& tok) { return tok.spelling == name; });
|
||||
return iter != attr.value().arguments().value().end();
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
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
|
||||
assert(param_type.kind() == cppast::cpp_type_kind::user_defined_t);
|
||||
auto& definition =
|
||||
static_cast<const cppast::cpp_user_defined_type&>(param_type).entity().get(index)[0u].get();
|
||||
|
||||
assert(definition.kind() == cppast::cpp_entity_kind::enum_t);
|
||||
return static_cast<const cppast::cpp_enum&>(definition);
|
||||
}
|
||||
|
||||
void generate_enum_category(const cppast::cpp_entity_index& index, const cppast::cpp_file& file)
|
||||
{
|
||||
cppast::visit(file,
|
||||
[](const cppast::cpp_entity& e) {
|
||||
// only visit function declarations that have the attribute set
|
||||
return (e.kind() == cppast::cpp_entity_kind::function_t
|
||||
&& !cppast::is_definition(e)
|
||||
&& cppast::has_attribute(e, "generate::enum_category"))
|
||||
// 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::function_t)
|
||||
{
|
||||
// a new function, generate implementation
|
||||
assert(info.is_new_entity());
|
||||
|
||||
auto category = cppast::has_attribute(e, "generate::enum_category")
|
||||
.value()
|
||||
.arguments()
|
||||
.value()
|
||||
.as_string();
|
||||
|
||||
auto& func = static_cast<const cppast::cpp_function&>(e);
|
||||
// return type must be bool
|
||||
assert(func.return_type().kind() == cppast::cpp_type_kind::builtin_t
|
||||
&& static_cast<const cppast::cpp_builtin_type&>(func.return_type())
|
||||
.builtin_type_kind()
|
||||
== cppast::cpp_bool);
|
||||
|
||||
// single parameter...
|
||||
assert(std::next(func.parameters().begin()) == func.parameters().end());
|
||||
auto& param = *func.parameters().begin();
|
||||
auto& enum_ = get_enum(index, param);
|
||||
|
||||
// generate function definition
|
||||
std::cout << "inline bool " << func.name() << "("
|
||||
<< cppast::to_string(param.type()) << " e) {\n";
|
||||
|
||||
// generate switch
|
||||
std::cout << " switch (e) {\n";
|
||||
for (const auto& enumerator : enum_)
|
||||
{
|
||||
std::cout << " case " << enum_.name() << "::" << enumerator.name()
|
||||
<< ":\n";
|
||||
if (is_category(enumerator, category))
|
||||
std::cout << " return true;\n";
|
||||
else
|
||||
std::cout << " return false;\n";
|
||||
}
|
||||
std::cout << " }\n";
|
||||
|
||||
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[])
|
||||
{
|
||||
cppast::cpp_entity_index index;
|
||||
return example_main(argc, argv, index,
|
||||
[&](const cppast::cpp_file& file) { generate_enum_category(index, file); });
|
||||
}
|
||||
70
example/enum_to_string.cpp
Normal file
70
example/enum_to_string.cpp
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
// Copyright (C) 2017 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
/// \file
|
||||
/// Generates enum `to_string()` code.
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <cppast/cpp_enum.hpp> // cpp_enum
|
||||
#include <cppast/visitor.hpp> // visit()
|
||||
|
||||
#include "example_parser.hpp"
|
||||
|
||||
void generate_to_string(const cppast::cpp_file& file)
|
||||
{
|
||||
cppast::visit(file,
|
||||
[](const cppast::cpp_entity& e) {
|
||||
// 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;
|
||||
},
|
||||
[](const cppast::cpp_entity& e, const cppast::visitor_info& info) {
|
||||
if (e.kind() == cppast::cpp_entity_kind::enum_t && !info.is_old_entity())
|
||||
{
|
||||
// a new enum, generate to string function
|
||||
auto& enum_ = static_cast<const cppast::cpp_enum&>(e);
|
||||
|
||||
// write function header
|
||||
std::cout << "inline const char* to_string(const " << enum_.name()
|
||||
<< "& e) {\n";
|
||||
|
||||
// generate switch
|
||||
std::cout << " switch (e) {\n";
|
||||
for (const auto& enumerator : enum_)
|
||||
{
|
||||
std::cout << " case " << enum_.name() << "::" << enumerator.name()
|
||||
<< ":\n";
|
||||
|
||||
if (auto attr =
|
||||
cppast::has_attribute(enumerator, "generate::to_string"))
|
||||
std::cout << " return "
|
||||
<< attr.value().arguments().value().as_string()
|
||||
<< ";\n";
|
||||
else
|
||||
std::cout << " return \"" << enumerator.name() << "\";\n";
|
||||
}
|
||||
std::cout << " }\n";
|
||||
|
||||
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_to_string);
|
||||
}
|
||||
56
example/example_parser.hpp
Normal file
56
example/example_parser.hpp
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
// Copyright (C) 2017 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef CPPAST_EXAMPLE_PARSER_HPP_INCLUDED
|
||||
#define CPPAST_EXAMPLE_PARSER_HPP_INCLUDED
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <cppast/libclang_parser.hpp>
|
||||
|
||||
// reads the database directory from the command line argument
|
||||
// parses all files in that directory
|
||||
// and invokes the callback for each of them
|
||||
template <typename Callback>
|
||||
int example_main(int argc, char* argv[], const cppast::cpp_entity_index& index, Callback cb) try
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
std::cerr << "usage: " << argv[0] << " <compile-commands-json-dir>\n";
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
cppast::libclang_compilation_database database(argv[1]); // the compilation database
|
||||
|
||||
// simple_file_parser allows parsing multiple files and stores the results for us
|
||||
cppast::simple_file_parser<cppast::libclang_parser> parser(type_safe::ref(index));
|
||||
try
|
||||
{
|
||||
cppast::parse_database(parser, database); // parse all files in the database
|
||||
}
|
||||
catch (cppast::libclang_error& ex)
|
||||
{
|
||||
std::cerr << "fatal libclang error: " << ex.what() << '\n';
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (parser.error())
|
||||
// a non-fatal parse error
|
||||
// error has been logged to stderr
|
||||
return 1;
|
||||
|
||||
for (auto& file : parser.files())
|
||||
cb(file);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
std::cerr << ex.what() << '\n';
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif // CPPAST_EXAMPLE_PARSER_HPP_INCLUDED
|
||||
116
example/serialization.cpp
Normal file
116
example/serialization.cpp
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
// Copyright (C) 2017 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// 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 <iostream>
|
||||
|
||||
#include <cppast/cpp_class.hpp>
|
||||
#include <cppast/cpp_member_variable.hpp>
|
||||
#include <cppast/cpp_type.hpp>
|
||||
#include <cppast/visitor.hpp>
|
||||
|
||||
#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<const cppast::cpp_pointer_type&>(type).pointee());
|
||||
if (pointee.kind() != cppast::cpp_type_kind::builtin_t)
|
||||
return false;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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<const cppast::cpp_class&>(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<const " << base.name()
|
||||
<< "&>(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);
|
||||
}
|
||||
|
|
@ -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<void*> 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<cpp_type> pointee) : pointee_(std::move(pointee))
|
||||
{
|
||||
}
|
||||
cpp_pointer_type(std::unique_ptr<cpp_type> pointee) : pointee_(std::move(pointee)) {}
|
||||
|
||||
cpp_type_kind do_get_kind() const noexcept override
|
||||
{
|
||||
|
|
@ -439,6 +436,9 @@ namespace cppast
|
|||
cpp_reference ref_;
|
||||
};
|
||||
|
||||
/// \returns The type as a string representation.
|
||||
std::string to_string(const cpp_type& type);
|
||||
|
||||
/// \exclude
|
||||
namespace detail
|
||||
{
|
||||
|
|
@ -456,9 +456,6 @@ namespace cppast
|
|||
// write prefix, variadic, name, suffix
|
||||
void write_type(code_generator::output& output, const cpp_type& type, std::string name,
|
||||
bool is_variadic = false);
|
||||
|
||||
// simple to_string() for types
|
||||
std::string to_string(const cpp_type& type);
|
||||
} // namespace detail
|
||||
} // namespace cppast
|
||||
|
||||
|
|
|
|||
|
|
@ -164,7 +164,12 @@ namespace cppast
|
|||
public:
|
||||
using config = libclang_compile_config;
|
||||
|
||||
/// \effects Creates a parser using the default logger.
|
||||
libclang_parser();
|
||||
|
||||
/// \effects Creates a parser that will log error messages using the specified logger.
|
||||
explicit libclang_parser(type_safe::object_ref<const diagnostic_logger> logger);
|
||||
|
||||
~libclang_parser() noexcept override;
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -187,6 +187,8 @@ namespace cppast
|
|||
[&](const std::string&) { return config; });
|
||||
}
|
||||
|
||||
/// Parses all files included by `file`.
|
||||
/// \effects For each [cppast::cpp_include_directive]() in file it will parse the included file.
|
||||
template <class FileParser>
|
||||
void resolve_includes(FileParser& parser, const cpp_file& file,
|
||||
typename FileParser::config config)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@
|
|||
#ifndef CPPAST_VISITOR_HPP_INCLUDED
|
||||
#define CPPAST_VISITOR_HPP_INCLUDED
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include <cppast/cpp_class.hpp>
|
||||
#include <cppast/cpp_entity.hpp>
|
||||
#include <cppast/cpp_entity_kind.hpp>
|
||||
|
|
@ -28,83 +30,212 @@ namespace cppast
|
|||
|
||||
cpp_access_specifier_kind access; //< The current access specifier.
|
||||
|
||||
bool
|
||||
last_child; //< True when the current entity is the last child of the visited parent entity.
|
||||
/// True when the current entity is the last child of the visited parent entity.
|
||||
/// \notes It will always be `false` for the initial entity.
|
||||
bool last_child;
|
||||
|
||||
/// \returns `true` if the entity was not visited already.
|
||||
bool is_new_entity() const noexcept
|
||||
{
|
||||
return event != container_entity_exit;
|
||||
}
|
||||
|
||||
/// \returns `true` if the entity was visited already.
|
||||
bool is_old_entity() const noexcept
|
||||
{
|
||||
return !is_new_entity();
|
||||
}
|
||||
};
|
||||
|
||||
/// A more expressive way to specify the return of a visit operation.
|
||||
enum visitor_result : bool
|
||||
{
|
||||
/// Visit should continue.
|
||||
/// \group continue
|
||||
continue_visit = true,
|
||||
|
||||
/// \group continue
|
||||
continue_visit_children = true,
|
||||
|
||||
/// Visit should not visit the children.
|
||||
/// \notes This only happens when the event is [cppast::visitor_info::container_entity_enter]().
|
||||
continue_visit_no_children = false,
|
||||
|
||||
/// Visit should be aborted.
|
||||
/// \notes This only happens when the event is not [cppast::visitor_info::container_entity_enter]().
|
||||
abort_visit = false,
|
||||
};
|
||||
|
||||
/// \exclude
|
||||
namespace detail
|
||||
{
|
||||
using visitor_callback_t = bool (*)(void* mem, const cpp_entity&, visitor_info info);
|
||||
using visitor_predicate_t = bool (*)(const cpp_entity&);
|
||||
using visitor_callback_t = bool (*)(void* mem, const cpp_entity&, visitor_info info);
|
||||
|
||||
struct visitor_info_void
|
||||
{
|
||||
};
|
||||
struct visitor_info_bool : visitor_info_void
|
||||
{
|
||||
};
|
||||
|
||||
template <typename Func>
|
||||
bool visitor_callback(void* mem, const cpp_entity& e, visitor_info info)
|
||||
visitor_callback_t get_visitor_callback(
|
||||
visitor_info_bool, decltype(std::declval<Func>()(std::declval<const cpp_entity&>(),
|
||||
visitor_info{})) = true)
|
||||
{
|
||||
auto& func = *static_cast<Func*>(mem);
|
||||
return func(e, info);
|
||||
return [](void* mem, const cpp_entity& e, visitor_info info) {
|
||||
auto& func = *static_cast<Func*>(mem);
|
||||
return func(e, info);
|
||||
};
|
||||
}
|
||||
|
||||
template <typename Func>
|
||||
visitor_callback_t get_visitor_callback(
|
||||
visitor_info_void,
|
||||
decltype(std::declval<Func>()(std::declval<const cpp_entity&>(), visitor_info{}),
|
||||
0) = 0)
|
||||
{
|
||||
return [](void* mem, const cpp_entity& e, visitor_info info) {
|
||||
auto& func = *static_cast<Func*>(mem);
|
||||
func(e, info);
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
template <typename Func>
|
||||
visitor_callback_t get_visitor_callback()
|
||||
{
|
||||
return get_visitor_callback<Func>(visitor_info_bool{});
|
||||
}
|
||||
|
||||
bool visit(const cpp_entity& e, visitor_callback_t cb, void* functor,
|
||||
cpp_access_specifier_kind cur_access, bool last_child);
|
||||
} // namespace detail
|
||||
|
||||
/// Visits a [cppast::cpp_entity]().
|
||||
/// \effects Invokes the callback for the current entity,
|
||||
/// and any child entities.
|
||||
/// It will pass a reference to the current entity and the [cppast::visitor_info]().
|
||||
/// The return value of the callback controls the visit operation,
|
||||
/// the semantic depend on the [cppast::visitor_info::event_type]().
|
||||
/// Visits a [cppast::cpp_entity]() and children.
|
||||
///
|
||||
/// \effects It will invoke the callback - the visitor - for the current entity and children,
|
||||
/// as controlled by the [cppast::visitor_result]().
|
||||
/// The visitor is given a reference to the currently visited entity and the [cppast::visitor_info]().
|
||||
///
|
||||
/// \requires The visitor must be callable as specified and must either return `bool` or nothing.
|
||||
/// If it returns nothing, a return value [cppast::visitor_result::continue_visit]() is assumed.
|
||||
template <typename Func>
|
||||
void visit(const cpp_entity& e, Func f)
|
||||
{
|
||||
detail::visit(e, &detail::visitor_callback<Func>, &f, cpp_public, false);
|
||||
detail::visit(e, detail::get_visitor_callback<Func>(), &f, cpp_public, false);
|
||||
}
|
||||
|
||||
/// Visits a [cppast::cpp_entity]().
|
||||
/// \effects Invokes the callback for the current entity,
|
||||
/// and child entities that match the given predicate.
|
||||
/// It will pass a reference to the current entity and the [cppast::visitor_info]().
|
||||
/// The return value of the callback controls the visit operation,
|
||||
/// the semantic depend on the [cppast::visitor_info::event_type]().
|
||||
template <typename Func, typename Predicate>
|
||||
void visit(const cpp_entity& e, Predicate pred, Func f)
|
||||
/// The result of a visitor filter operation.
|
||||
enum class visit_filter
|
||||
{
|
||||
visit(e, [&](const cpp_entity& e, visitor_info& info) {
|
||||
if (pred(e))
|
||||
return f(e, info);
|
||||
return true;
|
||||
include = true, //< The entity is included.
|
||||
exclude = false, //< The entity is excluded and will not be visited.
|
||||
exclude_and_children =
|
||||
2, //< The entity and all direct children are excluded and will not be visited.
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
using visitor_filter_t = visit_filter (*)(const cpp_entity&);
|
||||
|
||||
template <typename Filter>
|
||||
auto invoke_visit_filter(int, Filter f, const cpp_entity& e,
|
||||
cpp_access_specifier_kind access)
|
||||
-> decltype(static_cast<visit_filter>(f(e, access)))
|
||||
{
|
||||
return static_cast<visit_filter>(f(e, access));
|
||||
}
|
||||
|
||||
template <typename Filter>
|
||||
auto invoke_visit_filter(short, Filter f, const cpp_entity& e, cpp_access_specifier_kind)
|
||||
-> decltype(static_cast<visit_filter>(f(e)))
|
||||
{
|
||||
return static_cast<visit_filter>(f(e));
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
/// Visits a [cppast::cpp_entity]() and children that pass a given filter.
|
||||
///
|
||||
/// \effects It behaves like the non-filtered visit except it will only invoke the visitor for entities that match the filter.
|
||||
/// The filter is a predicate that will be invoked with the current entity and access specifier first and the result converted to [cppast::visit_filter]().
|
||||
/// Visit will behave accordingly.
|
||||
///
|
||||
/// \requires The visitor must be as specified for the other overload.
|
||||
/// The filter must be a function taking a [cppast::cpp_entity]() as first parameter and optionally a [cppast::cpp_access_specifier_kind]() as second.
|
||||
/// It must return a `bool` or a [cppast::visit_filter]().
|
||||
template <typename Func, typename Filter>
|
||||
void visit(const cpp_entity& e, Filter filter, Func f)
|
||||
{
|
||||
visit(e, [&](const cpp_entity& e, const visitor_info& info) -> bool {
|
||||
auto result = detail::invoke_visit_filter(0, filter, e, info.access);
|
||||
switch (result)
|
||||
{
|
||||
case visit_filter::include:
|
||||
return detail::get_visitor_callback<Func>()(&f, e, info);
|
||||
case visit_filter::exclude:
|
||||
return continue_visit;
|
||||
case visit_filter::exclude_and_children:
|
||||
// exclude children if entering
|
||||
if (info.event == visitor_info::container_entity_enter)
|
||||
return continue_visit_no_children;
|
||||
else
|
||||
return continue_visit;
|
||||
}
|
||||
return continue_visit;
|
||||
});
|
||||
}
|
||||
|
||||
/// Generates, at compile-time, a predicate that returns true iff the
|
||||
/// given entity holds one of the [cppast::cpp_entity_kind]()s specified
|
||||
/// in paramaters of the blacklist template.
|
||||
template <cpp_entity_kind... K>
|
||||
detail::visitor_predicate_t whitelist()
|
||||
/// \exclude
|
||||
namespace detail
|
||||
{
|
||||
static_assert(sizeof...(K) > 0, "At least one entity kind should be specified");
|
||||
return [](const cpp_entity& e) {
|
||||
template <cpp_entity_kind... K>
|
||||
bool has_one_of_kind(const cpp_entity& e)
|
||||
{
|
||||
static_assert(sizeof...(K) > 0, "At least one entity kind must be specified");
|
||||
bool result = false;
|
||||
// this ugliness avoids recursive expansion which would be required in C++11,
|
||||
// otherwise known as poor man's fold expression.
|
||||
(void)std::initializer_list<int>{(result = result || (K == e.kind()), 0)...};
|
||||
|
||||
// poor men's fold
|
||||
int dummy[]{(result |= (K == e.kind()), 0)...};
|
||||
(void)dummy;
|
||||
|
||||
return result;
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
/// Generates a blacklist visitor filter.
|
||||
///
|
||||
/// \returns A visitor filter that excludes all entities having one of the specified kinds.
|
||||
template <cpp_entity_kind... Kinds>
|
||||
detail::visitor_filter_t blacklist()
|
||||
{
|
||||
return [](const cpp_entity& e) {
|
||||
return detail::has_one_of_kind<Kinds...>(e) ? visit_filter::exclude :
|
||||
visit_filter::include;
|
||||
};
|
||||
}
|
||||
|
||||
/// Generates, at compile-time, a predicate that returns true iff the
|
||||
/// given entity holds a [cppast::cpp_entity_kind]() that's different
|
||||
/// from all of those specified in the paramaters of the blacklist template.
|
||||
template <cpp_entity_kind... K>
|
||||
detail::visitor_predicate_t blacklist()
|
||||
/// Generates a blacklist visitor filter.
|
||||
///
|
||||
/// \returns A visitor filter that excludes all entities having one of the specified kinds and children of those entities.
|
||||
template <cpp_entity_kind... Kinds>
|
||||
detail::visitor_filter_t blacklist_and_children()
|
||||
{
|
||||
static_assert(sizeof...(K) > 0, "At least one entity kind should be specified");
|
||||
return [](const cpp_entity& e) {
|
||||
bool result = true;
|
||||
(void)std::initializer_list<int>{(result = result && (K != e.kind()), 0)...};
|
||||
return result;
|
||||
return detail::has_one_of_kind<Kinds...>(e) ? visit_filter::exclude_and_children :
|
||||
visit_filter::include;
|
||||
};
|
||||
}
|
||||
|
||||
/// Generates a whitelist visitor filter.
|
||||
///
|
||||
/// \returns A visitor filter that excludes all entities having not one of the specified kinds.
|
||||
template <cpp_entity_kind... Kinds>
|
||||
detail::visitor_filter_t whitelist()
|
||||
{
|
||||
return [](const cpp_entity& e) {
|
||||
return detail::has_one_of_kind<Kinds...>(e) ? visit_filter::include :
|
||||
visit_filter::exclude;
|
||||
};
|
||||
}
|
||||
} // namespace cppast
|
||||
|
|
|
|||
|
|
@ -103,6 +103,7 @@ set(libclang_source
|
|||
libclang/variable_parser.cpp)
|
||||
|
||||
add_library(cppast ${detail_header} ${header} ${source} ${libclang_source})
|
||||
set_target_properties(cppast PROPERTIES CXX_STANDARD 11)
|
||||
target_include_directories(cppast PUBLIC ../include)
|
||||
target_link_libraries(cppast PUBLIC type_safe _cppast_tiny_process _cppast_libclang)
|
||||
target_compile_definitions(cppast PUBLIC
|
||||
|
|
@ -115,4 +116,3 @@ endif()
|
|||
if(CPPAST_ENABLE_PRECONDITION_CHECKS)
|
||||
target_compile_definitions(cppast PUBLIC CPPAST_ENABLE_PRECONDITION_CHECKS)
|
||||
endif()
|
||||
set_target_properties(cppast PROPERTIES CXX_STANDARD 11)
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ std::string cpp_function_base::do_get_signature() const
|
|||
{
|
||||
std::string result = "(";
|
||||
for (auto& param : parameters())
|
||||
result += detail::to_string(param.type()) + ',';
|
||||
result += to_string(param.type()) + ',';
|
||||
if (is_variadic())
|
||||
result += "...";
|
||||
|
||||
|
|
|
|||
|
|
@ -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<const cpp_cv_qualified_type&>(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<const cpp_cv_qualified_type&>(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<const cpp_cv_qualified_type&>(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())
|
||||
|
|
@ -548,7 +581,7 @@ void detail::write_type(code_generator::output& output, const cpp_type& type, st
|
|||
write_type_suffix(output, type);
|
||||
}
|
||||
|
||||
std::string detail::to_string(const cpp_type& type)
|
||||
std::string cppast::to_string(const cpp_type& type)
|
||||
{
|
||||
class to_string_generator : public code_generator
|
||||
{
|
||||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -266,12 +266,12 @@ namespace
|
|||
while (!token_after_is(tu, file, cur, end, ";"))
|
||||
end = get_next_location(tu, file, end);
|
||||
}
|
||||
else if (kind == CXCursor_EnumConstantDecl && !token_after_is(tu, file, cur, end, ",")
|
||||
&& !token_after_is(tu, file, cur, end, ";"))
|
||||
else if (kind == CXCursor_EnumConstantDecl && !token_after_is(tu, file, cur, end, ","))
|
||||
{
|
||||
while (!token_after_is(tu, file, cur, end, ",")
|
||||
&& !token_after_is(tu, file, cur, end, ";"))
|
||||
end = get_next_location(tu, file, end);
|
||||
// need to support attributes
|
||||
// just give up and extend the range to the range of the entire enum...
|
||||
auto parent = clang_getCursorLexicalParent(cur);
|
||||
end = clang_getRangeEnd(clang_getCursorExtent(parent));
|
||||
}
|
||||
else if (kind == CXCursor_FieldDecl || kind == CXCursor_ParmDecl
|
||||
|| kind == CXCursor_NonTypeTemplateParameter
|
||||
|
|
|
|||
|
|
@ -330,6 +330,8 @@ struct libclang_parser::impl
|
|||
}
|
||||
};
|
||||
|
||||
libclang_parser::libclang_parser() : libclang_parser(default_logger()) {}
|
||||
|
||||
libclang_parser::libclang_parser(type_safe::object_ref<const diagnostic_logger> logger)
|
||||
: parser(logger), pimpl_(new impl)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@ TEST_CASE("visitor_filtered")
|
|||
)";
|
||||
|
||||
cpp_entity_index idx;
|
||||
auto file = parse(idx, "cpp_class.cpp", code);
|
||||
unsigned filtered_count = 0;
|
||||
auto visitor_callback = [&](const cpp_entity&, cppast::visitor_info info) {
|
||||
auto file = parse(idx, "cpp_class.cpp", code);
|
||||
unsigned filtered_count = 0;
|
||||
auto visitor_callback = [&](const cpp_entity&, cppast::visitor_info info) {
|
||||
if (info.event != cppast::visitor_info::container_entity_exit)
|
||||
++filtered_count;
|
||||
return true;
|
||||
|
|
@ -83,4 +83,24 @@ TEST_CASE("visitor_filtered")
|
|||
REQUIRE(filtered_count == all_node_count - enum_count - class_count);
|
||||
}
|
||||
}
|
||||
SECTION("blacklist_and_children")
|
||||
{
|
||||
SECTION("only one kind blacklisted")
|
||||
{
|
||||
filtered_count = 0;
|
||||
cppast::visit(*file, blacklist_and_children<cpp_entity_kind::file_t>(),
|
||||
visitor_callback);
|
||||
REQUIRE(filtered_count == 0);
|
||||
}
|
||||
|
||||
SECTION("many kinds blacklisted")
|
||||
{
|
||||
filtered_count = 0;
|
||||
cppast::visit(*file,
|
||||
blacklist_and_children<cpp_entity_kind::enum_t,
|
||||
cpp_entity_kind::class_t>(),
|
||||
visitor_callback);
|
||||
REQUIRE(filtered_count == all_node_count - enum_count - class_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,5 +4,4 @@
|
|||
|
||||
add_executable(cppast_tool main.cpp)
|
||||
target_link_libraries(cppast_tool PUBLIC cppast cxxopts)
|
||||
set_target_properties(cppast_tool PROPERTIES CXX_STANDARD 11
|
||||
OUTPUT_NAME cppast)
|
||||
set_target_properties(cppast_tool PROPERTIES CXX_STANDARD 11 OUTPUT_NAME cppast)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue