3

Compile the snippet with clang++ -std=c++17 -g source.cpp

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

Try to access the element with key foo, but have errors:

* 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

How to work with string variables in the right way using lldb?

amordo
  • 449
  • 5
  • 15
  • I cannot check this, but maybe using string literal would help? That is adding `using namespace std::literals;` and using `"foo"s`: https://en.cppreference.com/w/cpp/string/basic_string/operator%22%22s – R2RT Nov 04 '21 at 10:59
  • @R2T2 I'm not sure how to say lldb to understand `"foo"s` expression. `(lldb) expr using namespace std::literals;` doesn't change that; the error `error: invalid suffix on literal; C++11 requires a space between literal and identifier`. (hm, where C++11 note comes from?) – amordo Nov 04 '21 at 11:32

1 Answers1

1

You don't say what versions of clang++ & lldb you are using. Using the tools that ship with Xcode 13, I see:

(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

which is clearly not what you are getting. These simple seeming C++ expressions actually end up creating a bunch of templated entities behind the scenes, and reconstructing the information needed to instantiate templates from debug information alone is tricky to impossible.

But there has been a bunch of work done recently on the lldb side to support building the clang module for the C++ standard library from which we can extract the necessary information. Either you have an older lldb which doesn't have this work, or for some reason building the clang module is failing. If you are using a current lldb, then you might want to file a bug with http://bugs.llvm.org and see if somebody can help you figure out what's going wrong.

Note, also, lldb doesn't rely on expression evaluation alone for presentation of the common C++ (or ObjC/Swift) containers. It also has a system of "data formatters" which understand the underlying structure of these container classes and can represent the contents w/o having to call the actual language accessors. By default the results of expressions are formatted using the data formatters (expr --raw -- turns the formatters off). That's why you see:

(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)
}

rather than all the actual fields of std::map.

In this case, the data formatter for std::map presents the map as a vector of pairs. Since the keys can be anything, this provides a more convenient way to access the elements...

This syntax can't be fed back into the expression evaluator - which is intended to be as close to the source language as possible, but you can use them to dig into local variables using the frame var command - short alias v:

(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"

etc.

Jim Ingham
  • 25,260
  • 2
  • 55
  • 63
  • I've created ubuntu docker container and installed the last stable clang tools (clang & lldb ver. 13.0.1), similar errors. The example in question uses MacOS (Catalina) clang ver. 11.0.0; lldb-1100.0.30.12; Xcode 11.3.1 (it's the latest for Catalina). It might be that Apple tuned clang in recent updates but these features aren't included in the open-source llvm project? – amordo Nov 16 '21 at 17:37
  • Are you using the clang C++ STL, or the GNU one? There are data formatters for the GNU STL, but they are less mature. – Jim Ingham Nov 16 '21 at 19:54