For each view in `rules['views']`, return (fields_block, bind_block).
(rules: Dict[str, Any], mjxmacro: Dict[str, Any])
| 2550 | # --------------------------------------------------------------------------- |
| 2551 | |
| 2552 | _VIEW_FIELD_TYPE_MAP = { |
| 2553 | "int": "int*", "mjtByte": "mjtByte*", "mjtBool": "mjtBool*", |
| 2554 | "mjtNum": "mjtNum*", "float": "float*", "char": "char*", |
| 2555 | } |
| 2556 | |
| 2557 | |
| 2558 | def _view_field_type_cpp(c_type: str, field_name: str = "") -> str: |
| 2559 | mapped = _VIEW_FIELD_TYPE_MAP.get(c_type) |
| 2560 | if mapped is None: |
| 2561 | # Don't silently default a new MuJoCo scalar type to mjtNum* — that |
| 2562 | # ships a wrong (often 8-byte-over-1-byte) pointer with no warning. |
| 2563 | # A bump that introduces a new type should surface here loudly. |
| 2564 | _diag_add( |
| 2565 | f"[diagnostic] view field '{field_name or '?'}' has unmapped C " |
| 2566 | f"type '{c_type}'; defaulting to 'mjtNum*'. A MuJoCo bump likely " |
| 2567 | f"introduced a new scalar type; add it to _VIEW_FIELD_TYPE_MAP.", |
| 2568 | source="view_field_type", |
| 2569 | ) |
| 2570 | return "mjtNum*" |
| 2571 | return mapped |
| 2572 | |
| 2573 | |
| 2574 | _MJ_M_MACRO_RE = re.compile(r"MJ_M\(\s*(\w+)\s*\)") |
| 2575 | # Any MJ_*( ... ) macro that survives _expand_stride is an unhandled |
| 2576 | # stride shape — emit invalid C++ in MjBind.h. The guard fires a |
| 2577 | # diagnostic so a MuJoCo bump that introduces new stride macros (e.g. |
| 2578 | # MJ_D(...) for mjData, MJ_V(...) for variable-size views) surfaces |
| 2579 | # loudly rather than landing as a compile error far downstream. |
| 2580 | _RESIDUAL_MJ_MACRO_RE = re.compile(r"\bMJ_[A-Z]\(\s*\w+\s*\)") |
| 2581 | |
| 2582 | |
| 2583 | def _expand_stride(stride: str) -> str: |
| 2584 | """Convert mjxmacro stride expressions to plain C++ expressions usable |
| 2585 | in MjBind.h. ``MJ_M(nuser_jnt)`` -> ``m->nuser_jnt``. |
| 2586 | |
| 2587 | Fires a diagnostic if any ``MJ_*(...)`` macro survives the |
| 2588 | substitution — that's an unhandled stride shape we'd otherwise |
| 2589 | silently emit into MjBind.h verbatim.""" |
| 2590 | expanded = _MJ_M_MACRO_RE.sub(r"m->\1", stride) |
| 2591 | residual = _RESIDUAL_MJ_MACRO_RE.findall(expanded) |
| 2592 | if residual: |
| 2593 | _diag_add( |
| 2594 | f"[diagnostic] _expand_stride saw stride '{stride}' with " |
| 2595 | f"unhandled MuJoCo macro(s) {residual}. Result '{expanded}' " |
| 2596 | f"will land in MjBind.h verbatim and likely fail to compile. " |
| 2597 | f"Teach _expand_stride about the new macro shape.", |
| 2598 | source="expand_stride", |
| 2599 | ) |
| 2600 | return expanded |
| 2601 | |
| 2602 | |
| 2603 | def emit_view_structs(rules: Dict[str, Any], mjxmacro: Dict[str, Any]) -> Dict[str, Tuple[str, str]]: |
| 2604 | """For each view in `rules['views']`, return (fields_block, bind_block).""" |
| 2605 | out: Dict[str, Tuple[str, str]] = {} |
| 2606 | views: Dict[str, Any] = rules.get("views", {}) |
| 2607 | |
| 2608 | for view_name, view_def in views.items(): |
| 2609 | renames: Dict[str, str] = view_def.get("field_renames", {}) |