Add ability to exclude parts of generation

Also fix bugs when excluding entities.
This commit is contained in:
Jonathan Müller 2017-05-25 23:39:08 +02:00
commit 074759fafb
5 changed files with 394 additions and 148 deletions

View file

@ -8,6 +8,7 @@
#include <cstring>
#include <type_safe/index.hpp>
#include <type_safe/flag_set.hpp>
#include <cppast/cpp_entity.hpp>
#include <cppast/cpp_entity_ref.hpp>
@ -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 generation.
enum generation_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 generation.
using generation_options = type_safe::flag_set<generation_flags>;
/// Sentinel type used to output a given entity.
class output
{
@ -150,10 +156,15 @@ namespace cppast
/// respectively.
explicit output(type_safe::object_ref<code_generator> gen,
type_safe::object_ref<const cpp_entity> 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
@ -177,10 +188,22 @@ namespace cppast
return options_ != exclude;
}
/// \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
{
return options_ == definition;
return !(options_ & declaration);
}
/// \returns A reference to the generator.
@ -280,6 +303,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
{
@ -299,21 +330,26 @@ namespace cppast
private:
type_safe::object_ref<code_generator> gen_;
type_safe::optional_ref<const cpp_entity> e_;
synopsis_options options_;
generation_options options_;
};
protected:
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 `definition`.
virtual synopsis_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;
return definition;
}
/// \effects Will be invoked after all code of a container entity has been generated.
@ -325,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 `definition`.
virtual synopsis_options on_leaf(const cpp_entity& e)
virtual void on_leaf(const cpp_entity& e)
{
(void)e;
return definition;
}
/// \effects Will be invoked when the indentation level should be increased by one.
@ -390,6 +423,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()`.
@ -413,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;

View file

@ -48,24 +48,25 @@ 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)
{
write_container(output, f, newl);
output << newl;
auto need_sep = write_container(output, f, newl);
if (!need_sep)
// file empty, write newl
output << newl;
}
return static_cast<bool>(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)
@ -79,9 +80,10 @@ namespace
else
output << newl;
}
return static_cast<bool>(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)
@ -98,9 +100,10 @@ namespace
output << preprocessor_token("\"");
output << newl;
}
return static_cast<bool>(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)
@ -122,9 +125,10 @@ namespace
generate_code(generator, *linkage.begin());
}
}
return static_cast<bool>(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)
@ -140,25 +144,35 @@ namespace
output.unindent();
output << punctuation("}") << newl;
}
return static_cast<bool>(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)
{
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;
}
return static_cast<bool>(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<bool>(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),
@ -166,21 +180,26 @@ namespace
if (output)
output << keyword("using") << whitespace << declaration.target() << punctuation(";")
<< newl;
return static_cast<bool>(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)
{
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;
}
return static_cast<bool>(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)
@ -195,9 +214,10 @@ namespace
.value()); // should have named something differently...
}
}
return static_cast<bool>(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)
@ -229,20 +249,25 @@ namespace
else
output << punctuation(";") << newl;
}
return static_cast<bool>(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<bool>(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{});
@ -264,6 +289,7 @@ namespace
output << identifier(base.name());
}
return static_cast<bool>(output);
}
void write_specialization_arguments(code_generator::output& output,
@ -275,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<const cpp_template_specialization> spec = nullptr)
{
code_generator::output output(type_safe::ref(generator), type_safe::ref(c), true);
@ -301,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<const cpp_access_specifier&>(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);
}
}
@ -350,9 +378,10 @@ namespace
output << punctuation("};") << newl;
}
}
return static_cast<bool>(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);
@ -362,6 +391,7 @@ namespace
output << punctuation("=");
detail::write_expression(output, var.default_value().value());
}
return static_cast<bool>(output);
}
void write_storage_class(code_generator::output& output, cpp_storage_class_specifiers storage,
@ -377,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)
@ -387,9 +417,10 @@ namespace
write_variable_base(output, var, var.name());
output << punctuation(";") << newl;
}
return static_cast<bool>(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)
@ -399,9 +430,10 @@ namespace
write_variable_base(output, var, var.name());
output << punctuation(";") << newl;
}
return static_cast<bool>(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)
@ -412,13 +444,15 @@ namespace
output << punctuation(":") << int_literal(std::to_string(var.no_bits()));
output << punctuation(";") << newl;
}
return static_cast<bool>(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<bool>(output);
}
void write_function_parameters(code_generator::output& output, const cpp_function_base& base)
@ -474,7 +508,7 @@ namespace
}
}
void generate_function(
bool generate_function(
code_generator& generator, const cpp_function& func,
type_safe::optional_ref<const cpp_template_specialization> spec = nullptr)
{
@ -485,7 +519,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,13 +540,15 @@ 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(), "");
}
write_function_body(output, func, false);
}
return static_cast<bool>(output);
}
void write_prefix_virtual(code_generator::output& output, const cpp_virtual& virt)
@ -565,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<const cpp_template_specialization> spec = nullptr)
{
@ -579,7 +617,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 +639,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(), "");
@ -608,9 +649,10 @@ namespace
write_suffix_virtual(output, func.virtual_info());
write_function_body(output, func, is_pure(func.virtual_info()));
}
return static_cast<bool>(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)
@ -628,7 +670,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);
@ -636,9 +682,10 @@ namespace
write_suffix_virtual(output, op.virtual_info());
write_function_body(output, op, is_pure(op.virtual_info()));
}
return static_cast<bool>(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)
@ -656,9 +703,10 @@ namespace
write_function_body(output, ctor, false);
}
return static_cast<bool>(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)
@ -673,35 +721,34 @@ namespace
write_suffix_virtual(output, dtor.virtual_info());
write_function_body(output, dtor, is_pure(dtor.virtual_info()));
}
return static_cast<bool>(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<const cpp_function&>(base),
type_safe::ref(spec));
break;
case cpp_entity_kind::member_function_t:
generate_member_function(generator, static_cast<const cpp_member_function&>(base),
return generate_function(generator, static_cast<const cpp_function&>(base),
type_safe::ref(spec));
break;
case cpp_entity_kind::member_function_t:
return generate_member_function(generator,
static_cast<const cpp_member_function&>(base),
type_safe::ref(spec));
case cpp_entity_kind::conversion_op_t:
generate_conversion_op(generator, static_cast<const cpp_conversion_op&>(base));
break;
return generate_conversion_op(generator, static_cast<const cpp_conversion_op&>(base));
case cpp_entity_kind::constructor_t:
generate_constructor(generator, static_cast<const cpp_constructor&>(base));
break;
return generate_constructor(generator, static_cast<const cpp_constructor&>(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)
@ -717,9 +764,10 @@ namespace
else
DEBUG_UNREACHABLE(detail::assert_handler{});
}
return static_cast<bool>(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);
@ -736,9 +784,10 @@ namespace
detail::write_type(output, param.default_type().value(), "");
}
}
return static_cast<bool>(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);
@ -751,9 +800,10 @@ namespace
detail::write_expression(output, param.default_value().value());
}
}
return static_cast<bool>(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);
@ -768,54 +818,68 @@ namespace
if (param.default_template())
output << punctuation("=") << param.default_template().value();
}
return static_cast<bool>(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<bool>(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<bool>(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<bool>(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);
@ -824,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<bool>(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<bool>(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<bool>(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)
@ -860,24 +927,25 @@ namespace
output << punctuation(",") << string_literal('"' + assert.message() + '"');
output << punctuation(");") << newl;
}
return static_cast<bool>(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<bool>(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<const cpp_##Name&>(e)); \
break;
return generate_##Name(generator, static_cast<const cpp_##Name&>(e));
CPPAST_DETAIL_HANDLE(file)
@ -926,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<const cpp_unexposed_entity&>(e));
break;
return generate_unexposed(generator, static_cast<const cpp_unexposed_entity&>(e));
#undef CPPAST_DETAIL_HANDLE
@ -935,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,

View file

@ -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;
@ -31,9 +35,135 @@ 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);
}
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 <typename T>
auto b() -> int*;
struct foo
{
int c() const&;
operator const int ();
};
)";
auto file = parse({}, "code_generator.cpp", code);
REQUIRE(get_code(*file) == code);
auto synopsis = R"(excluded a();
template<typename T>
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);
}
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 <typename e1, typename e2>
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);
}
}

View file

@ -45,12 +45,21 @@ inline std::unique_ptr<cppast::cpp_file> parse(const cppast::cpp_entity_index& i
class test_generator : public cppast::code_generator
{
public:
test_generator(generation_options options) : options_(std::move(options))
{
}
const std::string& str() const noexcept
{
return str_;
}
private:
generation_options do_get_options(const cppast::cpp_entity&) override
{
return options_;
}
void do_indent() override
{
++indent_;
@ -78,14 +87,16 @@ private:
was_newline_ = true;
}
std::string str_;
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)
inline std::string get_code(const cppast::cpp_entity& e,
cppast::code_generator::generation_options options = {})
{
test_generator generator;
test_generator generator(options);
cppast::generate_code(generator, e);
auto str = generator.str();
if (!str.empty() && str.back() == '\n')

View file

@ -78,18 +78,11 @@ 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
// called to retrieve the generation options of an entity
generation_options do_get_options(const cppast::cpp_entity&) override
{
// generate declaration only
return synopsis_options::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