Skip to content

Commit 73e659b

Browse files
committed
x
1 parent 984df11 commit 73e659b

3 files changed

Lines changed: 40 additions & 4 deletions

File tree

documentation/idl_codegen.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ Defined as `google.protobuf.ServiceOptions` extensions:
8585
6. `api_member_name`
8686
Override delegate member name in `ServiceImpl` (default: `api_`).
8787
7. `base_services`
88-
Parent services to inherit from. You can specify a same-package short name (for example `Print`) or a fully-qualified name (for example `arduino.idl.Print` or `.arduino.idl.Print`).
88+
Parent services to inherit from. You can specify a same-package short name (for example `Print`) or a fully-qualified name (for example `arduino.idl.Print` or `.arduino.idl.Print`). Generated `Service` class declarations inherit all ancestor `Ifc` classes (direct and indirect) in lineage order.
8989

9090
## Recommended Mapping
9191

@@ -109,6 +109,7 @@ The generator intentionally fails when configuration is inconsistent:
109109
3. `generate_api_class = true` rejects methods with `emit_api = true` and `source_virtual = false`
110110
4. `generate_service_impl_class = true` with `generate_api_class = true` requires all `Service` methods to be callable on generated `Api`
111111
5. Cyclic `base_services` references are rejected
112+
6. If `base_services` is set and `generate_service_class = true`, each ancestor service must also set `generate_ifc_class = true`
112113

113114
## Example
114115

idl/proto/hardware_serial.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import "google/protobuf/wrappers.proto";
1414

1515
service HardwareSerial {
1616
option (arduino.base_services) = "Stream";
17+
option (arduino.generate_api_class) = true;
1718
option (arduino.generate_ifc_class) = true;
1819
option (arduino.generate_service_class) = true;
1920
option (arduino.generate_service_impl_class) = true;

tools/protoc-gen-arduinoif

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,21 @@ def _collect_lineage_includes(
319319
return _dedupe_ordered(includes)
320320

321321

322+
def _service_class_name(service) -> str:
323+
return _get_string_option(service.options, SERVICE_SERVICE_CLASS_NAME_TAG).strip() or f"{service.name}Service"
324+
325+
326+
def _ifc_class_name(service) -> str:
327+
return _get_string_option(service.options, SERVICE_IFC_CLASS_NAME_TAG).strip() or f"{service.name}Ifc"
328+
329+
330+
def _class_decl(class_name: str, base_classes: List[str]) -> str:
331+
if not base_classes:
332+
return f"class {class_name} {{"
333+
bases = ", ".join(f"public {base_class}" for base_class in base_classes)
334+
return f"class {class_name} : {bases} {{"
335+
336+
322337
def _field_type(field: FieldDescriptorProto) -> str:
323338
default_types = {
324339
FieldDescriptorProto.TYPE_BOOL: "bool",
@@ -602,7 +617,7 @@ def _generate_service_headers(
602617
) -> Dict[str, str]:
603618
ifc_class_name = _get_string_option(service.options, SERVICE_IFC_CLASS_NAME_TAG).strip() or f"{service.name}Ifc"
604619
api_class_name = _get_string_option(service.options, SERVICE_API_CLASS_NAME_TAG).strip() or f"{service.name}Api"
605-
service_class_name = _get_string_option(service.options, SERVICE_SERVICE_CLASS_NAME_TAG).strip() or f"{service.name}Service"
620+
service_class_name = _service_class_name(service)
606621
service_impl_class_name = _get_string_option(service.options, SERVICE_SERVICE_IMPL_CLASS_NAME_TAG).strip() or f"{service.name}ServiceImpl"
607622
api_member_name = _get_string_option(service.options, SERVICE_API_MEMBER_NAME_TAG).strip() or "api_"
608623
generate_ifc_class = _get_bool_option(service.options, SERVICE_GENERATE_IFC_CLASS_TAG, True)
@@ -620,6 +635,7 @@ def _generate_service_headers(
620635
namespace_name = _cpp_namespace_from_package(package_name)
621636
own_method_specs = [_method_spec_from_descriptor(method, message_map) for method in service.method]
622637
lineage = _collect_service_lineage(service_full_name, service_index, lineage_cache)
638+
ancestor_services = lineage[:-1]
623639
lineage_method_specs = _collect_lineage_methods(lineage, service_index, message_map)
624640
ifc_methods = [spec for spec in own_method_specs if spec.source_virtual]
625641
api_methods = [spec for spec in lineage_method_specs if spec.emit_api and spec.source_virtual]
@@ -644,6 +660,23 @@ def _generate_service_headers(
644660
)
645661
service_impl_methods = [spec for spec in service_methods if spec.call_name in api_callable]
646662
include_list = _collect_lineage_includes(lineage, service_index)
663+
service_base_ifc_class_names: List[str] = []
664+
service_base_ifc_header_names: List[str] = []
665+
for ancestor_full_name in ancestor_services:
666+
ancestor_service, _ = service_index[ancestor_full_name]
667+
ancestor_generates_ifc_class = _get_bool_option(
668+
ancestor_service.options,
669+
SERVICE_GENERATE_IFC_CLASS_TAG,
670+
True,
671+
)
672+
if generate_service_class and not ancestor_generates_ifc_class:
673+
raise ValueError(
674+
f"{service.name}: ancestor service '{ancestor_service.name}' must set generate_ifc_class=true"
675+
)
676+
service_base_ifc_class_names.append(_ifc_class_name(ancestor_service))
677+
service_base_ifc_header_names.append(_service_header_name(ancestor_service.name))
678+
service_base_ifc_class_names = _dedupe_ordered(service_base_ifc_class_names)
679+
service_base_ifc_header_names = _dedupe_ordered(service_base_ifc_header_names)
647680
stem = _service_header_stem(service.name)
648681
ifc_header_name = _service_header_name(service.name)
649682
api_header_name = f"{stem}_api.hpp"
@@ -693,8 +726,9 @@ def _generate_service_headers(
693726
outputs[api_header_name] = _render_header(api_includes, namespace_name, body_lines)
694727

695728
if generate_service_class:
729+
service_includes = _dedupe_ordered([*service_base_ifc_header_names, *include_list])
696730
body_lines = []
697-
body_lines.append(f"class {service_class_name} {{")
731+
body_lines.append(_class_decl(service_class_name, service_base_ifc_class_names))
698732
body_lines.append("public:")
699733
body_lines.append(f" virtual ~{service_class_name}() = default;")
700734
body_lines.append("")
@@ -704,7 +738,7 @@ def _generate_service_headers(
704738
lambda spec: body_lines.append(f" virtual {spec.decl} = 0;"),
705739
)
706740
body_lines.append("};")
707-
outputs[service_header_name] = _render_header(include_list, namespace_name, body_lines)
741+
outputs[service_header_name] = _render_header(service_includes, namespace_name, body_lines)
708742

709743
if generate_service_impl_class:
710744
service_impl_includes = [service_header_name, *include_list]

0 commit comments

Comments
 (0)