cgal/Installation/share/gdb/auto-load/cgal_printers.py

138 lines
3.9 KiB
Python

# SPDX-License-Identifier: LGPL-3.0-or-later
# SPDX-FileCopyrightText: 2025 GeometryFactory Sarl
"""
CGAL GDB Pretty Printers
This module provides pretty printers for common CGAL types to make
debugging more convenient.
Auto-loaded via .debug_gdb_scripts section.
"""
import re
import os
import gdb
def base(val):
"""Get the base of a possibly derived CGAL type"""
v = val
while True:
# print([f.name for f in v.type.fields()])
if 'base' in [f.name for f in v.type.fields()]:
v = v['base']
continue
if len(v.type.fields()) == 1 and v.type.fields()[0].is_base_class:
v = v[v.type.fields()[0]]
continue
break
# print(v.type)
return v
class CGALPointPrinterBase:
"""Base class for CGAL 3D point pretty printers"""
def __init__(self, val, label):
self.val = val
self.label = label
def extract_coords(self):
try:
v = base(self.val)
elems = v['_M_elems']
x = float(elems[0])
y = float(elems[1])
z = float(elems[2])
return x, y, z
except (KeyError, IndexError) as e:
return (f"<error: {e}>", None, None)
def to_string(self):
x, y, z = self.extract_coords()
if x is not None and y is not None and z is not None:
return f"{self.label}({x}, {y}, {z})"
else:
# Print raw value for debugging
return f"{self.label} ERROR {x}"
def children(self):
# yield 'raw', str(self.val.format_string(raw=True, pretty=False))
x, y, z = self.extract_coords()
if x is not None and y is not None and z is not None:
yield 'x', x
yield 'y', y
yield 'z', z
def display_hint(self):
return 'array'
class PointC3Printer(CGALPointPrinterBase):
"""Pretty printer for CGAL::PointC3<any>"""
def __init__(self, val):
super().__init__(val, "PointC3")
class Point3Printer(CGALPointPrinterBase):
"""Pretty printer for CGAL::Point_3<any>"""
def __init__(self, val):
super().__init__(val, "Point_3")
def cgal_lookup_function(val):
"""Lookup function to register CGAL pretty printers"""
typename = str(val.type.strip_typedefs())
# Match CGAL::PointC3<anything>
if re.match(r'^CGAL::PointC3<.*>$', typename):
return PointC3Printer(val)
# Match CGAL::Point_3<R_>
if re.match(r'^CGAL::Point_3<.*>$', typename):
return Point3Printer(val)
return None
# De=register existing CGAL pretty printers to avoid duplicates
def deregister_existing_cgal_printers():
try:
objfile = gdb.current_objfile()
except (AttributeError, RuntimeError, gdb.error):
objfile = None
if objfile and hasattr(objfile, 'pretty_printers'):
printers = objfile.pretty_printers
elif hasattr(gdb, 'pretty_printers'):
printers = gdb.pretty_printersq
else:
printers = []
to_remove = []
for printer in printers:
if printer.__name__ == 'cgal_lookup_function':
to_remove.append(printer)
for printer in to_remove:
printers.remove(printer)
# Register the pretty printer for GDB (manual fallback for older GDB)
def register_cgal_printer(printer):
objfile = None
try:
objfile = gdb.current_objfile()
except (AttributeError, RuntimeError, gdb.error):
pass
if objfile and hasattr(objfile, 'pretty_printers'):
objfile.pretty_printers.append(printer)
elif hasattr(gdb, 'pretty_printers'):
gdb.pretty_printers.append(printer)
else:
print(
"[CGAL] Could not register pretty printer: no pretty_printers attribute found.")
deregister_existing_cgal_printers()
register_cgal_printer(cgal_lookup_function)
# Get the full path of this script file
current_file = os.path.abspath(__file__)
print(f"CGAL pretty printers auto-loaded from {current_file}")