[英]Circular Double Linked list visualization using natvis
我想为双链表编写一个natvis 可视化工具。 列表没有存储计数节点,简单的方法效果不佳,因为扩展永远不会停止(next 永远不会为空,列表的最后一项指向列表根)。
<Type Name="TListBidir<*>">
<Expand>
<LinkedListItems>
<HeadPointer>next</HeadPointer>
<NextPointer>next</NextPointer>
<ValueNode>($T1 *)this</ValueNode>
</LinkedListItems>
</Expand>
</Type>
我希望我能够在 NextPointer 中添加一个 Condition 属性,将它与列表头进行比较,但是由于 NextPoint 在节点的上下文中被评估,我不知道将它与什么进行比较:
<NextPointer Condition="next!=XXXXXXXXX">next</NextPointer>
这是以前(2010 年)可视化工具的样子,使用跳过指令,因为#list 正在自动处理:
#list
可以防止无限遍历,并且可以优雅地处理循环列表。 此外,您可以使用skip:
表达式来表示不应报告的哨兵节点。 虽然名字暗示节点会被跳过,但它实际上会导致遍历停止,所以如果你的哨兵节点是第一个你应该在它之后开始遍历。
TListBidir<*,*,*>{
children
(
#list(
head: ((($T2 *)&$c)->next),
next: next,
skip : &($c)
): (($T1 *)(&$e))
)
}
我如何在 natvis 中向调试器解释它一旦再次到达根元素就应该停止扩展列表?
我有一个类似的问题,不是循环列表,而是最后一个指向自身的哨兵节点,并提出了一个可能适合您需求的有趣解决方案:您可以使用三元运算符来伪造一个真正的终止。 <NextPointer>
的表达式可以是您可以用普通 C 编写的任何内容,因此您可以在其中进行真正的计算(但遗憾的是,没有递归)。
(请注意,不允许将Condition
属性放在<NextPointer>
,因此三元运算符是在那里完成条件的唯一方法。)
所以在我的情况下,列表是这样终止的:
<LinkedListItems>
<HeadPointer>this</HeadPointer>
<NextPointer>next != this ? next : 0</NextPointer>
<ValueNode>items</ValueNode>
</LinkedListItems>
在您的情况下,如果每个节点都有一个指向其容器的指针,您可以使用它来与头节点进行比较:
<LinkedListItems>
<HeadPointer>container->head</HeadPointer>
<NextPointer>next != container->head ? next : 0</NextPointer>
<ValueNode>items</ValueNode>
</LinkedListItems>
或者,没有>
实体并用更传统的 C 编写,这相当于:
next != container->head ? next : NULL
但是,如果您没有某种container
反向指针,那么您可能不走运,因为无法通过仅查看循环链表中的单个节点来回答它是否有效地“最后”节点。
您可以使用CustomListItems
元素执行此操作:
<CustomListItems>
<Variable Name="orig_head" InitialValue="head"/>
<Variable Name="iter" InitialValue="first_elem"/>
<Loop>
<Break Condition="iter == orig_head || iter == 0"/>
<Item>*iter</Item>
<Exec>iter = iter->next_elem</Exec>
</Loop>
</CustomListItems>
CustomListItems
允许您将头部保存在一个变量中,以便在遍历列表时可以使用它。 如果您的头部具有不同的类型,那么您需要将列表节点转换为节点类型。
natvis 框架目前不支持没有提供计数的循环链表。 如果您提供计数,它应该可以工作。 但是,如果没有计数,就没有什么好方法可以阻止扩张永远持续下去。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.