c++stlgdbpretty-printgdb-python

GDB: Pretty-Print class containing STL container


I'm trying to write a pretty-printer for a class containing a std::set of objects for which I'm also supplying my own pretty printer. Very basically, this is how my C++ code looks like:

#include <set>
#include <iostream>
#include <cassert>

class Foo {
public:
  int x;

  bool operator<(const Foo & rhs) const {
    return this->x < rhs.x;
  }
};

class FooContainer {
public:
  std::set<Foo> content;
};

int main(int argc, char **argv) {
  FooContainer c;
  Foo f1 {1};
  Foo f2 {2};
  c.content.insert(f1);
  c.content.insert(f2);

  assert(false); // hand over to gdb
}

I want to be able to pretty-print objects of class "FooContainer". So, I want pretty-printers that look somehow like these:

class FooPrinter(object):
    def __init__(self, val):
        self.val = val

    def to_string(self):
        return "X: " + str(self.val['x'])

class FooContainerPrinter(object):
    def __init__(self, val):
        self.val = val

    def to_string(self):
        res = ""
        for foo in self.val['content']:
            res += " " + FooPrinter(foo).to_string()
        return res

However, trying these, GDB gives me an error:

(gdb) p c 
Python Exception <class 'TypeError'> 'gdb.Value' object is not iterable: 
$7 =

It looks like the FooContainerPrinter only has access to the internal members of a std::set, and can't iterate it. I would really like to avoid having to traverse the red-black-tree behind that std::set myself. Is there a neat trick to achieve this?


Solution

  • After some trying, I've found a way that comes very close. I'm basically using the default StdSetPrinter provided with the stdlib, but I'm not using it for printing, just for iterating the set. My code looks like this now:

    from libstdcxx.v6.printers import StdSetPrinter
    
    class FooPrinter(object):
        def __init__(self, val):
            self.val = val
    
        def to_string(self):
            return "X: " + str(self.val['x'])
    
    class FooContainerPrinter(object):
        def __init__(self, val):
            self.val = val
    
        def to_string(self):
            return "My Foo Container"
    
        def children(self):
            pp = StdSetPrinter("dummy", self.val['content'])
            return pp.children()
    

    Now, the default pretty printing magic still adds some boilerplate (basically it outputs "My Foo Container = { … ⟨ pretty-print of the contents ⟩ …}") but that is fine with me. I think it would even be able to not define an own children(), but rather use the pp.children() inside to_string() and thus have full control over the output string.

    It has the drawback that the path where libstdc++ puts its default pretty printers needs to be in the PYTHONPATH.