I'm trying to register a pretty-printer for a specific specialization of std::unordered_map, but for some reason it always uses the standard pretty-printer for std::unordered_map in gdb instead of my specialized version.
Consider the following class
namespace ns {
class MyClass {
public:
std::string name;
int value = 0;
};
} // namespace ns
I have a gdb pretty-printer defined for it as
# myprinters.py -> sourced in gdb
class MyClassPrinter:
def __init__(self, val):
self.name = val["name"]
self.val = val["value"]
def to_string(self):
return f"MyClass(name={self.name}, value={self.val})"
import gdb.printing
pp = gdb.printing.RegexpCollectionPrettyPrinter('myprinters')
pp.add_printer('MyClass', 'MyClass', MyClassPrinter)
gdb.printing.register_pretty_printer(gdb.current_objfile(), pp, replace=True)
Now consider the main function below
int main(int argc, char *argv[]) {
std::tuple<int, MyClass> mytuple{10, {"name10", 10}};
std::unordered_map<int, MyClass> mymap;
mymap.insert({10, {"name10", 10}});
mymap.insert({15, {"name15", 15}});
mymap.insert({25, {"name25", 25}});
auto myobj = MyClass{"name5", 5};
std::unordered_map<int, MyClass *> mymap2;
mymap2.insert({10, new MyClass{"name10", 10}}); // don't worry about the new
mymap2.insert({15, new MyClass{"name15", 15}});
mymap2.insert({25, new MyClass{"name25", 25}});
std::cout << "The end" << std::endl;
return 0;
}
If a add a breakpoint in the cout line and print myobjI get $7 = MyClass(name="name5", value=5) as expected. Printing mytuple and mymap also works, since gdb has pretty-printers for the containers in STL. We get something like
$8 = std::tuple containing = {
[1] = 10,
[2] = MyClass(name="name10", value=10)
}
$9 = std::unordered_map with 3 elements = {
[25] = MyClass(name="name25", value=25),
[15] = MyClass(name="name15", value=15),
[10] = MyClass(name="name10", value=10)
}
However, what I actually have are containers with MyClass* and not MyClass, such as the mymap2 variable. Printing that results in
$10 = std::unordered_map with 3 elements = {
[25] = 0x555555576760,
[15] = 0x555555576710,
[10] = 0x555555576650
}
which is not very useful.
For my particular needs, I only need the "name" field of each MyClass object pointed in mymap2. Then I created a pretty-printer for std::unordered_map<int, MyClass *>, but I'm not able to register it correctly (maybe the version for just std::unordered_map is taking precedence, but I could not correctly disable to test this hypothesis. I get an error when I try to disable the pretty-printer as suggested in this question).
I tried with
class MyUnorderedMapOfMyClassPrinter:
def __init__(self, val):
self.val = val
def to_string(self):
return "MyUnorderedMapOfMyClassPrinter"
and then added the line below to the python script defining my pretty-printers
pp.add_printer('MyUnorderedMapOfMyClassPrinter', 'std::unordered_map<int, ns::MyClass\*>', MyUnorderedMapOfMyClassPrinter)
to include the new pretty-printer.
But printing the unordered_map does not use my pretty-printer. It still uses the regular std::unordered_map pretty-printer from gdb.
If I do create a new type like
class MyUnorderedMap : public std::unordered_map<int, MyClass *> {};
and register a pretty-printer for MyUnorderedMap then it works. But I don't want to create another type just to be able to register a pretty-printer.
How can I register a pretty-printer for a specific specialization of std::unordered_map?
Edit:
I could not disable only the std::unordered_map pretty-printer, but
I disabled all pretty-printers with disable pretty-printer in gdb, and then enabled only my pretty-printers with enable pretty-printer global myprinters. This allowed me try the regexp to register my pretty-printer and to make it work for a std::unordered_map specialization with with pp.add_printer('MyUnorderedMapOfMyClassPrinter', 'std::unordered_map<.*, ns::MyClass\*>', MyUnorderedMapOfMyClassPrinter) (note the "*" at the end, since I only want it to work for MyClass pointers).
Interesting, pp.add_printer('MyUnorderedMapOfMyClassPrinter', 'std::unordered_map<int, ns::MyClass\*>', MyUnorderedMapOfMyClassPrinter)
did not work. I had to use .* instead of intto make it work for some reason.
However, when all pretty-printers are enabled the standard std::unordered_map pretty printer still takes precedence over my specialization. How can I make my pretty-printer takes precedence?
For making things easily reproducible, here is the full main.cpp file
#include <iostream>
#include <string>
#include <tuple>
#include <unordered_map>
namespace ns {
class MyClass {
public:
std::string name;
int value = 0;
};
} // namespace ns
using namespace ns;
int main(int argc, char *argv[]) {
std::tuple<int, MyClass> mytuple{10, {"name10", 10}};
std::unordered_map<int, MyClass> mymap;
mymap.insert({10, {"name10", 10}});
mymap.insert({15, {"name15", 15}});
mymap.insert({25, {"name25", 25}});
auto myobj = MyClass{"name5", 5};
std::unordered_map<int, MyClass *> mymap2;
mymap2.insert({10, new MyClass{"name10", 10}});
mymap2.insert({15, new MyClass{"name15", 15}});
mymap2.insert({25, new MyClass{"name25", 25}});
std::cout << "The end" << std::endl;
return 0;
}
and the full myprinters.py file defining the pretty-printers
class MyClassPrinter:
def __init__(self, val):
self.name = str(val["name"])
self.val = val["value"]
def to_string(self):
return f"MyClass(name={self.name}, value={self.val})"
class MyClassPointerPrinter:
def __init__(self, val):
self.ptr = val
self.name = str(val.dereference()["name"])
self.val = val.dereference()["value"]
def to_string(self):
return f"Pointer to MyClass(name={self.name}, value={self.val})"
class MyUnorderedMapOfMyClassPrinter:
def __init__(self, val):
self.val = val
def to_string(self):
return "MyUnorderedMapOfMyClassPrinter"
import gdb.printing
pp = gdb.printing.RegexpCollectionPrettyPrinter('myprinters')
pp.add_printer('MyClass', '^ns::MyClass$', MyClassPrinter)
# pp.add_printer('MyClass', 'MyClass\*', MyClassPointerPrinter)
# pp.add_printer('MyClass', 'MyClass.?\*', MyClassPointerPrinter)
# pp.add_printer('MyClass', 'MyClass \*', MyClassPointerPrinter)
pp.add_printer('MyUnorderedMapOfMyClassPrinter', 'std::unordered_map<.*, ns::MyClass\*>', MyUnorderedMapOfMyClassPrinter)
gdb.printing.register_pretty_printer(gdb.current_objfile(), pp, replace=True)
def my_pp_func(val):
if str(val.type) == "ns::MyClass *":
return MyClassPointerPrinter(val)
gdb.pretty_printers.append(my_pp_func)