我想为双链表编写natvis visualizer。该列表没有存储计数节点,并且简单的方法不能很好地工作,因为扩展永远不会停止(next永远不为null,列表的最后一项指向列表根目录。)
<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>
使用skip指令作为#list was handling this automatically的前一个(2010)可视化工具的样子如下:
#list
受到无限遍历的保护,并且可以优雅地处理循环列表。此外,您可以使用skip:
表达式来表示不应报告的标记节点。虽然名称暗示将跳过该节点,但它实际上会导致遍历停止,因此如果您的Sentinel节点是第一个,则应该在它之后开始遍历。
TListBidir<*,*,*>{
children
(
#list(
head: ((($T2 *)&$c)->next),
next: next,
skip : &($c)
): (($T1 *)(&$e))
)
}
如何在natvis中向调试器解释它应该在再次到达根元素后停止扩展列表?
答案 0 :(得分:2)
natvis框架目前不支持循环链表而不提供计数。如果你提供一个计数,它应该工作。然而,没有统计,没有好办法阻止扩张永远持续下去。
答案 1 :(得分:2)
我有一个类似的问题,不是循环列表,而是在末尾有一个指向自身的哨兵节点,并提出了一个可能适合您需求的有趣解决方案:您可以使用三元运算符来伪造出真正的终止。 <NextPointer>
中的表达式可以是你在vanilla 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
后退指针,你可能会因此而失去运气,因为只有a循环链接列表中的单个节点,用于回答它是否有效地排除了最后一个&#34;节点
答案 2 :(得分:2)
您可以使用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
允许您将头保存在变量中,以便在遍历列表时使用它。如果您的头部具有不同的类型,则需要将列表节点转换为节点类型。