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 myobj
I 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 int
to 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)
For me, the problem is that gdb.current_objfile()
always returns None
. Thus your pretty printer is registered as a global one, whereas all the standard ones are object level. Object level pretty-printers take precedence.
I have no idea why, but I was unable to make this function work.
It is possible to use this workaround:
gdb.printing.register_pretty_printer(gdb.objfiles()[0], pp, replace=True)
After more investigation and comments in the question from "n. 'pronouns' m.", I was able to solve the problem, although the question is still technically not solved.
In summary, with
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('MyUnorderedMapOfMyClassPrinter', 'std::unordered_map<.*, ns::MyClass\*>', MyUnorderedMapOfMyClassPrinter)
a pretty printer is registered for my specialization of unordered_map
. However, the version for the general unordered_map
is still used and if I print the mymap2
variable the string "MyUnorderedMapOfMyClassPrinter"
is not shown as expected. If we disable all pretty printers and enable only our pretty-printers, then "MyUnorderedMapOfMyClassPrinter" is shown, thus confirming that it was correctly registered. That is why the question is technically not solved, since disabling all other pretty-printers is not a good solution.
But registering a pretty-printer for MyClass*
does work, as suggested in the comments, as long as we use a lookup function instead of relying on the RegexpCollectionPrettyPrinter
. This solves the original problem, since the gdb regular pretty-printer for unordered_map
will now be enough.
More specifically, we can create a pretty-printer as
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})"
and register it with
def my_pp_func(val):
if str(val.type) == "ns::MyClass *":
return MyClassPointerPrinter(val)
gdb.pretty_printers.append(my_pp_func)
We can even extend this further and create a pretty-printer for any pointer . For instance, we can define a pretty printer as
class MyPointerPrettyPrinter:
def __init__(self, val):
self.ptr = val
def to_string(self):
default_visualizer = gdb.default_visualizer(self.ptr.dereference())
return f"({self.ptr.type}) {self.ptr.format_string(raw=True)} -> {default_visualizer.to_string()}"
and register it with
def my_pointer_func(val):
# This matches any pointer
if val.type.code == gdb.TYPE_CODE_PTR:
# Only if the pointed object has a registered pretty-printer we will use
# our pointer pretty-printer
if gdb.default_visualizer(val.dereference()) is not None:
return MyPointerPrettyPrinter(val)
gdb.pretty_printers.append(my_pointer_func)
This will match any pointer and print something like "(ClassName *) 0x<pointer_address> -> OBJ_REP", where "OBJ_REP" is the pretty-printing of the pointed object. If there is no visualizer registered for ClassName
, then only "(ClassName *) 0x<pointer_address>" is shown.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.