5

在 Visual Studio 2017 中,我创建了一个调用 C++ 函数的 .natvis 调试器可视化规则。

在调试器中显示:

This expression has side effects and will not be evaluated.

除此之外,它显示了一个蓝色的小箭头,可以单击它来强制它进行评估,然后它实际上确实调用了该函数。(我认为这可能是最近的一个功能,因为我似乎记得在 VS2013 中尝试过这个并且不记得它有绕过)

我的问题是:有什么方法可以永久绕过此安全检查,以便它始终立即评估我的功能并且不需要我点击箭头?

我看过一个非常相似的问题: “这个表达式会导致副作用,不会被评估”。怎么压制?其中接受的答案仅对 C# 有效(在 C# 表达式的末尾添加 ,ac 会强制调试器自动重新评估它)

我想如果存在这样的事情,它将是以下机制之一:

  1. 始终禁用安全检查的注册表设置或其他全局设置。
  2. 一种注释代码或 .natvis 规则的方法,以便编译器以某种方式知道它是一个安全且纯粹的无副作用函数

提供有关应用程序的更多详细信息:我们在代码中到处使用 uint32,它们是从字符串散列而来的,并且我们有一个仅在开发版本中加载的字符串数据库。解码函数在二叉搜索树中查找 u32 ID,返回在存储节点中找到的字符串。所以我知道它完全没有副作用,而且速度足够快,不会损害调试体验,每次 .natvis 规则都这么说时调用函数。

4

2 回答 2

3

经过进一步研究,我怀疑没有办法告诉 Visual Studio 你的函数没有副作用,即使是一个微不足道的无副作用函数,例如int MyTestFunction() { return 56; }它仍然会给出“表达式有副作用并且不会被评估” " 监视窗口中的消息。

虽然这不是一个非常令人满意的答案,但我确实找到了解决这个问题的方法,即使用笨拙、丑陋的 XML 语法重新实现我试图调用的函数(在我的例子中是通过表进行二进制搜索) .natvis 的CustomListItems标签。

尽管这个标签的名字暗示它与列表有关,但对于任何需要某种算法来实现的可视化器来说,这个标签似乎是一种万能的。在下面<CustomListItems>,您可以使用一堆不同的标签,这些标签可能足以实现几乎任何算法:

  • <Variable>声明变量的标签,
  • <Loop>用于迭代的标签,
  • <Break Condition="myCondition">用于检查是否终止循环,
  • <Exec>用于执行表达式的标签,改变标签中声明的变量的值<Variable>
  • <If Condition="myCondition">用于分支
  • 您可以包含任意数量的<Item Name="name">value</Item>标签,每个标签都将作为一行显示在正在展开的变量下方的监视窗口中。

据我所知,这些算法标签中的大多数只能在以下情况下使用CustomListItems(我只尝试了<Expand>使用一些<Variable>标签的常规规则,但出现错误,表明它不受支持)

不得不重新实现一个已经在这种糟糕的语法中工作的 C++ 函数并不是一件很愉快的事情(记住一些陷阱,比如必须使用&gt;&lt;代替,>因为<它是 XML)。

但是,通过以下几个技巧,它并没有想象的那么糟糕

  • 如果您从 Visual Studio 中编辑 .natvis 文件,保存文件会触发调试器重新评估显示,而无需重新启动程序,因此迭代您的 .natvis 规则非常快。(注意:从外部编辑器编辑 .natvis 文件时,这似乎不适用于 VS2017 中的我)
  • 在您的规则中使用大量<Item>标签,您基本上可以执行相当于“打印调试”的操作。在尝试使您的 .natvis 规则起作用时,请使用它<Item>来打印出中间值。如果算法不能正常工作,这将更容易追踪算法失败的地方。工作时删除这些额外的标签。

您可以在此处找到文档和示例CustomListItems

https://msdn.microsoft.com/en-us/library/jj620914.aspx

于 2017-11-06T21:19:09.933 回答
0

我遇到了同样的问题,在搜索时我来到了这个话题。

我刚刚找到了一个解决方案并想在此处发布:如果您在 XML 中将一个函数定义为 Intrinsic,那么编译器不会像This expression has side effects...您调用该内部函数时那样显示任何内容。

这是一个例子:

我写了一个自定义字符串类,它是这样的:

namespace rkstl
{
    namespace strings
    {
        //null-terminated string object consisting of 'char' elements
        class string 
        {
         public:    
             //... c'tors, copy c'tor and d'tor come here

             length(); //returns the length of null-terminated string: _mSize
             capacity(); //returns the length of the actual buffer: _mCap
             clear();

             //... other member functions

         private:
             char* _pStr; //the actual buffer
             size_t _mSize; //length of the string
             size_t _mCap; //length of the actual buffer
        };

        //null-terminated wide string object consisting of 'wchar_t' elements
        class wstring
        {
         //...
        };

    }
}

你可以从我的实现中看出length()capacity()是内在函数。因此,如果我直接在 .XML 中调用它们,那么编译器将显示This expression has side effects...,因此我必须单击重新评估按钮(蓝色圆圈箭头)以查看数据是什么:

<Type Name="rkstl::strings::string">
<DisplayString>{_pStr,na}</DisplayString>
<StringView>_pStr,na</StringView>
<Expand>
  <Item Name="[string length]" ExcludeView="simple">length()</Item>
  <Item Name="[buffer capacity]" ExcludeView="simple">capacity()</Item>
  <ArrayItems>
    <Size>_pEnd - _pBegin</Size>
    <ValuePointer>_pStr</ValuePointer>
  </ArrayItems>
</Expand>

相反,我定义了评估与类中相同事物的内在函数。我可以直接在 .natvis XML 中调用它们。这是我的 .natvis 实现rkstl::strings::string

<Type Name="rkstl::strings::string">
<Intrinsic Name="length_dbg" Expression="(_mSize)"/>
<Intrinsic Name="capacity_dbg" Expression="(_mCap)"/>
<DisplayString>{_pStr,na}</DisplayString>
<StringView>_pStr,na</StringView>
<Expand>
  <Item Name="[length of the string]" ExcludeView="simple">length_dbg()</Item>
  <Item Name="[capacity of the buffer]" ExcludeView="simple">capacity_dbg()</Item>
  <ArrayItems>
    <Size>_pEnd - _pBegin</Size>
    <ValuePointer>_pStr</ValuePointer>
  </ArrayItems>
</Expand>

您可以看到我已将length_dbg()和定义capacity_dbg()为 Intrinsic。因此,调试器可以安全地调用它们并评估我想要显示的数据。结果如下:

在此处输入图像描述

于 2019-10-20T09:58:23.357 回答