【问题标题】:Circular Double Linked list visualization using natvis使用 natvis 的循环双链表可视化
【发布时间】:2012-08-07 11:17:04
【问题描述】:

我想为双链表写一个natvis visualizer。列表没有存储计数节点,简单的方法效果不佳,因为扩展永远不会停止(next 永远不会为空,列表的最后一项指向列表根)。

<Type Name="TListBidir&lt;*&gt;">
    <Expand>
        <LinkedListItems>
            <HeadPointer>next</HeadPointer>
            <NextPointer>next</NextPointer>
            <ValueNode>($T1 *)this</ValueNode>
        </LinkedListItems>
    </Expand>
</Type>

我希望我能够添加一个 Condition 属性,将 NextPointer 与列表头进行比较,但由于 NextPoint 是在节点的上下文中评估的,我不知道与什么进行比较:

<NextPointer Condition="next!=XXXXXXXXX">next</NextPointer>

这是以前 (2010) 可视化工具的样子,使用 skip 指令,如 #list was handling this automatically

#list 可以防止无限遍历,并且可以优雅地处理循环列表。此外,您可以使用skip: 表达式来表示不应报告的哨兵节点。虽然名字暗示会跳过节点,但实际上会导致遍历停止,所以如果你的哨兵节点是第一个,你应该在它之后开始遍历。

TListBidir<*,*,*>{
    children
    (
      #list(
        head: ((($T2 *)&$c)->next),
        next: next,
        skip : &($c)
      ): (($T1 *)(&$e))
    )
}

如何在 natvis 中向调试器解释它应该在列表再次到达根元素时停止扩展?

【问题讨论】:

    标签: c++ visual-studio debugging debuggervisualizer natvis


    【解决方案1】:

    我遇到了类似的问题,不是循环列表,而是末尾有一个指向自身的哨兵节点,并提出了一个可能适合您需求的有趣解决方案:您可以使用三元运算符伪造一个真正的终止。 &lt;NextPointer&gt; 中的表达式可以是您可以用 vanilla C 编写的任何内容,因此您可以在其中进行真正的计算(但遗憾的是,没有递归)。

    (请注意,您不能在&lt;NextPointer&gt; 上放置Condition 属性,因此三元运算符是在那里完成条件的唯一方法。)

    所以在我的例子中,列表是这样终止的:

    <LinkedListItems>
        <HeadPointer>this</HeadPointer>
        <NextPointer>next != this ? next : 0</NextPointer>
        <ValueNode>items</ValueNode>
    </LinkedListItems>
    

    在您的情况下,如果每个节点都有一个指向其容器的指针,您可以使用它与头节点进行比较:

    <LinkedListItems>
        <HeadPointer>container-&gt;head</HeadPointer>
        <NextPointer>next != container-&gt;head ? next : 0</NextPointer>
        <ValueNode>items</ValueNode>
    </LinkedListItems>
    

    或者,如果没有 &amp;gt; 实体并用更传统的 C 编写,则相当于:

    next != container->head ? next : NULL
    

    但是,如果您没有某种container 后向指针,那么您可能对此不走运,因为无法通过仅查看循环链接列表中的单个节点来回答它是否实际上是“最后一个”节点。

    【讨论】:

    • 嗯,你没有倒三元吗?不应该是next != container-&gt;head ? next : 0吗?或者我不明白你在做什么?
    • 是的,在这个版本中是倒退的;我会纠正它。我使用的实际代码要复杂得多,因为数据结构要复杂得多,但经过几次额外的指针间接后,仍然松散地归结为一个循环链表。我搞砸了从中提取出这篇帖子的核心逻辑。
    • 我不知道VS2012,但是在VS2013中,如果列表最终命中&lt;HeadPointer&gt;中描述的指针,它也会停止列表,就像&lt;NextPointer&gt;指向nullptr一样。
    【解决方案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-&gt;next_elem</Exec>
        </Loop>
    </CustomListItems>
    

    CustomListItems 允许您将头部保存在变量中,以便在遍历列表时使用它。如果您的头部有不同的类型,那么您需要将列表节点转换为节点类型。

    【讨论】:

      【解决方案3】:

      natvis 框架目前不支持没有提供计数的循环链表。如果您提供计数,它应该可以工作。但是,如果没有计数,就没有好的方法可以阻止扩展永远持续下去。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-07-14
        • 1970-01-01
        • 2021-07-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多