Search code examples
c++gdbpretty-print

GDB pretty printer for classes derived from one base


I want to add a pretty printer to my .gdbinit that will be used for any type that is derived from a base class Base, which contains "value". This partially works:

class MyBasePrinter(gdb.printing.PrettyPrinter):
    def __init__(self, val):
        self.__val = val['value']
    def to_string(self):
        return self.__val
my_pretty_printer = gdb.printing.RegexpCollectionPrettyPrinter('my_printer')
my_printer.add_printer('Base', '^Base.*$', MyBasePrinter)

Unfortunately, what I'm getting printed in gdb is something like this:

$1 = {<Base class stuff> = <value>}

The Base class is a CRTP template, so the "Base class stuff" gets pretty long. Alternatively, I can add a separate pretty printer for each of my derived classes, in which case printing works perfectly, but then .gdbinit needs to be changed every time someone derives a new class from Base. I wonder if there are any better solutions? Ideally, I'd like to see printed something like

$1 = <Derived> = <value>

Minimal code example for Base and Derived:

template <typename RawType, typename T, template <typename> typename...Traits>
struct IntegralValue : Traits<T>... {
    using raw_type = RawType;
    raw_type value;
    explicit IntegralValue(raw_type v) : value(v) { }
    void operator=(IntegralValue const& rhs) { value = rhs.value; }
};
template <typename T> struct EqualComparable {
    bool operator==(T rhs) const {
        return static_cast<T const*>(this)->value == rhs.value;
    }
    bool operator!=(T rhs) const {
        return static_cast<T const*>(this)->value != rhs.value;
    }
};
template <typename T> struct Incrementable {
    T operator++(int) {
        auto& self = static_cast<T&>(*this);
        T retval = self;
        self.value++;
        return retval;
    }
    // other increment-related operators here
    // ...
};
struct SequenceNumber : IntegralValue<uint64_t, SequenceNumber, EqualComparable, Incrementable> {
    explicit SequenceNumber(raw_type v) : IntegralValue(v) { }
};
struct Id : IntegralValue<uint64_t, Id, EqualComparable> {
    explicit Id(raw_type v) : IntegralType(v) { }
};
auto id = Id(1);
auto s = SequenceNumber(2);
// stop gdb around here and print id and s

Solution

  • Your code has:

    my_pretty_printer = gdb.printing.RegexpCollectionPrettyPrinter('my_printer')
    

    While it is conventional for gdb pretty-printers to be registered by the name of the type -- and there are helpers like this one to make it simpler -- it is not actually required. Instead you can have a recognizer that accepts any value and decides whether or not to construct a pretty-printer for it.

    See the section on gdb.pretty_printers in the manual. Your recognizer could work by inspecting the value's type to see if it is derived from Base.