[2/2] libstdc++: Add pretty printer for std::stringstream
Commit Message
Signed-off-by: Philipp Fent <fent@in.tum.de>
---
libstdc++-v3/python/libstdcxx/v6/printers.py | 37 +++++++++++++++++++
.../libstdc++-prettyprinters/debug.cc | 5 +++
.../libstdc++-prettyprinters/simple.cc | 5 +++
.../libstdc++-prettyprinters/simple11.cc | 5 +++
4 files changed, 52 insertions(+)
Comments
Thanks for the patch. Comments below.
On Sun, 4 Sept 2022 at 19:48, Philipp Fent via Libstdc++
<libstdc++@gcc.gnu.org> wrote:
>
> Signed-off-by: Philipp Fent <fent@in.tum.de>
> ---
> libstdc++-v3/python/libstdcxx/v6/printers.py | 37 +++++++++++++++++++
> .../libstdc++-prettyprinters/debug.cc | 5 +++
> .../libstdc++-prettyprinters/simple.cc | 5 +++
> .../libstdc++-prettyprinters/simple11.cc | 5 +++
> 4 files changed, 52 insertions(+)
>
> diff --git a/libstdc++-v3/python/libstdcxx/v6/printers.py b/libstdc++-v3/python/libstdcxx/v6/printers.py
> index d70c8d5d616..5083f693387 100644
> --- a/libstdc++-v3/python/libstdcxx/v6/printers.py
> +++ b/libstdc++-v3/python/libstdcxx/v6/printers.py
> @@ -969,6 +969,39 @@ class StdStringPrinter:
> def display_hint (self):
> return 'string'
>
> +class StdStringBufPrinter:
> + "Print a std::basic_stringbuf"
> +
> + def __init__(self, _, val):
> + self.val = val
> +
> + def to_string(self):
> + pbase = self.val['_M_out_beg']
> + pptr = self.val['_M_out_cur']
> + egptr = self.val['_M_in_end']
It would be nice to encapsulate this into a generic helper for all
streambufs, as this part isn't specific to basic_stringbuf. We could
then reuse it for printing spanbufs and other types in future. It
could be a function that takes a streambuf and returns a tuple of
(pbase, pptr, egptr).
> + # Logic from basic_stringbuf::_M_high_mark()
> + if pptr:
> + if not egptr or pptr > egptr:
> + return pbase.string(length = pptr - pbase)
> + else:
> + return pbase.string(length = pptr - egptr)
Shouldn't this be egptr - pbase?
This suggests the tests are inadequate. I think this bug would have
been found by a test something like:
std::stringstream ssin("input", std::ios::in);
// { dg-final { note-test ssin "\"input\"" } }
The (egptr - pbase) case is also needed for printing an istringstream.
> + return self.val['_M_string']
> +
> + def display_hint(self):
> + return 'string'
> +
> +class StdStringStreamPrinter:
> + "Print a std::basic_stringstream"
> +
> + def __init__(self, _, val):
> + self.val = val
> +
> + def to_string(self):
> + return self.val['_M_stringbuf']
This assumes that the stream is still using its stringbuf, which is
probably true 99% of the time, but isn't guaranteed. In the following
situation, the printer would give potentially misleading output:
std::stringstream ss("xxx");
ss.rdbuf(std::cin.rdbuf());
I wonder if we want to have a check that self.val['_M_streambuf'] ==
self.val['_M_stringbuf'].address
Alternatively, don't provide a printer for the stringstream at all,
just for the stringbuf, and then when GDB uses its default display for
the stringstream it will show that member.
> +
> + def display_hint(self):
> + return 'string'
> +
> class Tr1HashtableIterator(Iterator):
> def __init__ (self, hashtable):
> self.buckets = hashtable['_M_buckets']
> @@ -2232,6 +2265,10 @@ def build_libstdcxx_dictionary ():
> libstdcxx_printer.add_version('std::', 'initializer_list',
> StdInitializerListPrinter)
> libstdcxx_printer.add_version('std::', 'atomic', StdAtomicPrinter)
> + libstdcxx_printer.add_version('std::', 'basic_stringbuf', StdStringBufPrinter)
> + libstdcxx_printer.add_version('std::__cxx11::', 'basic_stringbuf', StdStringBufPrinter)
> + libstdcxx_printer.add_version('std::', 'basic_stringstream', StdStringStreamPrinter)
> + libstdcxx_printer.add_version('std::__cxx11::', 'basic_stringstream', StdStringStreamPrinter)
Wouldn't we want it for ostringstream and istringstream too?
Hi Jonathan,
I've sent an updated patch addressing your comments here:
https://gcc.gnu.org/pipermail/libstdc++/2022-September/054587.html
Details below.
On 06.09.22 13:27, Jonathan Wakely wrote:
>> + pbase = self.val['_M_out_beg']
>> + pptr = self.val['_M_out_cur']
>> + egptr = self.val['_M_in_end']
>
> It would be nice to encapsulate this into a generic helper for all
> streambufs, as this part isn't specific to basic_stringbuf. We could
> then reuse it for printing spanbufs and other types in future. It
> could be a function that takes a streambuf and returns a tuple of
> (pbase, pptr, egptr).
I factored this into a reusable access_streambuf_ptrs().
>> + # Logic from basic_stringbuf::_M_high_mark()
>> + if pptr:
>> + if not egptr or pptr > egptr:
>> + return pbase.string(length = pptr - pbase)
>> + else:
>> + return pbase.string(length = pptr - egptr)
>
> Shouldn't this be egptr - pbase?
>
> This suggests the tests are inadequate. I think this bug would have
> been found by a test something like:
>
> std::stringstream ssin("input", std::ios::in);
> // { dg-final { note-test ssin "\"input\"" } }
>
> The (egptr - pbase) case is also needed for printing an istringstream.
Good catch! I fixed that bug and added a test for this in the testsuite.
>
>> + return self.val['_M_string']
>> +
>> + def display_hint(self):
>> + return 'string'
>> +
>> +class StdStringStreamPrinter:
>> + "Print a std::basic_stringstream"
>> +
>> + def __init__(self, _, val):
>> + self.val = val
>> +
>> + def to_string(self):
>> + return self.val['_M_stringbuf']
>
> This assumes that the stream is still using its stringbuf, which is
> probably true 99% of the time, but isn't guaranteed. In the following
> situation, the printer would give potentially misleading output:
>
> std::stringstream ss("xxx");
> ss.rdbuf(std::cin.rdbuf());
>
> I wonder if we want to have a check that self.val['_M_streambuf'] ==
> self.val['_M_stringbuf'].address
>
> Alternatively, don't provide a printer for the stringstream at all,
> just for the stringbuf, and then when GDB uses its default display for
> the stringstream it will show that member.
I didn't know one could redirect a stringstream, since its rdbuf()
method hides the basic_ios rdbuf() methods. But of course, that's still
possible via the base class...
The Patch v2 checks that case in the StdStringStreamPrinter constructor,
and follows that redirect in to_string().
>
>> +
>> + def display_hint(self):
>> + return 'string'
>> +
>> class Tr1HashtableIterator(Iterator):
>> def __init__ (self, hashtable):
>> self.buckets = hashtable['_M_buckets']
>> @@ -2232,6 +2265,10 @@ def build_libstdcxx_dictionary ():
>> libstdcxx_printer.add_version('std::', 'initializer_list',
>> StdInitializerListPrinter)
>> libstdcxx_printer.add_version('std::', 'atomic', StdAtomicPrinter)
>> + libstdcxx_printer.add_version('std::', 'basic_stringbuf', StdStringBufPrinter)
>> + libstdcxx_printer.add_version('std::__cxx11::', 'basic_stringbuf', StdStringBufPrinter)
>> + libstdcxx_printer.add_version('std::', 'basic_stringstream', StdStringStreamPrinter)
>> + libstdcxx_printer.add_version('std::__cxx11::', 'basic_stringstream', StdStringStreamPrinter)
>
> Wouldn't we want it for ostringstream and istringstream too?
Indeed. The updated patch registers the pretty printer for all flavors.
@@ -969,6 +969,39 @@ class StdStringPrinter:
def display_hint (self):
return 'string'
+class StdStringBufPrinter:
+ "Print a std::basic_stringbuf"
+
+ def __init__(self, _, val):
+ self.val = val
+
+ def to_string(self):
+ pbase = self.val['_M_out_beg']
+ pptr = self.val['_M_out_cur']
+ egptr = self.val['_M_in_end']
+ # Logic from basic_stringbuf::_M_high_mark()
+ if pptr:
+ if not egptr or pptr > egptr:
+ return pbase.string(length = pptr - pbase)
+ else:
+ return pbase.string(length = pptr - egptr)
+ return self.val['_M_string']
+
+ def display_hint(self):
+ return 'string'
+
+class StdStringStreamPrinter:
+ "Print a std::basic_stringstream"
+
+ def __init__(self, _, val):
+ self.val = val
+
+ def to_string(self):
+ return self.val['_M_stringbuf']
+
+ def display_hint(self):
+ return 'string'
+
class Tr1HashtableIterator(Iterator):
def __init__ (self, hashtable):
self.buckets = hashtable['_M_buckets']
@@ -2232,6 +2265,10 @@ def build_libstdcxx_dictionary ():
libstdcxx_printer.add_version('std::', 'initializer_list',
StdInitializerListPrinter)
libstdcxx_printer.add_version('std::', 'atomic', StdAtomicPrinter)
+ libstdcxx_printer.add_version('std::', 'basic_stringbuf', StdStringBufPrinter)
+ libstdcxx_printer.add_version('std::__cxx11::', 'basic_stringbuf', StdStringBufPrinter)
+ libstdcxx_printer.add_version('std::', 'basic_stringstream', StdStringStreamPrinter)
+ libstdcxx_printer.add_version('std::__cxx11::', 'basic_stringstream', StdStringStreamPrinter)
# std::regex components
libstdcxx_printer.add_version('std::__detail::', '_State',
@@ -29,6 +29,7 @@
#include <list>
#include <map>
#include <set>
+#include <sstream>
#include <vector>
#include <ext/slist>
@@ -110,6 +111,10 @@ main()
__gnu_cxx::slist<int>::iterator slliter = sll.begin();
// { dg-final { note-test slliter {47} } }
+ std::stringstream sstream;
+ sstream << "abc";
+// { dg-final { note-test sstream "\"abc\"" } }
+
std::cout << "\n";
return 0; // Mark SPOT
}
@@ -30,6 +30,7 @@
#include <list>
#include <map>
#include <set>
+#include <sstream>
#include <vector>
#include <ext/slist>
@@ -169,6 +170,10 @@ main()
__gnu_cxx::slist<int>::iterator slliter0;
// { dg-final { note-test slliter0 {non-dereferenceable iterator for __gnu_cxx::slist} } }
+ std::stringstream sstream;
+ sstream << "abc";
+// { dg-final { note-test sstream "\"abc\"" } }
+
std::cout << "\n";
return 0; // Mark SPOT
}
@@ -30,6 +30,7 @@
#include <list>
#include <map>
#include <set>
+#include <sstream>
#include <vector>
#include <ext/slist>
@@ -162,6 +163,10 @@ main()
__gnu_cxx::slist<int>::iterator slliter0;
// { dg-final { note-test slliter0 {non-dereferenceable iterator for __gnu_cxx::slist} } }
+ std::stringstream sstream;
+ sstream << "abc";
+// { dg-final { note-test sstream "\"abc\"" } }
+
std::cout << "\n";
return 0; // Mark SPOT
}