为 std::unordered_map 的专业化注册 gdb 漂亮打印机

问题描述

我正在尝试为 std::unordered_map 的特定专业注册一个漂亮的打印机,但出于某种原因,它总是在 gdb 中为 std::unordered_map 使用标准的漂亮打印机,而不是我的专业版本。


考虑下面的类

namespace ns {

class MyClass {
public:
    std::string name;
    int value = 0;
};

}  // namespace ns

我为它定义了一个 gdb 漂亮的打印机

# 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)

现在考虑下面的主要功能

int main(int argc,char *argv[]) {
    std::tuple<int,MyClass> mytuple{10,{"name10",10}};
    std::unordered_map<int,MyClass> mymap;
    mymap.insert({10,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;
}

如果在 cout 行中添加断点并按预期打印 myobj 我得到 $7 = MyClass(name="name5",value=5)。打印 mytuplemymap 也有效,因为 gdb 为 STL 中的容器提供了漂亮的打印机。我们得到类似

$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)
}

然而,我实际拥有的是带有 MyClass* 而不是 MyClass 的容器,例如 mymap2 变量。打印结果

$10 = std::unordered_map with 3 elements = {
  [25] = 0x555555576760,[15] = 0x555555576710,[10] = 0x555555576650
}

这不是很有用。

对于我的特殊需要,我只需要 MyClass 中指向的每个 mymap2 对象的“名称”字段。然后我为 std::unordered_map<int,MyClass *> 创建了一个漂亮的打印机,但我无法正确注册它(也许只是 std::unordered_map 的版本优先,但我无法正确禁用来测试这个假设。当我尝试按照 this question 中的建议禁用漂亮打印机时出现错误

我试过

class MyUnorderedMapOfMyClassprinter:
    def __init__(self,val):
        self.val = val

    def to_string(self):
        return "MyUnorderedMapOfMyClassprinter"

然后将下面的行添加到定义我的漂亮打印机的 python 脚本中

pp.add_printer('MyUnorderedMapOfMyClassprinter','std::unordered_map<int,ns::MyClass\*>',MyUnorderedMapOfMyClassprinter)

包括新的漂亮打印机。

但是打印 unordered_map 不使用我的漂亮打印机。它仍然使用来自 gdb 的常规 std::unordered_map 漂亮打印机。

如果我确实创建了一个新类型

class MyUnorderedMap : public std::unordered_map<int,MyClass *> {};

并为 MyUnorderedMap 注册一个漂亮的打印机,然后它就可以工作了。但我不想创建另一种类型只是为了能够注册一个漂亮的打印机。

如何为 std::unordered_map 的特定专业注册漂亮的打印机?


编辑: 我不能只禁用 std::unordered_map 漂亮的打印机,但是 我在 gdb 中使用 disable pretty-printer 禁用了所有漂亮打印机,然后使用 enable pretty-printer global myprinters 仅启用了我的漂亮打印机。这使我可以尝试使用正则表达式来注册我的漂亮打印机,并使其适用于带有 pp.add_printer('MyUnorderedMapOfMyClassprinter','std::unordered_map<.*,MyUnorderedMapOfMyClassprinter) 的 std::unordered_map 专业化(注意末尾的“*”,因为我只希望它适用于 { {1}} 个指针)。

有趣,MyClass 没有用。出于某种原因,我不得不使用 pp.add_printer('MyUnorderedMapOfMyClassprinter',MyUnorderedMapOfMyClassprinter) 而不是 .* 才能使其工作。

然而,当所有漂亮打印机都启用时,标准的 int 漂亮打印机仍然优先于我的专业化。 如何让漂亮的打印机优先?


为了让事情更容易重现,这里是完整的 std::unordered_map 文件

main.cpp

以及定义漂亮打印机的完整 #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,10}}); mymap2.insert({15,25}}); std::cout << "The end" << std::endl; return 0; } 文件

myprinters.py

解决方法

对我来说,问题是 gdb.current_objfile() 总是返回 None。因此,您的漂亮打印机被注册为全局打印机,而所有标准打印机都是对象级别的。对象级漂亮打印机优先。

我不知道为什么,但我无法使这个功能工作。

可以使用此解决方法:

gdb.printing.register_pretty_printer(gdb.objfiles()[0],pp,replace=True)
,

在“n.'pronouns' m.”的问题中进行了更多的调查和评论后,我能够解决问题,尽管问题在技术上仍然没有解决。

总而言之,与

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)

为我的 unordered_map 专业注册了一台漂亮的打印机。但是,仍使用通用 unordered_map 的版本,如果我打印 mymap2 变量,则字符串 "MyUnorderedMapOfMyClassPrinter" 未显示正如预期的那样。如果我们禁用所有漂亮的打印机并仅启用我们的漂亮打印机,则会显示“MyUnorderedMapOfMyClassPrinter”,从而确认它已正确注册。这就是问题在技术上没有解决的原因,因为禁用所有其他漂亮的打印机并不是一个好的解决方案。

但是为 MyClass* 注册一个漂亮的打印机确实有效,正如评论中所建议的那样,只要我们使用查找函数而不是依赖于 RegexpCollectionPrettyPrinter。这解决了最初的问题,因为 unordered_map 的 gdb 常规漂亮打印机现在就足够了。

更具体地说,我们可以创建一个漂亮的打印机

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})"

并注册

def my_pp_func(val):
    if str(val.type) == "ns::MyClass *":
        return MyClassPointerPrinter(val)


gdb.pretty_printers.append(my_pp_func)

我们甚至可以进一步扩展它,为任何指针创建一个漂亮的打印机。例如,我们可以将漂亮的打印机定义为

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()}"

并注册

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)

这将匹配任何指针并打印类似“(ClassName *) 0x -> OBJ_REP”的内容,其中“OBJ_REP”是指向对象的漂亮打印。如果没有为 ClassName 注册的可视化工具,则只显示“(ClassName *) 0x”。