Source code for ifgen.struct.header
"""
A module implementing a struct header-file interface generator.
"""
# built-in
from contextlib import ExitStack
from typing import Any
# third-party
from runtimepy.ui.controls import Default
from vcorelib.io.file_writer import (
CommentStyle,
IndentedFileWriter,
LineWithComment,
)
# internal
from ifgen.generation.interface import GenerateTask
from ifgen.struct.methods import struct_methods
from ifgen.struct.methods.fields import bit_fields
from ifgen.struct.python import python_struct_header
from ifgen.struct.stream import struct_stream_methods
FieldConfig = dict[str, int | str]
[docs]
def enforce_expected_size(
size: int, data: dict[str, Any], assert_msg: str
) -> None:
"""Enforce an expected-size field."""
# If expected size is set, verify it.
if "expected_size" in data:
assert data["expected_size"] == size, (
assert_msg,
data["expected_size"],
size,
)
[docs]
def struct_line(
name: str,
value: FieldConfig,
volatile: bool,
const: bool,
packed: bool,
array_length: int = None,
default: Default | str = None,
) -> LineWithComment:
"""Build a string for a struct-field line."""
prefix = "volatile " if volatile else ""
prefix += "const " if const else ""
line = str(value["type"])
if not packed and array_length is not None:
line = f"<{prefix}{value['type']}, {array_length}>"
prefix = "std::array"
line += f" {name}"
if packed and array_length is not None:
line += f"[{name}_length]"
if default is not None:
if default is True or default is False:
default = str(default).lower()
line += f" = {default}"
elif const:
line += " = {}"
return f"{prefix}{line};", value.get("description") # type: ignore
[docs]
def struct_fields(task: GenerateTask, writer: IndentedFileWriter) -> None:
"""Generate struct fields."""
writer.c_comment("Fields.")
with writer.trailing_comment_lines(style=CommentStyle.C_DOXYGEN) as lines:
# Fields.
for field in task.instance["fields"]:
enforce_expected_size(
task.env.size(field["type"]) * field.get("array_length", 1),
field,
f"{task.name}.{field['name']}",
)
standard_array = task.instance["packed"] or bool(
task.instance.get("instances")
)
if "array_length" in field and standard_array:
lines.append(
(
(
f"static constexpr std::size_t "
f"{field['name']}_length = "
f"{field['array_length']};"
),
None,
)
)
with ExitStack() as stack:
is_union = field.get("alternates")
all_fields = [field]
if is_union:
writer.write("union")
stack.enter_context(writer.scope(suffix=";"))
all_fields.extend(field["alternates"])
for possible_union in all_fields:
if "type" not in possible_union:
possible_union["type"] = field["type"]
# Handle enum defaults.
default = field.get("default")
if task.env.is_enum(possible_union["type"]):
enum = task.env.get_enum(possible_union["type"])
if enum.default and default is None:
default = f"{possible_union['type']}_default"
if "array_length" in field and default is not None:
default = (
"{"
+ ", ".join(
[default for _ in range(field["array_length"])]
)
+ "}"
)
line, comment = struct_line(
possible_union["name"],
possible_union,
possible_union["volatile"],
possible_union["const"],
standard_array,
array_length=possible_union.get("array_length"),
default=default,
)
if is_union:
writer.write(
line + ("" if not comment else f" /* {comment} */")
)
else:
lines.append((line, comment))
lines.append(("", None))
[docs]
def struct_instance(
task: GenerateTask, writer: IndentedFileWriter, instance: dict[str, Any]
) -> None:
"""Generate struct instances."""
writer.empty()
if instance.get("description"):
with writer.javadoc():
writer.write(instance["description"])
type_base = f"{task.name} *"
writer.write(
(
"static "
+ ("volatile " if instance["volatile"] else "")
+ f"{type_base}const "
f"{instance['name']} = "
f"reinterpret_cast<{type_base}>({instance['address']});"
)
)