From c86e9508477aed2250df97cd432d565bd28e3b3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20M=C3=BCller?= Date: Thu, 25 May 2017 21:04:50 +0200 Subject: [PATCH 1/4] Add `exclude_return` and `exclude_target` code generation options --- include/cppast/code_generator.hpp | 48 +++++++++++++++++----- src/code_generator.cpp | 34 ++++++++++++---- test/code_generator.cpp | 68 ++++++++++++++++++++++++++++--- test/test_parser.hpp | 26 +++++++++--- tool/main.cpp | 4 +- 5 files changed, 151 insertions(+), 29 deletions(-) diff --git a/include/cppast/code_generator.hpp b/include/cppast/code_generator.hpp index 2d1d769..3ed6fb3 100644 --- a/include/cppast/code_generator.hpp +++ b/include/cppast/code_generator.hpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -129,14 +130,19 @@ namespace cppast code_generator& operator=(const code_generator&) = delete; virtual ~code_generator() noexcept = default; - /// Options that control the synopsis. - enum synopsis_options + /// Flags that control the synopsis. + enum synopsis_flags { - exclude, //< Exclude the entire entity. - declaration, //< Only write declaration. - definition, //< Also write definition. + exclude, //< Exclude the entire entity. + exclude_return, //< Exclude the return type of a function entity. + exclude_target, //< Exclude the underlying entity of an alias (e.g. typedef). + declaration, //< Only write declaration. + _flag_set_size, //< \exclude }; + /// Options that control the synopsis. + using synopsis_options = type_safe::flag_set; + /// Sentinel type used to output a given entity. class output { @@ -177,10 +183,16 @@ namespace cppast return options_ != exclude; } + /// \returns The synopsis options. + synopsis_options options() const noexcept + { + return options_; + } + /// \returns Whether or not the definition should be generated as well. bool generate_definition() const noexcept { - return options_ == definition; + return !(options_ & declaration); } /// \returns A reference to the generator. @@ -280,6 +292,14 @@ namespace cppast return *this; } + /// \effects Calls `do_write_excluded()`. + const output& excluded(const cpp_entity& e) const + { + if (*this) + gen_->do_write_excluded(e); + return *this; + } + /// \effects Calls `do_write_newline()`. const output& operator<<(newl_t) const { @@ -309,11 +329,11 @@ namespace cppast /// \effects Will be invoked before code of a container entity is generated. /// The base class version has no effect. /// \returns The synopsis options for that entity, - /// the base class version always returns `definition`. + /// the base class version always returns no special options. virtual synopsis_options on_container_begin(const cpp_entity& e) { (void)e; - return definition; + return {}; } /// \effects Will be invoked after all code of a container entity has been generated. @@ -326,11 +346,11 @@ namespace cppast /// \effects Will be invoked before code of a non-container entity is generated. /// The base class version has no effect. /// \returns The synopsis options for that entity, - /// the base class version always returns `definition`. + /// the base class version always returns no special options. virtual synopsis_options on_leaf(const cpp_entity& e) { (void)e; - return definition; + return {}; } /// \effects Will be invoked when the indentation level should be increased by one. @@ -390,6 +410,14 @@ namespace cppast do_write_token_seq(punct); } + /// \effects Writes a string for an excluded target or return type for the given entity. + /// The base class version writes the identifier `excluded`. + virtual void do_write_excluded(const cpp_entity& e) + { + (void)e; + do_write_identifier("excluded"); + } + /// \effects Writes a newline. /// It is guaranteed that this is the only way a newline will be printed. /// The base class forwards to `do_write_token_seq()`. diff --git a/src/code_generator.cpp b/src/code_generator.cpp index a54e938..fa78193 100644 --- a/src/code_generator.cpp +++ b/src/code_generator.cpp @@ -146,8 +146,15 @@ namespace { code_generator::output output(type_safe::ref(generator), type_safe::ref(alias), false); if (output) + { output << keyword("namespace") << whitespace << identifier(alias.name()) - << punctuation("=") << alias.target() << punctuation(";") << newl; + << punctuation("="); + if (output.options() & code_generator::exclude_target) + output.excluded(alias); + else + output << alias.target(); + output << punctuation(";") << newl; + } } void generate_using_directive(code_generator& generator, const cpp_using_directive& directive) @@ -175,7 +182,10 @@ namespace { output << keyword("using") << whitespace << identifier(alias.name()) << punctuation("="); - detail::write_type(output, alias.underlying_type(), ""); + if (output.options() & code_generator::exclude_target) + output.excluded(alias); + else + detail::write_type(output, alias.underlying_type(), ""); output << punctuation(";") << newl; } } @@ -485,7 +495,9 @@ namespace output << keyword("friend") << whitespace; write_storage_class(output, func.storage_class(), func.is_constexpr()); - if (detail::is_complex_type(func.return_type())) + if (output.options() & code_generator::exclude_return) + output.excluded(func) << whitespace; + else if (detail::is_complex_type(func.return_type())) output << keyword("auto") << whitespace; else { @@ -504,7 +516,8 @@ namespace write_function_parameters(output, func); write_noexcept(output, func, false); - if (detail::is_complex_type(func.return_type())) + if (!(output.options() & code_generator::exclude_return) + && detail::is_complex_type(func.return_type())) { output << punctuation("->"); detail::write_type(output, func.return_type(), ""); @@ -579,7 +592,9 @@ namespace else write_prefix_virtual(output, func.virtual_info()); - if (detail::is_complex_type(func.return_type())) + if (output.options() & code_generator::exclude_return) + output.excluded(func) << whitespace; + else if (detail::is_complex_type(func.return_type())) output << keyword("auto") << whitespace; else { @@ -599,7 +614,8 @@ namespace auto need_ws = write_cv_ref(output, func); write_noexcept(output, func, need_ws); - if (detail::is_complex_type(func.return_type())) + if (!(output.options() & code_generator::exclude_return) + && detail::is_complex_type(func.return_type())) { output << punctuation("->"); detail::write_type(output, func.return_type(), ""); @@ -628,7 +644,11 @@ namespace auto pos = op.name().find("operator"); output << identifier(op.name().substr(0u, pos)) << keyword("operator") << whitespace; - detail::write_type(output, op.return_type(), ""); + if (output.options() & code_generator::exclude_return) + output.excluded(op); + else + detail::write_type(output, op.return_type(), ""); + output << punctuation("(") << punctuation(")"); auto need_ws = write_cv_ref(output, op); write_noexcept(output, op, need_ws); diff --git a/test/code_generator.cpp b/test/code_generator.cpp index 8ed4108..e6e9d56 100644 --- a/test/code_generator.cpp +++ b/test/code_generator.cpp @@ -6,11 +6,15 @@ #include "test_parser.hpp" +using namespace cppast; + TEST_CASE("code_generator") { - // no need to check much here, as each entity check separately - // only write some file with equivalent code and synopsis - auto code = R"(using type=int; + SECTION("basic") + { + // no need to check much here, as each entity check separately + // only write some file with equivalent code and synopsis + auto code = R"(using type=int; struct foo{ int a; @@ -34,6 +38,60 @@ void func(int(*)(int)); extern void(* ptr)(int(*)(int))=&func; )"; - auto file = parse({}, "code_generator.cpp", code); - REQUIRE(get_code(*file) == code); + auto file = parse({}, "code_generator.cpp", code); + REQUIRE(get_code(*file) == code); + } + SECTION("exclude target") + { + auto code = R"( +namespace a {} + +namespace b = a; + +using c = int*; +typedef int d; +)"; + + auto synopsis = R"(namespace a{ +} + +namespace b=excluded; + +using c=excluded; + +using d=excluded; +)"; + + auto file = parse({}, "code_generator_exclude_target.cpp", code); + REQUIRE(get_code(*file, code_generator::exclude_target) == synopsis); + } + SECTION("exclude return") + { + auto code = R"( +void a(); +template +auto b() -> int*; + +struct foo +{ + int c() const&; + operator const int (); +}; +)"; + + auto synopsis = R"(excluded a(); + +template +excluded b(); + +struct foo{ + excluded c()const&; + + operator excluded(); +}; +)"; + + auto file = parse({}, "code_generator_exclude_return.cpp", code); + REQUIRE(get_code(*file, code_generator::exclude_return) == synopsis); + } } diff --git a/test/test_parser.hpp b/test/test_parser.hpp index 5eedfd4..84047f8 100644 --- a/test/test_parser.hpp +++ b/test/test_parser.hpp @@ -45,12 +45,26 @@ inline std::unique_ptr parse(const cppast::cpp_entity_index& i class test_generator : public cppast::code_generator { public: + test_generator(synopsis_options options) : options_(std::move(options)) + { + } + const std::string& str() const noexcept { return str_; } private: + synopsis_options on_container_begin(const cppast::cpp_entity&) override + { + return options_; + } + + synopsis_options on_leaf(const cppast::cpp_entity&) override + { + return options_; + } + void do_indent() override { ++indent_; @@ -78,14 +92,16 @@ private: was_newline_ = true; } - std::string str_; - unsigned indent_ = 0; - bool was_newline_ = false; + std::string str_; + synopsis_options options_; + unsigned indent_ = 0; + bool was_newline_ = false; }; -inline std::string get_code(const cppast::cpp_entity& e) +inline std::string get_code(const cppast::cpp_entity& e, + cppast::code_generator::synopsis_options options = {}) { - test_generator generator; + test_generator generator(options); cppast::generate_code(generator, e); auto str = generator.str(); if (!str.empty() && str.back() == '\n') diff --git a/tool/main.cpp b/tool/main.cpp index 45558b1..1e656c4 100644 --- a/tool/main.cpp +++ b/tool/main.cpp @@ -82,14 +82,14 @@ void print_entity(std::ostream& out, const cppast::cpp_entity& e) synopsis_options on_container_begin(const cppast::cpp_entity&) override { // generate declaration only - return synopsis_options::declaration; + return code_generator::declaration; } // called before code generation of a leaf entity synopsis_options on_leaf(const cppast::cpp_entity&) override { // generate declaration only - return synopsis_options::declaration; + return code_generator::declaration; } // no need to handle indentation, as only a single line is used From 27fd67bad12f674c3ee0f322620013ea1c5d1ab4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20M=C3=BCller?= Date: Thu, 25 May 2017 21:06:47 +0200 Subject: [PATCH 2/4] =?UTF-8?q?[breaking]=20Rename=20synopsis=5Foptions=20?= =?UTF-8?q?=E2=80=94>=20generation=5Foptions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit More consistent. --- include/cppast/code_generator.hpp | 16 ++++++++-------- test/test_parser.hpp | 18 +++++++++--------- tool/main.cpp | 4 ++-- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/include/cppast/code_generator.hpp b/include/cppast/code_generator.hpp index 3ed6fb3..53091cf 100644 --- a/include/cppast/code_generator.hpp +++ b/include/cppast/code_generator.hpp @@ -130,8 +130,8 @@ namespace cppast code_generator& operator=(const code_generator&) = delete; virtual ~code_generator() noexcept = default; - /// Flags that control the synopsis. - enum synopsis_flags + /// Flags that control the generation. + enum generation_flags { exclude, //< Exclude the entire entity. exclude_return, //< Exclude the return type of a function entity. @@ -140,8 +140,8 @@ namespace cppast _flag_set_size, //< \exclude }; - /// Options that control the synopsis. - using synopsis_options = type_safe::flag_set; + /// Options that control the generation. + using generation_options = type_safe::flag_set; /// Sentinel type used to output a given entity. class output @@ -184,7 +184,7 @@ namespace cppast } /// \returns The synopsis options. - synopsis_options options() const noexcept + generation_options options() const noexcept { return options_; } @@ -319,7 +319,7 @@ namespace cppast private: type_safe::object_ref gen_; type_safe::optional_ref e_; - synopsis_options options_; + generation_options options_; }; protected: @@ -330,7 +330,7 @@ namespace cppast /// The base class version has no effect. /// \returns The synopsis options for that entity, /// the base class version always returns no special options. - virtual synopsis_options on_container_begin(const cpp_entity& e) + virtual generation_options on_container_begin(const cpp_entity& e) { (void)e; return {}; @@ -347,7 +347,7 @@ namespace cppast /// The base class version has no effect. /// \returns The synopsis options for that entity, /// the base class version always returns no special options. - virtual synopsis_options on_leaf(const cpp_entity& e) + virtual generation_options on_leaf(const cpp_entity& e) { (void)e; return {}; diff --git a/test/test_parser.hpp b/test/test_parser.hpp index 84047f8..70f4c03 100644 --- a/test/test_parser.hpp +++ b/test/test_parser.hpp @@ -45,7 +45,7 @@ inline std::unique_ptr parse(const cppast::cpp_entity_index& i class test_generator : public cppast::code_generator { public: - test_generator(synopsis_options options) : options_(std::move(options)) + test_generator(generation_options options) : options_(std::move(options)) { } @@ -55,12 +55,12 @@ public: } private: - synopsis_options on_container_begin(const cppast::cpp_entity&) override + generation_options on_container_begin(const cppast::cpp_entity&) override { return options_; } - synopsis_options on_leaf(const cppast::cpp_entity&) override + generation_options on_leaf(const cppast::cpp_entity&) override { return options_; } @@ -92,14 +92,14 @@ private: was_newline_ = true; } - std::string str_; - synopsis_options options_; - unsigned indent_ = 0; - bool was_newline_ = false; + std::string str_; + generation_options options_; + unsigned indent_ = 0; + bool was_newline_ = false; }; -inline std::string get_code(const cppast::cpp_entity& e, - cppast::code_generator::synopsis_options options = {}) +inline std::string get_code(const cppast::cpp_entity& e, + cppast::code_generator::generation_options options = {}) { test_generator generator(options); cppast::generate_code(generator, e); diff --git a/tool/main.cpp b/tool/main.cpp index 1e656c4..1704894 100644 --- a/tool/main.cpp +++ b/tool/main.cpp @@ -79,14 +79,14 @@ void print_entity(std::ostream& out, const cppast::cpp_entity& e) private: // called at the beginning of the code generation of a container entity (i.e. one with child) - synopsis_options on_container_begin(const cppast::cpp_entity&) override + generation_options on_container_begin(const cppast::cpp_entity&) override { // generate declaration only return code_generator::declaration; } // called before code generation of a leaf entity - synopsis_options on_leaf(const cppast::cpp_entity&) override + generation_options on_leaf(const cppast::cpp_entity&) override { // generate declaration only return code_generator::declaration; From 59eee1f75a701432cb6766cdd42adc8ae7a0f7d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20M=C3=BCller?= Date: Thu, 25 May 2017 21:25:25 +0200 Subject: [PATCH 3/4] Remove duplicate newl at end of code generation for a file --- src/code_generator.cpp | 6 ++++-- test/code_generator.cpp | 9 +++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/code_generator.cpp b/src/code_generator.cpp index fa78193..2a6dc53 100644 --- a/src/code_generator.cpp +++ b/src/code_generator.cpp @@ -60,8 +60,10 @@ namespace code_generator::output output(type_safe::ref(generator), type_safe::ref(f), true); if (output) { - write_container(output, f, newl); - output << newl; + auto need_sep = write_container(output, f, newl); + if (!need_sep) + // file empty, write newl + output << newl; } } diff --git a/test/code_generator.cpp b/test/code_generator.cpp index e6e9d56..7f0cd7e 100644 --- a/test/code_generator.cpp +++ b/test/code_generator.cpp @@ -35,8 +35,7 @@ enum class bar void func(int(*)(int)); -extern void(* ptr)(int(*)(int))=&func; -)"; +extern void(* ptr)(int(*)(int))=&func;)"; auto file = parse({}, "code_generator.cpp", code); REQUIRE(get_code(*file) == code); @@ -49,8 +48,7 @@ namespace a {} namespace b = a; using c = int*; -typedef int d; -)"; +typedef int d;)"; auto synopsis = R"(namespace a{ } @@ -59,8 +57,7 @@ namespace b=excluded; using c=excluded; -using d=excluded; -)"; +using d=excluded;)"; auto file = parse({}, "code_generator_exclude_target.cpp", code); REQUIRE(get_code(*file, code_generator::exclude_target) == synopsis); From 315fb3a98f311925ebe9eb45e9527811855df041 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20M=C3=BCller?= Date: Thu, 25 May 2017 23:22:59 +0200 Subject: [PATCH 4/4] [breaking] Fix generation when excluding entities Had to change generation options retrieval. --- include/cppast/code_generator.hpp | 39 +++-- src/code_generator.cpp | 245 ++++++++++++++++++------------ test/code_generator.cpp | 79 +++++++++- test/test_parser.hpp | 7 +- tool/main.cpp | 11 +- 5 files changed, 253 insertions(+), 128 deletions(-) diff --git a/include/cppast/code_generator.hpp b/include/cppast/code_generator.hpp index 53091cf..d0cd653 100644 --- a/include/cppast/code_generator.hpp +++ b/include/cppast/code_generator.hpp @@ -156,10 +156,15 @@ namespace cppast /// respectively. explicit output(type_safe::object_ref gen, type_safe::object_ref e, bool is_container) - : gen_(gen), options_(is_container ? gen_->on_container_begin(*e) : gen_->on_leaf(*e)) + : gen_(gen), options_(gen->do_get_options(*e)) { if (is_container) + { + gen_->on_container_begin(*e); e_ = e; + } + else + gen_->on_leaf(*e); } /// \effects If the entity is a container @@ -183,12 +188,18 @@ namespace cppast return options_ != exclude; } - /// \returns The synopsis options. + /// \returns The generation options. generation_options options() const noexcept { return options_; } + /// \returns The generation options for the given entity. + generation_options options(const cpp_entity& e) const noexcept + { + return gen_->do_get_options(e); + } + /// \returns Whether or not the definition should be generated as well. bool generate_definition() const noexcept { @@ -326,16 +337,21 @@ namespace cppast code_generator() noexcept = default; private: - /// \effects Will be invoked before code of a container entity is generated. - /// The base class version has no effect. - /// \returns The synopsis options for that entity, - /// the base class version always returns no special options. - virtual generation_options on_container_begin(const cpp_entity& e) + /// \returns The generation options for that entity. + /// The base class version always returns no special options. + virtual generation_options do_get_options(const cpp_entity& e) { (void)e; return {}; } + /// \effects Will be invoked before code of a container entity is generated. + /// The base class version has no effect. + virtual void on_container_begin(const cpp_entity& e) + { + (void)e; + } + /// \effects Will be invoked after all code of a container entity has been generated. /// The base class version has no effect. virtual void on_container_end(const cpp_entity& e) @@ -345,12 +361,9 @@ namespace cppast /// \effects Will be invoked before code of a non-container entity is generated. /// The base class version has no effect. - /// \returns The synopsis options for that entity, - /// the base class version always returns no special options. - virtual generation_options on_leaf(const cpp_entity& e) + virtual void on_leaf(const cpp_entity& e) { (void)e; - return {}; } /// \effects Will be invoked when the indentation level should be increased by one. @@ -441,7 +454,9 @@ namespace cppast /// The implementation will write whitespace only where necessary, /// but a newline after each entity. /// This allows customization of formatting. - void generate_code(code_generator& generator, const cpp_entity& e); + /// + /// \returns Whether or not any code was actually written. + bool generate_code(code_generator& generator, const cpp_entity& e); /// \exclude class cpp_template_argument; diff --git a/src/code_generator.cpp b/src/code_generator.cpp index 2a6dc53..17fd928 100644 --- a/src/code_generator.cpp +++ b/src/code_generator.cpp @@ -48,14 +48,12 @@ namespace { if (need_sep) write_sep(output, s); - else - need_sep = true; - generate_code(*output.generator(), child); + need_sep = generate_code(*output.generator(), child); } return need_sep; } - void generate_file(code_generator& generator, const cpp_file& f) + bool generate_file(code_generator& generator, const cpp_file& f) { code_generator::output output(type_safe::ref(generator), type_safe::ref(f), true); if (output) @@ -65,9 +63,10 @@ namespace // file empty, write newl output << newl; } + return static_cast(output); } - void generate_macro_definition(code_generator& generator, const cpp_macro_definition& def) + bool generate_macro_definition(code_generator& generator, const cpp_macro_definition& def) { code_generator::output output(type_safe::ref(generator), type_safe::ref(def), false); if (output) @@ -81,9 +80,10 @@ namespace else output << newl; } + return static_cast(output); } - void generate_include_directive(code_generator& generator, const cpp_include_directive& include) + bool generate_include_directive(code_generator& generator, const cpp_include_directive& include) { code_generator::output output(type_safe::ref(generator), type_safe::ref(include), false); if (output) @@ -100,9 +100,10 @@ namespace output << preprocessor_token("\""); output << newl; } + return static_cast(output); } - void generate_language_linkage(code_generator& generator, const cpp_language_linkage& linkage) + bool generate_language_linkage(code_generator& generator, const cpp_language_linkage& linkage) { code_generator::output output(type_safe::ref(generator), type_safe::ref(linkage), true); if (output) @@ -124,9 +125,10 @@ namespace generate_code(generator, *linkage.begin()); } } + return static_cast(output); } - void generate_namespace(code_generator& generator, const cpp_namespace& ns) + bool generate_namespace(code_generator& generator, const cpp_namespace& ns) { code_generator::output output(type_safe::ref(generator), type_safe::ref(ns), true); if (output) @@ -142,9 +144,10 @@ namespace output.unindent(); output << punctuation("}") << newl; } + return static_cast(output); } - void generate_namespace_alias(code_generator& generator, const cpp_namespace_alias& alias) + bool generate_namespace_alias(code_generator& generator, const cpp_namespace_alias& alias) { code_generator::output output(type_safe::ref(generator), type_safe::ref(alias), false); if (output) @@ -157,17 +160,19 @@ namespace output << alias.target(); output << punctuation(";") << newl; } + return static_cast(output); } - void generate_using_directive(code_generator& generator, const cpp_using_directive& directive) + bool generate_using_directive(code_generator& generator, const cpp_using_directive& directive) { code_generator::output output(type_safe::ref(generator), type_safe::ref(directive), false); if (output) output << keyword("using") << whitespace << keyword("namespace") << whitespace << directive.target() << punctuation(";") << newl; + return static_cast(output); } - void generate_using_declaration(code_generator& generator, + bool generate_using_declaration(code_generator& generator, const cpp_using_declaration& declaration) { code_generator::output output(type_safe::ref(generator), type_safe::ref(declaration), @@ -175,9 +180,10 @@ namespace if (output) output << keyword("using") << whitespace << declaration.target() << punctuation(";") << newl; + return static_cast(output); } - void generate_type_alias(code_generator& generator, const cpp_type_alias& alias) + bool generate_type_alias(code_generator& generator, const cpp_type_alias& alias) { code_generator::output output(type_safe::ref(generator), type_safe::ref(alias), false); if (output) @@ -190,9 +196,10 @@ namespace detail::write_type(output, alias.underlying_type(), ""); output << punctuation(";") << newl; } + return static_cast(output); } - void generate_enum_value(code_generator& generator, const cpp_enum_value& value) + bool generate_enum_value(code_generator& generator, const cpp_enum_value& value) { code_generator::output output(type_safe::ref(generator), type_safe::ref(value), false); if (output) @@ -207,9 +214,10 @@ namespace .value()); // should have named something differently... } } + return static_cast(output); } - void generate_enum(code_generator& generator, const cpp_enum& e) + bool generate_enum(code_generator& generator, const cpp_enum& e) { code_generator::output output(type_safe::ref(generator), type_safe::ref(e), true); if (output) @@ -241,20 +249,25 @@ namespace else output << punctuation(";") << newl; } + return static_cast(output); } - void generate_access_specifier(code_generator& generator, const cpp_access_specifier& access) + void write_access_specifier(code_generator::output& output, cpp_access_specifier_kind access) + { + output.unindent(); + output << keyword(to_string(access)) << punctuation(":"); + output.indent(); + } + + bool generate_access_specifier(code_generator& generator, const cpp_access_specifier& access) { code_generator::output output(type_safe::ref(generator), type_safe::ref(access), false); if (output) - { - output.unindent(); - output << keyword(to_string(access.access_specifier())) << punctuation(":"); - output.indent(false); - } + write_access_specifier(output, access.access_specifier()); + return static_cast(output); } - void generate_base_class(code_generator& generator, const cpp_base_class& base) + bool generate_base_class(code_generator& generator, const cpp_base_class& base) { DEBUG_ASSERT(base.parent() && base.parent().value().kind() == cpp_entity_kind::class_t, detail::assert_handler{}); @@ -276,6 +289,7 @@ namespace output << identifier(base.name()); } + return static_cast(output); } void write_specialization_arguments(code_generator::output& output, @@ -287,7 +301,24 @@ namespace output << punctuation("<") << token_seq(spec.unexposed_arguments()) << punctuation(">"); } - void generate_class(code_generator& generator, const cpp_class& c, + void write_bases(code_generator& generator, code_generator::output& output, const cpp_class& c) + { + auto need_sep = false; + auto first = true; + for (auto& base : c.bases()) + { + if (first && !output.options(base).is_set(code_generator::exclude)) + { + first = false; + output << newl << punctuation(":"); + } + else if (need_sep) + output << punctuation(","); + need_sep = generate_base_class(generator, base); + } + } + + bool generate_class(code_generator& generator, const cpp_class& c, type_safe::optional_ref spec = nullptr) { code_generator::output output(type_safe::ref(generator), type_safe::ref(c), true); @@ -313,48 +344,33 @@ namespace output << punctuation(";") << newl; else { - if (!c.bases().empty()) - { - output << newl << punctuation(":"); - - auto need_sep = false; - for (auto& base : c.bases()) - { - if (need_sep) - output << punctuation(","); - else - need_sep = true; - generate_base_class(generator, base); - } - } + write_bases(generator, output, c); output << punctuation("{"); output.indent(); auto need_sep = false; auto last_access = c.class_kind() == cpp_class_kind::class_t ? cpp_private : cpp_public; + auto last_written_access = last_access; for (auto& member : c) { if (member.kind() == cpp_entity_kind::access_specifier_t) { auto& access = static_cast(member); - if (access.access_specifier() != last_access) - { - if (need_sep) - output << newl; - else - need_sep = true; - generate_access_specifier(generator, access); - last_access = access.access_specifier(); - } + last_access = access.access_specifier(); } + else if (output.options(member).is_set(code_generator::exclude)) + continue; else { if (need_sep) output << newl; - else - need_sep = true; - generate_code(generator, member); + if (last_access != last_written_access) + { + write_access_specifier(output, last_access); + last_written_access = last_access; + } + need_sep = generate_code(generator, member); } } @@ -362,9 +378,10 @@ namespace output << punctuation("};") << newl; } } + return static_cast(output); } - void write_variable_base(code_generator::output& output, const cpp_variable_base& var, + bool write_variable_base(code_generator::output& output, const cpp_variable_base& var, const std::string& name) { detail::write_type(output, var.type(), name); @@ -374,6 +391,7 @@ namespace output << punctuation("="); detail::write_expression(output, var.default_value().value()); } + return static_cast(output); } void write_storage_class(code_generator::output& output, cpp_storage_class_specifiers storage, @@ -389,7 +407,7 @@ namespace output << keyword("constexpr") << whitespace; } - void generate_variable(code_generator& generator, const cpp_variable& var) + bool generate_variable(code_generator& generator, const cpp_variable& var) { code_generator::output output(type_safe::ref(generator), type_safe::ref(var), false); if (output) @@ -399,9 +417,10 @@ namespace write_variable_base(output, var, var.name()); output << punctuation(";") << newl; } + return static_cast(output); } - void generate_member_variable(code_generator& generator, const cpp_member_variable& var) + bool generate_member_variable(code_generator& generator, const cpp_member_variable& var) { code_generator::output output(type_safe::ref(generator), type_safe::ref(var), false); if (output) @@ -411,9 +430,10 @@ namespace write_variable_base(output, var, var.name()); output << punctuation(";") << newl; } + return static_cast(output); } - void generate_bitfield(code_generator& generator, const cpp_bitfield& var) + bool generate_bitfield(code_generator& generator, const cpp_bitfield& var) { code_generator::output output(type_safe::ref(generator), type_safe::ref(var), false); if (output) @@ -424,13 +444,15 @@ namespace output << punctuation(":") << int_literal(std::to_string(var.no_bits())); output << punctuation(";") << newl; } + return static_cast(output); } - void generate_function_parameter(code_generator& generator, const cpp_function_parameter& param) + bool generate_function_parameter(code_generator& generator, const cpp_function_parameter& param) { code_generator::output output(type_safe::ref(generator), type_safe::ref(param), false); if (output) write_variable_base(output, param, param.name()); + return static_cast(output); } void write_function_parameters(code_generator::output& output, const cpp_function_base& base) @@ -486,7 +508,7 @@ namespace } } - void generate_function( + bool generate_function( code_generator& generator, const cpp_function& func, type_safe::optional_ref spec = nullptr) { @@ -526,6 +548,7 @@ namespace } write_function_body(output, func, false); } + return static_cast(output); } void write_prefix_virtual(code_generator::output& output, const cpp_virtual& virt) @@ -580,7 +603,7 @@ namespace return need_ws; } - void generate_member_function( + bool generate_member_function( code_generator& generator, const cpp_member_function& func, type_safe::optional_ref spec = nullptr) { @@ -626,9 +649,10 @@ namespace write_suffix_virtual(output, func.virtual_info()); write_function_body(output, func, is_pure(func.virtual_info())); } + return static_cast(output); } - void generate_conversion_op(code_generator& generator, const cpp_conversion_op& op) + bool generate_conversion_op(code_generator& generator, const cpp_conversion_op& op) { code_generator::output output(type_safe::ref(generator), type_safe::ref(op), true); if (output) @@ -658,9 +682,10 @@ namespace write_suffix_virtual(output, op.virtual_info()); write_function_body(output, op, is_pure(op.virtual_info())); } + return static_cast(output); } - void generate_constructor(code_generator& generator, const cpp_constructor& ctor) + bool generate_constructor(code_generator& generator, const cpp_constructor& ctor) { code_generator::output output(type_safe::ref(generator), type_safe::ref(ctor), true); if (output) @@ -678,9 +703,10 @@ namespace write_function_body(output, ctor, false); } + return static_cast(output); } - void generate_destructor(code_generator& generator, const cpp_destructor& dtor) + bool generate_destructor(code_generator& generator, const cpp_destructor& dtor) { code_generator::output output(type_safe::ref(generator), type_safe::ref(dtor), true); if (output) @@ -695,35 +721,34 @@ namespace write_suffix_virtual(output, dtor.virtual_info()); write_function_body(output, dtor, is_pure(dtor.virtual_info())); } + return static_cast(output); } - void generate_function_base(code_generator& generator, const cpp_function_base& base, + bool generate_function_base(code_generator& generator, const cpp_function_base& base, const cpp_template_specialization& spec) { switch (base.kind()) { case cpp_entity_kind::function_t: - generate_function(generator, static_cast(base), - type_safe::ref(spec)); - break; - case cpp_entity_kind::member_function_t: - generate_member_function(generator, static_cast(base), + return generate_function(generator, static_cast(base), type_safe::ref(spec)); - break; + case cpp_entity_kind::member_function_t: + return generate_member_function(generator, + static_cast(base), + type_safe::ref(spec)); case cpp_entity_kind::conversion_op_t: - generate_conversion_op(generator, static_cast(base)); - break; + return generate_conversion_op(generator, static_cast(base)); case cpp_entity_kind::constructor_t: - generate_constructor(generator, static_cast(base)); - break; + return generate_constructor(generator, static_cast(base)); default: DEBUG_UNREACHABLE(detail::assert_handler{}); break; } + return false; } - void generate_friend(code_generator& generator, const cpp_friend& f) + bool generate_friend(code_generator& generator, const cpp_friend& f) { code_generator::output output(type_safe::ref(generator), type_safe::ref(f), true); if (output) @@ -739,9 +764,10 @@ namespace else DEBUG_UNREACHABLE(detail::assert_handler{}); } + return static_cast(output); } - void generate_template_type_parameter(code_generator& generator, + bool generate_template_type_parameter(code_generator& generator, const cpp_template_type_parameter& param) { code_generator::output output(type_safe::ref(generator), type_safe::ref(param), false); @@ -758,9 +784,10 @@ namespace detail::write_type(output, param.default_type().value(), ""); } } + return static_cast(output); } - void generate_non_type_template_parameter(code_generator& generator, + bool generate_non_type_template_parameter(code_generator& generator, const cpp_non_type_template_parameter& param) { code_generator::output output(type_safe::ref(generator), type_safe::ref(param), false); @@ -773,9 +800,10 @@ namespace detail::write_expression(output, param.default_value().value()); } } + return static_cast(output); } - void generate_template_template_parameter(code_generator& generator, + bool generate_template_template_parameter(code_generator& generator, const cpp_template_template_parameter& param) { code_generator::output output(type_safe::ref(generator), type_safe::ref(param), true); @@ -790,54 +818,68 @@ namespace if (param.default_template()) output << punctuation("=") << param.default_template().value(); } + return static_cast(output); } - void write_template_parameters(code_generator::output& output, const cpp_template& templ) + void write_template_parameters(code_generator::output& output, const cpp_template& templ, + bool hide_if_empty) { - output << keyword("template") << punctuation("<"); + if (!hide_if_empty) + output << keyword("template") << punctuation("<"); + auto need_sep = false; + auto first = hide_if_empty; for (auto& param : templ.parameters()) { - if (need_sep) + if (first + && !output.options(*templ.parameters().begin()).is_set(code_generator::exclude)) + { + first = false; + output << keyword("template") << punctuation("<"); + } + else if (need_sep) output << punctuation(","); - else - need_sep = true; - generate_code(*output.generator(), param); + need_sep = generate_code(*output.generator(), param); } - output << punctuation(">") << newl; + + if (!hide_if_empty || need_sep) + output << punctuation(">") << newl; } - void generate_alias_template(code_generator& generator, const cpp_alias_template& alias) + bool generate_alias_template(code_generator& generator, const cpp_alias_template& alias) { code_generator::output output(type_safe::ref(generator), type_safe::ref(alias), true); if (output) { - write_template_parameters(output, alias); + write_template_parameters(output, alias, true); generate_code(generator, alias.type_alias()); } + return static_cast(output); } - void generate_variable_template(code_generator& generator, const cpp_variable_template& var) + bool generate_variable_template(code_generator& generator, const cpp_variable_template& var) { code_generator::output output(type_safe::ref(generator), type_safe::ref(var), true); if (output) { - write_template_parameters(output, var); + write_template_parameters(output, var, true); generate_code(generator, var.variable()); } + return static_cast(output); } - void generate_function_template(code_generator& generator, const cpp_function_template& func) + bool generate_function_template(code_generator& generator, const cpp_function_template& func) { code_generator::output output(type_safe::ref(generator), type_safe::ref(func), true); if (output) { - write_template_parameters(output, func); + write_template_parameters(output, func, true); generate_code(generator, func.function()); } + return static_cast(output); } - void generate_function_template_specialization(code_generator& generator, + bool generate_function_template_specialization(code_generator& generator, const cpp_function_template_specialization& func) { code_generator::output output(type_safe::ref(generator), type_safe::ref(func), true); @@ -846,33 +888,36 @@ namespace DEBUG_ASSERT(func.is_full_specialization(), detail::assert_handler{}); if (!is_friended(func)) // don't write template parameters in friend - write_template_parameters(output, func); + write_template_parameters(output, func, false); generate_function_base(generator, func.function(), func); } + return static_cast(output); } - void generate_class_template(code_generator& generator, const cpp_class_template& templ) + bool generate_class_template(code_generator& generator, const cpp_class_template& templ) { code_generator::output output(type_safe::ref(generator), type_safe::ref(templ), true); if (output) { - write_template_parameters(output, templ); + write_template_parameters(output, templ, true); generate_class(generator, templ.class_()); } + return static_cast(output); } - void generate_class_template_specialization(code_generator& generator, + bool generate_class_template_specialization(code_generator& generator, const cpp_class_template_specialization& templ) { code_generator::output output(type_safe::ref(generator), type_safe::ref(templ), true); if (output) { - write_template_parameters(output, templ); + write_template_parameters(output, templ, false); generate_class(generator, templ.class_(), type_safe::ref(templ)); } + return static_cast(output); } - void generate_static_assert(code_generator& generator, const cpp_static_assert& assert) + bool generate_static_assert(code_generator& generator, const cpp_static_assert& assert) { code_generator::output output(type_safe::ref(generator), type_safe::ref(assert), false); if (output) @@ -882,24 +927,25 @@ namespace output << punctuation(",") << string_literal('"' + assert.message() + '"'); output << punctuation(");") << newl; } + return static_cast(output); } - void generate_unexposed(code_generator& generator, const cpp_unexposed_entity& entity) + bool generate_unexposed(code_generator& generator, const cpp_unexposed_entity& entity) { code_generator::output output(type_safe::ref(generator), type_safe::ref(entity), false); if (output) output << token_seq(entity.spelling()); + return static_cast(output); } } -void cppast::generate_code(code_generator& generator, const cpp_entity& e) +bool cppast::generate_code(code_generator& generator, const cpp_entity& e) { switch (e.kind()) { #define CPPAST_DETAIL_HANDLE(Name) \ case cpp_entity_kind::Name##_t: \ - generate_##Name(generator, static_cast(e)); \ - break; + return generate_##Name(generator, static_cast(e)); CPPAST_DETAIL_HANDLE(file) @@ -948,8 +994,7 @@ void cppast::generate_code(code_generator& generator, const cpp_entity& e) CPPAST_DETAIL_HANDLE(static_assert) case cpp_entity_kind::unexposed_t: - generate_unexposed(generator, static_cast(e)); - break; + return generate_unexposed(generator, static_cast(e)); #undef CPPAST_DETAIL_HANDLE @@ -957,6 +1002,8 @@ void cppast::generate_code(code_generator& generator, const cpp_entity& e) DEBUG_UNREACHABLE(detail::assert_handler{}); break; } + + return false; } void detail::write_template_arguments(code_generator::output& output, diff --git a/test/code_generator.cpp b/test/code_generator.cpp index 7f0cd7e..8a63411 100644 --- a/test/code_generator.cpp +++ b/test/code_generator.cpp @@ -85,10 +85,85 @@ struct foo{ excluded c()const&; operator excluded(); -}; -)"; +};)"; auto file = parse({}, "code_generator_exclude_return.cpp", code); REQUIRE(get_code(*file, code_generator::exclude_return) == synopsis); } + SECTION("exclude") + { + // exclude all entities starting with `e` + class exclude_generator : public test_generator + { + public: + using test_generator::test_generator; + + private: + generation_options do_get_options(const cpp_entity& e) override + { + if (e.name().front() == 'e') + return code_generator::exclude; + return {}; + } + }; + + auto code = R"( +void e(); + +void func(int a, int e, int c); + +template +void tfunc(int a); + +struct base {}; +struct e_t {}; + +struct bar : e_t, base {}; + +class foo : e_t +{ + int a; + +public: + int e1; + +private: + int b; + +public: + int c; + int e2; + +private: + int e3; +}; +)"; + + auto synopsis = R"(void func(int a,int c); + +void tfunc(int a); + +struct base{ +}; + +struct bar +:base{ +}; + +class foo{ + int a; + + int b; + +public: + int c; +}; +)"; + + auto file = parse({}, "code_generator_exclude.cpp", code); + + exclude_generator generator(code_generator::generation_options{}); + generate_code(generator, *file); + REQUIRE(generator.str() == synopsis); + } } diff --git a/test/test_parser.hpp b/test/test_parser.hpp index 70f4c03..ebb8048 100644 --- a/test/test_parser.hpp +++ b/test/test_parser.hpp @@ -55,12 +55,7 @@ public: } private: - generation_options on_container_begin(const cppast::cpp_entity&) override - { - return options_; - } - - generation_options on_leaf(const cppast::cpp_entity&) override + generation_options do_get_options(const cppast::cpp_entity&) override { return options_; } diff --git a/tool/main.cpp b/tool/main.cpp index 1704894..2f90d87 100644 --- a/tool/main.cpp +++ b/tool/main.cpp @@ -78,15 +78,8 @@ void print_entity(std::ostream& out, const cppast::cpp_entity& e) } private: - // called at the beginning of the code generation of a container entity (i.e. one with child) - generation_options on_container_begin(const cppast::cpp_entity&) override - { - // generate declaration only - return code_generator::declaration; - } - - // called before code generation of a leaf entity - generation_options on_leaf(const cppast::cpp_entity&) override + // called to retrieve the generation options of an entity + generation_options do_get_options(const cppast::cpp_entity&) override { // generate declaration only return code_generator::declaration;