【问题标题】:lldb: custom summary for a vector's elements (c++)lldb:向量元素的自定义摘要(c++)
【发布时间】:2014-10-29 05:23:02
【问题描述】:

我有一个自定义类MyNameSpace::String,为此我在 lldb 中创建了一个摘要。例如,对于MyNameSpace::String myString="a string",这里是 lldb 中的摘要:

frame variable myString
   (MyNameSpace::String) myString = "a string"

我怎样才能获得看起来像 std::vector<std::string>vec 的摘要的 std::vector<MyNameSpace::String>vecString 的摘要?

到目前为止,这里是命令 frame variable 对不同变量的结果:

  • 标准字符串向量

    frame variable vec
    (std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >) vec = size=3 {
    [0] = "bonjour"
    [1] = "je suis"
    [2] = "vincent"
    }
    
  • 我的自定义字符串的向量

    frame variable vecString
    (std::__1::vector< MyNameSpace::String, std::__1::allocator< MyNameSpace::String> >) vecString = size=2 {
      [0] = {
        value = 7453010373645639777
      }
      [1] = {
        value = 18302628889929726940
      }
    }
    

如何让我的自定义摘要应用于我的矢量元素?

非常感谢您的帮助。

V.

编辑:回应评论

我正在使用 lldb-310.2.37。

我使用脚本为我的 String 类创建了摘要:

def generic_summary(valueObject,dict):
    name=valueObject.GetName()
    return valueObject.GetFrame().EvaluateExpression(name+".toString().toStdString()").GetSummary()

debugger.HandleCommand('type summary add MyNameSpace::String -F CustomSummaries.generic_summary')

这与 ~/.lldbinit 中的正确命令一起使用。

frame variable -T vecString:

(lldb) frame variable -T vecString
(std::__1::vector<MyNameSpace::String, std::__1::allocator< MyNameSpace::String> >) vecString = size=2 {
  (MyNameSpace::String) [0] = {
    (MyNameSpace::uint8) value = 7453010373645639777
  }
  (MyNameSpace::String) [1] = {
    (MyNameSpace::uint8) value = 18302628889929726844
  }
}

【问题讨论】:

  • 这应该可以,毕竟 std::string 的摘要字符串是 lldb 如何在标准字符串示例的向量中生成字符串的。您能否向您展示“类型格式添加”命令,并使用 -T 标志运行“帧变量”命令 - 如果我们可以看到 lldb 认为元素的类型是什么,它可能有助于了解为什么类型名匹配不是为你工作。另外你使用的是什么版本的lldb,也许这只是一个已经修复的错误?
  • 我认为问题出在我的摘要脚本上。实际上,在 EvaluateExpression 中,我们使用 GetName() 返回的“名称”。它对单个字符串正常工作,因为 GetName() 返回我们可以应用我们的方法的对象的名称。但是在向量中,GetName() 为第一个元素返回“[0]”,但我们需要它返回“vectorName[0]”才能应用我们的方法......我们如何访问 SBValue 父名称,如果我们封装了多个容器,更好的是完整的名称?
  • 这里是来自 valueObject.GetFrame().EvaluateExpression(name+".toString().toStdString()") 在向量内的错误代码:
  • 啊,是的。这实际上是一个非常棘手的问题,因为对于像 std::vector 这样的容器类,变量的“子代”可能是“合成子代”,它们的名称实际上更多是出于显示目的,并且可能无法返回到有效的表达式。如果您需要调用一个函数,您最好的选择可能是获取您收到的 SBValue 的地址并在您的表达式中使用该地址 - 适当地转换为您的类型。
  • @JimIngham:获取地址可能会奏效——但这也有点棘手。它通常对 Objective-C 对象更有效,因为您可以将指针值转换为 id 并调用任何选择器;对于 C++ 对象,您仍然需要处理潜在的复杂类型转换来制作合理的表达式

标签: c++ xcode lldb


【解决方案1】:

