4

As Element at index in a std::set? explains, there is no "direct" random access by index in a std::set - so, here I'm trying to use it's .begin() method that returns an iterator... Here is a simple minimal example:

// g++ --std=c++11 -g test.cpp -o test.exe

#include <iostream>
#include <set>

int main()
{
  std::set<std::string> my_set;
  my_set.insert("AA");
  my_set.insert("BB");
  std::cout << "Hello World!" << std::endl;
  return 0;
}

What I ultimately want to do is use a gdb dprintf type break in my actual problem - I would like to not change the code, and thus not add additional iterator variables. Since dprintf uses format specifiers, where %s is for a C-style string, I ultimately need not just a reference to the first element of my_set, but I'd also need to call .c_str() on it - and all of this in a one-liner. (in my actual problem, I have a std::set of a custom String class, which has a custom method for getting a C-style string).

The problem is, I cannot find the right syntax to access the first element:

$ gdb --args ./test.exe
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1
...
Reading symbols from ./test.exe...done.
(gdb) b test.cpp:11
Breakpoint 1 at 0x8048bf8: file test.cpp, line 11.
(gdb) r
Starting program: /tmp/test.exe 

Breakpoint 1, main () at test.cpp:11
11    std::cout << "Hello World!" << std::endl;
(gdb) p my_set
$1 = std::set with 2 elements = {[0] = "AA", [1] = "BB"}
(gdb) p my_set.begin()
Cannot evaluate function -- may be inlined
(gdb) p my_set->begin()
Cannot resolve method std::set<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >::begin to any overloaded instance
(gdb) printf "%s", my_set.begin()
Cannot evaluate function -- may be inlined

Is it possible to print the first value of a std::set in a context like this using gdb's printf, without having to change the code (for instance, to add iterator variables)?

Community
  • 1
  • 1
sdaau
  • 36,975
  • 46
  • 198
  • 278
  • 1
    it seems like you may adapt script to dump STL containers content, https://gist.github.com/skyscribe/3978082 – Andrey Starodubtsev Nov 16 '15 at 18:22
  • Many thanks for that, @AndreyStarodubtsev - however, I still cannot access the first element, `.begin()` still gives "Cannot evaluate function -- may be inlined", `my_set[0]` still gives "No symbol "operator[]" in current context." I see there is a `pset` command, but cannot get it to work either. Any suggestions? – sdaau Nov 16 '15 at 18:30
  • 1
    I could be miserably wrong but I guess compiler needs to see a function usage in oder to put it inside the executable. Try calling my_set.begin() anywhere in the code to see if it works – Amadeus Nov 16 '15 at 18:36
  • Thanks, @Amadeus - you are, in fact, absolutely right: I added some usage as `std::set::iterator my_iter; my_iter = my_set.begin(); cout << &my_iter;` and that indeed made `my_set.begin()` print in `gdb`! But that seemingly requires code changes, which I'd like to avoid... – sdaau Nov 16 '15 at 18:47

1 Answers1

0

Well, I got somewhere, thanks to both comments from @AndreyStarodubtsev and @Amadeus.

For one, as @Amadeus noted - it turns out, that indeed you must have some sort of a reference to a type in the program, otherwise it may be optimised away. So the OP example, as it is, when debugged will give this:

(gdb) p ((std::string*)(my_set._M_t._M_impl._M_header._M_left+1))->c_str()
A syntax error in expression, near `)(my_set._M_t._M_impl._M_header._M_left+1))->c_str()'.
(gdb) p std::string
No symbol "string" in namespace "std".

... however, just a variable reference seems enough to fix that:

// g++ --std=c++11 -g test.cpp -o test.exe

#include <iostream>
#include <set>

std::string aa; // just to have reference to std::string

int main()
{
  std::set<std::string> my_set;
  my_set.insert("AA");
  my_set.insert("BB");
  std::cout << "Hello World!" << std::endl;
  return 0;
}

... and while this is code change, I do not have to add new lines of actual code referring to iterators inside. Then, a gdb session looks like this:

$ gdb --args ./test.exe
...
Reading symbols from ./test.exe...done.
(gdb) b 13
Breakpoint 1 at 0x8048c38: file test.cpp, line 13.
(gdb) r
Starting program: /tmp/test.exe 

Breakpoint 1, main () at test.cpp:13
13    std::cout << "Hello World!" << std::endl;
(gdb) p ((std::string*)(my_set._M_t._M_impl._M_header._M_left+1))->c_str()
$1 = 0x804d014 "AA"
(gdb) printf "'%s'\n", ((std::string*)(my_set._M_t._M_impl._M_header._M_left+1))->c_str()
'AA'

And the syntax to access the first node of my_set (which is apparently a "red-black tree" of nodes), I consulted the https://gist.github.com/skyscribe/3978082 gdb script noted by @AndreyStarodubtsev, in particular the function pset (which for some reason tended to give me syntax errors - possibly because of the optimized reference to std::string)

sdaau
  • 36,975
  • 46
  • 198
  • 278
  • 1
    You can try putting `std::string aa;` inside a new file, compiles it, and make an executable linking both files, this new one and the test.cpp. In this way, you will not need to change test.cpp – Amadeus Nov 16 '15 at 19:14