Split out enum trait generation
This change makes the generation of EnumTraits configurable. With this
change we can see reductions in code size by up over 1MB. Example file
size changes:
|File | src (KB) | .h (KB) before | .h (KB) after |
|------------------|----------|----------------|---------------|
| hci_events.emb | 76 | 5356 | 3884 |
| hci_commands.emb | 108 | 5612 | 4268 |
| hci_vendor.emb | 32 | 1748 | 1304 |
| hci_common.emb | 32 | 644 | 368 |
| hci_data.emb | 4 | 152 | 104 |
| l2cap_frames.emb | 4 | 48 | 40 |
| hci_h4.emb | 4 | 8 | 4 |
This is also helpful for embedded users who couldn't use emboss due to
dependencies on string libraries and functions like `sscanf`.
diff --git a/compiler/back_end/cpp/generated_code_templates b/compiler/back_end/cpp/generated_code_templates
index df9ecac..a0acc68 100644
--- a/compiler/back_end/cpp/generated_code_templates
+++ b/compiler/back_end/cpp/generated_code_templates
@@ -179,57 +179,7 @@
emboss_reserved_local_other.IntrinsicSizeIn${units}().Read());
}
- template <class Stream>
- bool UpdateFromTextStream(Stream *emboss_reserved_local_stream) const {
- ::std::string emboss_reserved_local_brace;
- if (!::emboss::support::ReadToken(emboss_reserved_local_stream,
- &emboss_reserved_local_brace))
- return false;
- if (emboss_reserved_local_brace != "{") return false;
- for (;;) {
- ::std::string emboss_reserved_local_name;
- if (!::emboss::support::ReadToken(emboss_reserved_local_stream,
- &emboss_reserved_local_name))
- return false;
- if (emboss_reserved_local_name == ",")
- if (!::emboss::support::ReadToken(emboss_reserved_local_stream,
- &emboss_reserved_local_name))
- return false;
- if (emboss_reserved_local_name == "}") return true;
- ::std::string emboss_reserved_local_colon;
- if (!::emboss::support::ReadToken(emboss_reserved_local_stream,
- &emboss_reserved_local_colon))
- return false;
- if (emboss_reserved_local_colon != ":") return false;
-${decode_fields}
- // decode_fields will `continue` if it successfully finds a field.
- return false;
- }
- }
-
- template <class Stream>
- void WriteToTextStream(
- Stream *emboss_reserved_local_stream,
- ::emboss::TextOutputOptions emboss_reserved_local_options) const {
- ::emboss::TextOutputOptions emboss_reserved_local_field_options =
- emboss_reserved_local_options.PlusOneIndent();
- if (emboss_reserved_local_options.multiline()) {
- emboss_reserved_local_stream->Write("{\n");
- } else {
- emboss_reserved_local_stream->Write("{");
- }
- bool emboss_reserved_local_wrote_field = false;
-${write_fields}
- // Avoid unused variable warnings for empty structures:
- (void)emboss_reserved_local_wrote_field;
- if (emboss_reserved_local_options.multiline()) {
- emboss_reserved_local_stream->Write(
- emboss_reserved_local_options.current_indent());
- emboss_reserved_local_stream->Write("}");
- } else {
- emboss_reserved_local_stream->Write(" }");
- }
- }
+${text_stream_methods}
static constexpr bool IsAggregate() { return true; }
@@ -301,6 +251,60 @@
emboss_reserved_local_size);
}
+// ** struct_text_stream ** ////////////////////////////////////////////////////
+ template <class Stream>
+ bool UpdateFromTextStream(Stream *emboss_reserved_local_stream) const {
+ ::std::string emboss_reserved_local_brace;
+ if (!::emboss::support::ReadToken(emboss_reserved_local_stream,
+ &emboss_reserved_local_brace))
+ return false;
+ if (emboss_reserved_local_brace != "{") return false;
+ for (;;) {
+ ::std::string emboss_reserved_local_name;
+ if (!::emboss::support::ReadToken(emboss_reserved_local_stream,
+ &emboss_reserved_local_name))
+ return false;
+ if (emboss_reserved_local_name == ",")
+ if (!::emboss::support::ReadToken(emboss_reserved_local_stream,
+ &emboss_reserved_local_name))
+ return false;
+ if (emboss_reserved_local_name == "}") return true;
+ ::std::string emboss_reserved_local_colon;
+ if (!::emboss::support::ReadToken(emboss_reserved_local_stream,
+ &emboss_reserved_local_colon))
+ return false;
+ if (emboss_reserved_local_colon != ":") return false;
+${decode_fields}
+ // decode_fields will `continue` if it successfully finds a field.
+ return false;
+ }
+ }
+
+ template <class Stream>
+ void WriteToTextStream(
+ Stream *emboss_reserved_local_stream,
+ ::emboss::TextOutputOptions emboss_reserved_local_options) const {
+ ::emboss::TextOutputOptions emboss_reserved_local_field_options =
+ emboss_reserved_local_options.PlusOneIndent();
+ if (emboss_reserved_local_options.multiline()) {
+ emboss_reserved_local_stream->Write("{\n");
+ } else {
+ emboss_reserved_local_stream->Write("{");
+ }
+ bool emboss_reserved_local_wrote_field = false;
+${write_fields}
+ // Avoid unused variable warnings for empty structures:
+ (void)emboss_reserved_local_wrote_field;
+ if (emboss_reserved_local_options.multiline()) {
+ emboss_reserved_local_stream->Write(
+ emboss_reserved_local_options.current_indent());
+ emboss_reserved_local_stream->Write("}");
+ } else {
+ emboss_reserved_local_stream->Write(" }");
+ }
+ }
+
+
// ** decode_field ** //////////////////////////////////////////////////////////
// If the field name matches ${field_name}, handle it, otherwise fall
// through to the next field.
@@ -772,6 +776,7 @@
// "the output of this rule is an indeterminate number of files, all of which
// should be used as input to this other rule," which would be necessary to
// generate all the .cc files and then build and link them into a library.
+// ** enum_traits ** ///////////////////////////////////////////////////////////
template <class Enum>
class EnumTraits;
diff --git a/compiler/back_end/cpp/header_generator.py b/compiler/back_end/cpp/header_generator.py
index c671d6e..8b585ca 100644
--- a/compiler/back_end/cpp/header_generator.py
+++ b/compiler/back_end/cpp/header_generator.py
@@ -21,6 +21,7 @@
import collections
import pkgutil
import re
+from typing import NamedTuple
from compiler.back_end.cpp import attributes
from compiler.back_end.util import code_template
@@ -77,6 +78,8 @@
# TODO(bolms): This should be a command-line flag.
_PRELUDE_INCLUDE_FILE = "runtime/cpp/emboss_prelude.h"
+_ENUM_VIEW_INCLUDE_FILE = "runtime/cpp/emboss_enum_view.h"
+_TEXT_UTIL_INCLUDE_FILE = "runtime/cpp/emboss_text_util.h"
# Cases allowed in the `enum_case` attribute.
_SUPPORTED_ENUM_CASES = ("SHOUTY_CASE", "kCamelCase")
@@ -86,6 +89,13 @@
assert name_conversion.is_case_conversion_supported("SHOUTY_CASE", _enum_case)
+class Config(NamedTuple):
+ """Configuration for C++ header generation."""
+
+ include_enum_traits: bool = True
+ """Whether or not to include EnumTraits in the generated header."""
+
+
def _get_namespace_components(namespace):
"""Gets the components of a C++ namespace
@@ -124,7 +134,7 @@
return re.sub("['\"\\\\]", r"\\\0", string)
-def _get_includes(module):
+def _get_includes(module, config: Config):
"""Returns the appropriate #includes based on module's imports."""
includes = []
for import_ in module.foreign_import:
@@ -138,6 +148,13 @@
code_template.format_template(
_TEMPLATES.include,
file_name=_cpp_string_escape(_PRELUDE_INCLUDE_FILE)))
+ if config.include_enum_traits:
+ includes.extend(
+ [code_template.format_template(
+ _TEMPLATES.include,
+ file_name=_cpp_string_escape(file_name))
+ for file_name in (_ENUM_VIEW_INCLUDE_FILE, _TEXT_UTIL_INCLUDE_FILE)
+ ])
return "".join(includes)
@@ -1059,14 +1076,14 @@
return "", declaration, definition
-def _generate_subtype_definitions(type_ir, ir):
+def _generate_subtype_definitions(type_ir, ir, config: Config):
"""Generates C++ code for subtypes of type_ir."""
subtype_bodies = []
subtype_forward_declarations = []
subtype_method_definitions = []
type_name = type_ir.name.name.text
for subtype in type_ir.subtype:
- inner_defs = _generate_type_definition(subtype, ir)
+ inner_defs = _generate_type_definition(subtype, ir, config)
subtype_forward_declaration, subtype_body, subtype_methods = inner_defs
subtype_forward_declarations.append(subtype_forward_declaration)
subtype_bodies.append(subtype_body)
@@ -1096,7 +1113,7 @@
return name
-def _generate_structure_definition(type_ir, ir):
+def _generate_structure_definition(type_ir, ir, config: Config):
"""Generates C++ for an Emboss structure (struct or bits).
Arguments:
@@ -1108,7 +1125,7 @@
suitable for insertion into the appropriate places in the generated header.
"""
subtype_bodies, subtype_forward_declarations, subtype_method_definitions = (
- _generate_subtype_definitions(type_ir, ir))
+ _generate_subtype_definitions(type_ir, ir, config))
type_name = type_ir.name.name.text
field_helper_type_definitions = []
field_method_declarations = []
@@ -1221,6 +1238,15 @@
else:
requires_check = ""
+ if config.include_enum_traits:
+ text_stream_methods = code_template.format_template(
+ _TEMPLATES.struct_text_stream,
+ decode_fields="\n".join(decode_field_clauses),
+ write_fields="\n".join(write_field_clauses))
+ else:
+ text_stream_methods = ""
+
+
class_forward_declarations = code_template.format_template(
_TEMPLATES.structure_view_declaration,
name=type_name)
@@ -1234,9 +1260,8 @@
requires_check=requires_check,
equals_method_body="\n".join(equals_method_clauses),
unchecked_equals_method_body="\n".join(unchecked_equals_method_clauses),
- decode_fields="\n".join(decode_field_clauses),
enum_usings="\n".join(enum_using_statements),
- write_fields="\n".join(write_field_clauses),
+ text_stream_methods=text_stream_methods,
parameter_fields="\n".join(parameter_fields),
constructor_parameters="".join(constructor_parameters),
forwarded_parameters="".join(forwarded_parameters),
@@ -1314,7 +1339,7 @@
for case in cases]
-def _generate_enum_definition(type_ir):
+def _generate_enum_definition(type_ir, include_traits=True):
"""Generates C++ for an Emboss enum."""
enum_values = []
enum_from_string_statements = []
@@ -1333,48 +1358,52 @@
code_template.format_template(_TEMPLATES.enum_value,
name=enum_value_name,
value=_render_integer(numeric_value)))
-
- enum_from_string_statements.append(
- code_template.format_template(_TEMPLATES.enum_from_name_case,
- enum=type_ir.name.name.text,
- value=enum_value_name,
- name=value.name.name.text))
-
- if numeric_value not in previously_seen_numeric_values:
- string_from_enum_statements.append(
- code_template.format_template(_TEMPLATES.name_from_enum_case,
+ if include_traits:
+ enum_from_string_statements.append(
+ code_template.format_template(_TEMPLATES.enum_from_name_case,
enum=type_ir.name.name.text,
value=enum_value_name,
name=value.name.name.text))
- enum_is_known_statements.append(
- code_template.format_template(_TEMPLATES.enum_is_known_case,
- enum=type_ir.name.name.text,
- name=enum_value_name))
+ if numeric_value not in previously_seen_numeric_values:
+ string_from_enum_statements.append(
+ code_template.format_template(_TEMPLATES.name_from_enum_case,
+ enum=type_ir.name.name.text,
+ value=enum_value_name,
+ name=value.name.name.text))
+
+ enum_is_known_statements.append(
+ code_template.format_template(_TEMPLATES.enum_is_known_case,
+ enum=type_ir.name.name.text,
+ name=enum_value_name))
previously_seen_numeric_values.add(numeric_value)
- return (
- code_template.format_template(
+
+ declaration = code_template.format_template(
_TEMPLATES.enum_declaration,
enum=type_ir.name.name.text,
- enum_type=enum_type),
- code_template.format_template(
+ enum_type=enum_type)
+ definition = code_template.format_template(
_TEMPLATES.enum_definition,
enum=type_ir.name.name.text,
enum_type=enum_type,
- enum_values="".join(enum_values),
+ enum_values="".join(enum_values))
+ if include_traits:
+ definition += code_template.format_template(
+ _TEMPLATES.enum_traits,
+ enum=type_ir.name.name.text,
enum_from_name_cases="\n".join(enum_from_string_statements),
name_from_enum_cases="\n".join(string_from_enum_statements),
- enum_is_known_cases="\n".join(enum_is_known_statements)),
- ""
- )
+ enum_is_known_cases="\n".join(enum_is_known_statements))
+
+ return (declaration, definition, "")
-def _generate_type_definition(type_ir, ir):
+def _generate_type_definition(type_ir, ir, config: Config):
"""Generates C++ for an Emboss type."""
if type_ir.HasField("structure"):
- return _generate_structure_definition(type_ir, ir)
+ return _generate_structure_definition(type_ir, ir, config)
elif type_ir.HasField("enumeration"):
- return _generate_enum_definition(type_ir)
+ return _generate_enum_definition(type_ir, config.include_enum_traits)
elif type_ir.HasField("external"):
# TODO(bolms): This should probably generate an #include.
return "", "", ""
@@ -1549,7 +1578,7 @@
return []
-def generate_header(ir):
+def generate_header(ir, config=Config()):
"""Generates a C++ header from an Emboss module.
Arguments:
@@ -1569,7 +1598,7 @@
method_definitions = []
for type_definition in ir.module[0].type:
declaration, definition, methods = _generate_type_definition(
- type_definition, ir)
+ type_definition, ir, config)
type_declarations.append(declaration)
type_definitions.append(definition)
method_definitions.append(methods)
@@ -1579,7 +1608,7 @@
type_definitions="".join(type_definitions),
method_definitions="".join(method_definitions))
body = _wrap_in_namespace(body, _get_module_namespace(ir.module[0]))
- includes = _get_includes(ir.module[0])
+ includes = _get_includes(ir.module[0], config)
return code_template.format_template(
_TEMPLATES.outline,
includes=includes,