所以,正如您正确地意识到的那样,您的问题就在这里:

name=valueObject.GetName()

你的问题的真正答案是“不要做你正在做的事”。让我们一步一步来。

您在这里要求其名称的值。如果我有

struct Foo { int x; int y; } aFoo;

“x”的 SBValue 的 name 将是“x”。如果你想要“aFoo.x”,你想要的是表达式路径,正如它的名字所暗示的那样,它就是你在表达式中输入的内容来引用那个值。 p>

不过,当合成孩子出现时,事情会变得更加棘手。

您可能认为您的元素 [0] 具有 vecString[0] 的表达式路径。但它不会。它很可能再次只是 [0]。为什么?

这与合成子代的创建方式有关。具体来说,虽然 "aFoo" 中的 "x" 知道它的父对象是 "aFoo" 结构,但 vecString 的 [0] 元素却不知道。它是从整个布料创建的,作为一个值,其位置是某个内存地址。 给定一个 libc++ 向量的布局:

struct vector<T> { T* begin; T* end; T* endStorage; };

公式为 locationOfXElement = begin + X*sizeof(T)

(这都是草拟的伪代码,但请与我相处!)

LLDB 所做的是,计算那个位置,然后说 - 好吧,现在在内存中的那个位置创建一个 ValueObject(SBValue 的内部术语,如果你愿意的话)。这与底层向量没有任何关系。它只是某个位置的值。因此,表达式路径的概念是错误的。

“但你可以解决这个问题!”,你宣称。

是的,我们可能可以。它会考虑一个合成子节点有一个逻辑父节点的概念,以及模拟对结构的哪种访问的概念:它类似于数组吗?指针式的?结构体? 这样就知道要写 myObject[N], vs. myObject->foo vs. myObject.bar

这样就可以了。但它仍然不能解决你的问题。

现在考虑代替向量

std::map<yourString,int> myMap;

LLDB 将其呈现为一组美化的

pair<myString,int> { myString key; int value };

命名为 [0],[1],...

假设我们得到了正确的表达式路径语法,我们将其呈现为

myMap[0].key

尝试在表达式中使用它!是的——你猜对了——它会失败! myMap::operator[] 需要一个字符串,而不是数字索引。 在这种情况下,LLDB 选择的表示模式破坏了代码中合成子代的可用性,无论表达式路径多么智能。

我相信这也可以修复,但归根结底,在评估表达式时有充分的理由不使用合成子代。考虑以下几点:

vecString[0].size()

你想让你的向量元素被 operator[] 访问吗?还是您希望使用合成子代?

那么这个呢?

vecString[0] = "hello"

现在呢?

vecString[0] = vecString[0] + " world!"

一般来说,混合合成子元素和表达式的问题非常棘手,我们选择了“不,不” - 合成子元素对表达式评估器完全透明。

现在我们回到“不要做你正在做的事情”,不要尝试在格式化程序中运行代码,除非你真的真的真的真的必须这样做,即使那样,也要为这样的头痛做好准备。

我会假设“toString().toStdString()”本质上是在做一些内存访问和一些计算——你要做的是在 Python 代码中使用内存读取操作复制它,并在 Python 代码中进行计算仔细阅读表达式评估器。

生成的格式化程序将更快,可能更可靠 - 它适用于所有情况。

【讨论】:

  • 非常感谢您的回答,我没有意识到它会变得多么混乱。我现在会坚持使用 Python :)
  • 出于好奇,您是如何选择使用 Python 而不是 C 库进行编译的(参见 NatVis)?
  • IIUC,NatVis 不是一个 C 库,而是一个 XML 配置文件。 LLDB 方法似乎更通用,它还允许/鼓励您不运行表达式来检索数据(这是一件好事,性能和稳定性方面)。话虽如此,我敢肯定,如果他们愿意,可以在 LLDB API 之上实现类似的设施,而且 LLDB 是开源的,所以你去吧:-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多