【问题标题】:How to access map<std::string, int> element using lldb?如何使用 lldb 访问 map<std::string, int> 元素?
【发布时间】:2021-12-18 14:42:19
【问题描述】:

用clang++ -std=c++17 -g source.cpp编译sn-p

#include <map>
#include <string>
int main()
{
  std::map<std::string, int> m;
  m["foo"] = 23;
}

尝试使用键 foo 访问元素,但出现错误:

* thread #1, queue = 'com.apple.main-thread', stop reason = step over
    frame #0: 0x000000010000131e a.out`main at source.cpp:8:3
   5    {
   6      std::map<std::string, int> m;
   7      m["foo"] = 23;
-> 8    }
Target 0: (a.out) stopped.
(lldb) p m["foo"]
error: no viable overloaded operator[] for type 'std::__1::map<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, int, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::pair<const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, int> > >'
candidate function not viable: no known conversion from 'const char [4]' to 'const std::__1::map<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, int, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::pair<const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, int> > >::key_type' (aka 'const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >') for 1st argument
candidate function not viable: no known conversion from 'const char [4]' to 'std::__1::map<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, int, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::pair<const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, int> > >::key_type' (aka 'std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >') for 1st argument
(lldb) p m[std::string("foo")]
error: no member named 'string' in namespace 'std'
(lldb) p m[std::__1::string("foo")]
error: no matching conversion for functional-style cast from 'const char [4]' to 'std::__1::string' (aka 'std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >')
candidate constructor not viable: no known conversion from 'const char [4]' to 'const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >::allocator_type' (aka 'const std::__1::allocator<char>') for 1st argument
...
candidate constructor not viable: no known conversion from 'const char [4]' to 'std::initializer_list<char>' for 1st argument
candidate constructor not viable: requires 0 arguments, but 1 was provided
...
candidate constructor not viable: requires 4 arguments, but 1 was provided

如何使用 lldb 以正确的方式处理字符串变量?

【问题讨论】:

  • 我无法检查这一点,但也许使用字符串文字会有所帮助?那就是添加using namespace std::literals;并使用"foo"s:en.cppreference.com/w/cpp/string/basic_string/operator%22%22s
  • @R2T2 我不知道该怎么说 lldb 才能理解 "foo"s 表达式。 (lldb) expr using namespace std::literals; 不会改变这一点;错误error: invalid suffix on literal; C++11 requires a space between literal and identifier。 (嗯,C++11 注释从何而来?)

标签: c++ string llvm lldb


【解决方案1】:

您没有说您使用的是什么版本的 clang++ 和 lldb。使用 Xcode 13 附带的工具,我看到:

(lldb) expr m["foo"]
(std::map<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, int, std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, int> > >::mapped_type) $0 = 23
(lldb) expr m[std::string("foo")]
(std::map<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, int, std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, int> > >::mapped_type) $1 = 23

这显然不是你得到的。这些看似简单的 C++ 表达式实际上最终会在幕后创建一堆模板化实体,而仅从调试信息重构实例化模板所需的信息非常棘手甚至不可能。

但是最近在 lldb 方面做了很多工作来支持为 C++ 标准库构建 clang 模块,我们可以从中提取必要的信息。要么你有一个没有这项工作的旧 lldb,要么由于某种原因构建 clang 模块失败。如果您使用的是当前的 lldb,那么您可能需要使用 http://bugs.llvm.org 提交错误,看看是否有人可以帮助您找出问题所在。

另外请注意,lldb 并不单独依赖表达式评估来表示常见的 C++(或 ObjC/Swift)容器。它还有一个“数据格式化程序”系统,可以理解这些容器类的底层结构,并且可以表示内容而无需调用实际的语言访问器。默认情况下,表达式的结果使用数据格式化程序进行格式化(expr --raw -- 关闭格式化程序)。这就是你看到的原因:

(lldb) expr m
(std::map<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, int, std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, int> > >) $0 = size=1 {
  [0] = (first = "foo", second = 23)
}

而不是 std::map 的所有实际字段。

在这种情况下,std::map 的数据格式化程序将地图呈现为对的向量。由于键可以是任何东西,这提供了一种更方便的方式来访问元素...

此语法无法反馈到表达式求值器中 - 旨在尽可能接近源语言,但您可以使用它们通过 frame var 命令挖掘局部变量 - 短别名 @ 987654326@:

(lldb) v m[0]
(std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, int>) [0] = (first = "foo", second = 23)
(lldb) v m[0].first
(const std::basic_string<char, std::char_traits<char>, std::allocator<char> >) m[0].first = "foo"

等等

【讨论】:

  • 我已经创建了 ubuntu docker 容器并安装了最后一个稳定的 clang 工具(clang & lldb ver. 13.0.1),类似的错误。有问题的示例使用 MacOS (Catalina) clang ver。 11.0.0; lldb-1100.0.30.12; Xcode 11.3.1(它是 Catalina 的最新版本)。可能是 Apple 在最近的更新中调整了 clang,但这些功能并未包含在开源 llvm 项目中?
  • 您使用的是 clang C++ STL 还是 GNU 的?有适用于 GNU STL 的数据格式化程序,但还不够成熟。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-29
  • 1970-01-01
  • 1970-01-01
  • 2019-03-15
  • 2012-12-08
相关资源
最近更新 更多