public inbox for libstdc++@gcc.gnu.org
 help / color / mirror / Atom feed
* Unify container pretty printers [PR65230]
@ 2022-08-09 10:35 Ulrich Drepper
  0 siblings, 0 replies; only message in thread
From: Ulrich Drepper @ 2022-08-09 10:35 UTC (permalink / raw)
  To: gcc-patches, libstdc++

[-- Attachment #1: Type: text/plain, Size: 1155 bytes --]

In PR65320 Martin raised the point that the pretty printer for the C++
containers is inconsistent across the different types.  It's also
inconsistent when it comes to showing different states (empty vs not) of
the same type.

In addition, IMO some more information should be printed like the template
parameters which otherwise certainly can be retrieved through ptype but
along with a lot more information one doesn't look for.

In the attached patch I've changed the pretty printers of most of the
containers to be mostly consistent, at least as far as it is possible given
the different nature.  I've also fixed some bugs (e.g., printing indeces
for set elements).

You can see the side-by-side comparison of the output in the text file I
attached to the bug (https://gcc.gnu.org/bugzilla/attachment.cgi?id=53419).
Bring very long lines or use 'less -S'.

Jonathan and I discussed whether such big changes are possible.  Someone
could consider the output guaranteed by some ABI.  We came to the
conclusion that this is a bad idea and shouldn't even start to plant that
idea in people's minds.  The pretty printer output is meant for humans.

Comments?

[-- Attachment #2: d-containers-printers --]
[-- Type: application/octet-stream, Size: 17003 bytes --]

--- libstdc++-v3/python/libstdcxx/v6/printers.py	2022-07-27 18:39:55.611944765 +0200
+++ libstdc++-v3/python/libstdcxx/v6/printers.py	2022-08-05 18:11:13.207812168 +0200
@@ -242,7 +242,6 @@ class SharedPointerPrinter:
         state = 'empty'
         refcounts = self._get_refcounts()
         targ = self.val.type.template_argument(0)
-        targ = strip_versioned_namespace(str(targ))
 
         if refcounts != 0:
             usecount = refcounts['_M_use_count']
@@ -251,7 +250,7 @@ class SharedPointerPrinter:
                 state = 'expired, weak count %d' % weakcount
             else:
                 state = 'use count %d, weak count %d' % (usecount, weakcount - 1)
-        return '%s<%s> (%s)' % (self.typename, targ, state)
+        return '%s<%s> (%s)' % (self.typename, str(targ), state)
 
 def _tuple_impl_get(val):
     "Return the tuple element stored in a _Tuple_impl<N, T> base class."
@@ -482,7 +481,11 @@ class StdVectorPrinter:
                 return ('[%d]' % count, elt)
 
     def __init__(self, typename, val):
-        self.typename = strip_versioned_namespace(typename)
+        match = re.match(r'^(std::(__debug::)?vector)<(.*), std::allocator<\3 ?> >$', val.type.name)
+        if match is None:
+            self.typename = strip_versioned_namespace(val.type.name)
+        else:
+            self.typename = '%s<%s>' % (match[1], match[3])
         self.val = val
         self.is_bool = val.type.template_argument(0).code == gdb.TYPE_CODE_BOOL
 
@@ -503,11 +506,17 @@ class StdVectorPrinter:
             bl = 8 * itype.sizeof
             length   = bl * (finish - start) + fo
             capacity = bl * (end - start)
-            return ('%s<bool> of length %d, capacity %d'
-                    % (self.typename, int (length), int (capacity)))
+            res = ('%s of length %d, capacity %d'
+                   % (self.typename, int (length), int (capacity)))
+            if length == 0:
+                res += ' = {}'
+            return res
         else:
-            return ('%s of length %d, capacity %d'
-                    % (self.typename, int (finish - start), int (end - start)))
+            res = ('%s of length %d, capacity %d'
+                   % (self.typename, int (finish - start), int (end - start)))
+            if int (finish - start) == 0:
+                res += ' = {}'
+            return res
 
     def display_hint(self):
         return 'array'
@@ -545,6 +554,47 @@ class StdBitReferencePrinter:
             return 'invalid std::vector<bool>::reference'
         return bool(self.val['_M_p'].dereference() & (self.val['_M_mask']))
 
+class StdArrayPrinter:
+    "Print a std::array"
+
+    class _iterator(Iterator):
+        def __init__(self, arr):
+            self.els = arr['_M_elems']
+            if self.els.type.code == gdb.TYPE_CODE_STRUCT:
+                # A zero-sized array has an empty struct as value
+                self.n = 0
+            else:
+                self.n = self.els.type.sizeof // self.els[0].type.sizeof
+            self.count = 0
+
+        def __iter__(self):
+            return self
+
+        def __next__(self):
+            if self.count == self.n:
+                raise StopIteration
+            count = self.count
+            self.count += 1
+            elt = self.els[count]
+            return ('[%d]' % count, elt)
+
+    def __init__(self, typename, val):
+        self.typename = strip_versioned_namespace(val.type.name)
+        self.val = val
+
+    def children(self):
+        return self._iterator(self.val)
+
+    def to_string(self):
+        res = self.typename
+        if self.val['_M_elems'].type.code == gdb.TYPE_CODE_STRUCT:
+            # A zero-sized array has an empty struct as value
+            res += ' = {}'
+        return res
+
+    def display_hint(self):
+        return 'array'
+
 class StdTuplePrinter:
     "Print a std::tuple"
 
@@ -616,16 +666,18 @@ class StdTuplePrinter:
                 return ('[%d]' % (self.count - 1), impl['_M_head_impl'])
 
     def __init__ (self, typename, val):
-        self.typename = strip_versioned_namespace(typename)
+        self.typename = val.type.name
         self.val = val;
 
     def children (self):
         return self._iterator (self.val)
 
     def to_string (self):
+        res = self.typename
         if len (self.val.type.fields ()) == 0:
-            return 'empty %s' % (self.typename)
-        return '%s containing' % (self.typename)
+            res += ' = {}'
+        return res
+
 
 class StdStackOrQueuePrinter:
     "Print a std::stack or std::queue"
@@ -766,12 +818,23 @@ class StdMapPrinter:
             return result
 
     def __init__ (self, typename, val):
-        self.typename = strip_versioned_namespace(typename)
+        match = re.match(r'^(std::(__debug::)?(multi)?map)<(.*), (.*), std::less<\4 ?>, std::allocator<std::pair<\4 const, \5> > >$', val.type.name)
+        if match is None:
+            match = re.match(r'^(std::(__debug::)?(multi)?map)<(.*), (.*), (.*), std::allocator<std::pair<\4 const, \5> > >$', val.type.name)
+            if match is None:
+                self.typename = val.type.name
+            else:
+                self.typename = '%s<%s, %s, %s>' % (match[1], match[4], match[5], match[6])
+        else:
+            self.typename = '%s<%s, %s>' % (match[1], match[4], match[5])
         self.val = val
 
     def to_string (self):
-        return '%s with %s' % (self.typename,
-                               num_elements(len(RbtreeIterator (self.val))))
+        nelems = len(RbtreeIterator (self.val))
+        res = '%s with %s' % (self.typename, num_elements(nelems))
+        if nelems == 0:
+            res += ' = {}'
+        return res
 
     def children (self):
         node = lookup_node_type('_Rb_tree_node', self.val.type).pointer()
@@ -797,35 +860,64 @@ class StdSetPrinter:
             item = next(self.rbiter)
             item = item.cast(self.type).dereference()
             item = get_value_from_Rb_tree_node(item)
-            # FIXME: this is weird ... what to do?
-            # Maybe a 'set' display hint?
             result = ('[%d]' % self.count, item)
             self.count = self.count + 1
             return result
 
     def __init__ (self, typename, val):
-        self.typename = strip_versioned_namespace(typename)
+        match = re.match(r'^(std::(__debug::)?(multi)?set)<(.*), std::less<\4 ?>, std::allocator<\4 ?> >$', val.type.name)
+        if match is None:
+            match = re.match(r'^(std::(__debug::)?(multi)?set)<(.*)(, .*), std::allocator<\4 ?> >$', val.type.name)
+            if match is None:
+                self.typename = val.type.name
+            else:
+                self.typename = '%s<%s%s>' % (match[1], match[4], match[5])
+        else:
+            self.typename = '%s<%s>' % (match[1], match[4])
         self.val = val
 
     def to_string (self):
-        return '%s with %s' % (self.typename,
-                               num_elements(len(RbtreeIterator (self.val))))
+        nelem = len(RbtreeIterator (self.val))
+        res = '%s with %s' % (self.typename, num_elements(nelem))
+        if nelem == 0:
+            res += ' = {}'
+        return res
 
     def children (self):
         node = lookup_node_type('_Rb_tree_node', self.val.type).pointer()
         return self._iter (RbtreeIterator (self.val), node)
 
+    def display_hint (self):
+        return 'array'
+
 class StdBitsetPrinter:
     "Print a std::bitset"
 
     def __init__(self, typename, val):
-        self.typename = strip_versioned_namespace(typename)
+        self.typename = val.type.name
         self.val = val
 
+    @staticmethod
+    def _is_empty(bts):
+        wtype = bts.type
+        if wtype.code != gdb.TYPE_CODE_ARRAY:
+            return bts == 0
+        for idx in range(wtype.sizeof // wtype.target ().sizeof):
+            if bts[idx] != 0:
+                return False
+        return True
+
     def to_string (self):
         # If template_argument handled values, we could print the
         # size.  Or we could use a regexp on the type.
-        return '%s' % (self.typename)
+        res = '%s' % (self.typename)
+        try:
+            # Zero-sized bitsets do not have the data element _M_w
+            if self._is_empty(self.val['_M_w']):
+                res += ' = {}'
+        except:
+            res += ' = {}'
+        return res
 
     def children (self):
         try:
@@ -846,7 +938,7 @@ class StdBitsetPrinter:
             words = [words]
             tsize = wtype.sizeof
 
-        nwords = wtype.sizeof / tsize
+        nwords = wtype.sizeof // tsize
         result = []
         byte = 0
         while byte < nwords:
@@ -895,7 +987,11 @@ class StdDequePrinter:
             return result
 
     def __init__(self, typename, val):
-        self.typename = strip_versioned_namespace(typename)
+        match = re.match(r'^(std::(__debug::)?deque)<(.*), std::allocator<\3 ?> >$', val.type.name)
+        if match is None:
+            self.typename = val.type.name
+        else:
+            self.typename = '%s<%s>' % (match[1], match[3])
         self.val = val
         self.elttype = val.type.template_argument(0)
         size = self.elttype.sizeof
@@ -914,7 +1010,10 @@ class StdDequePrinter:
 
         size = self.buffer_size * delta_n + delta_s + delta_e
 
-        return '%s with %s' % (self.typename, num_elements(long(size)))
+        res = '%s with %s' % (self.typename, num_elements(long(size)))
+        if size == 0:
+            res += ' = {}'
+        return res
 
     def children(self):
         start = self.val['_M_impl']['_M_start']
@@ -1025,7 +1124,19 @@ class Tr1UnorderedSetPrinter:
     "Print a std::unordered_set or tr1::unordered_set"
 
     def __init__ (self, typename, val):
-        self.typename = strip_versioned_namespace(typename)
+        match = re.match(r'^(std::(__debug::)?unordered_(multi)?set)<(.*), std::hash<\4 ?>, std::equal_to<\4 ?>, std::allocator<\4 ?> >$', val.type.name)
+        if match is None:
+            match = re.match(r'^(std::(__debug::)?unordered_(multi)?set)<(.*)(, .*), std::equal_to<\4 ?>, std::allocator<\4 ?> >$', val.type.name)
+            if match is None:
+                match = re.match(r'^(std::(__debug::)?unordered_(multi)?set)<(.*)(, .*), std::allocator<\4 ?> >$', val.type.name)
+                if match is None:
+                    self.typename = val.type.name
+                else:
+                    self.typename = '%s<%s%s>' % (match[1], match[4], match[5])
+            else:
+                self.typename = '%s<%s%s>' % (match[1], match[4], match[5])
+        else:
+            self.typename = '%s<%s>' % (match[1], match[4])
         self.val = val
 
     def hashtable (self):
@@ -1035,7 +1146,10 @@ class Tr1UnorderedSetPrinter:
 
     def to_string (self):
         count = self.hashtable()['_M_element_count']
-        return '%s with %s' % (self.typename, num_elements(count))
+        res = '%s with %s' % (self.typename, num_elements(count))
+        if count == 0:
+            res += ' = {}'
+        return res
 
     @staticmethod
     def format_count (i):
@@ -1047,11 +1161,26 @@ class Tr1UnorderedSetPrinter:
             return izip (counter, Tr1HashtableIterator (self.hashtable()))
         return izip (counter, StdHashtableIterator (self.hashtable()))
 
+    def display_hint(self):
+        return 'array'
+
 class Tr1UnorderedMapPrinter:
     "Print a std::unordered_map or tr1::unordered_map"
 
     def __init__ (self, typename, val):
-        self.typename = strip_versioned_namespace(typename)
+        match = re.match(r'^(std::(__debug::)?unordered_(multi)?map)<(.*), (.*), std::hash<\4 ?>, std::equal_to<\4 ?>, std::allocator<std::pair<\4 const, \5> > >$', val.type.name)
+        if match is None:
+            match = re.match(r'^(std::(__debug::)?unordered_(multi)?map)<(.*), (.*), (.*), std::equal_to<\4 ?>, std::allocator<std::pair<\4 const, \5> > >$', val.type.name)
+            if match is None:
+                match = re.match(r'^(std::(__debug::)?unordered_(multi)?map)<(.*), (.*), (.*), (.*), std::allocator<std::pair<\4 const, \5> > >$', val.type.name)
+                if match is None:
+                    self.typename = val.type.name
+                else:
+                    self.typename = '%s<%s, %s, %s, %s>' % (match[1], match[4], match[5], match[6], match[7])
+            else:
+                self.typename = '%s<%s, %s, %s>' % (match[1], match[4], match[5], match[6])
+        else:
+            self.typename = '%s<%s, %s>' % (match[1], match[4], match[5])
         self.val = val
 
     def hashtable (self):
@@ -1061,7 +1190,10 @@ class Tr1UnorderedMapPrinter:
 
     def to_string (self):
         count = self.hashtable()['_M_element_count']
-        return '%s with %s' % (self.typename, num_elements(count))
+        res = '%s with %s' % (self.typename, num_elements(count))
+        if count == 0:
+            res += ' = {}'
+        return res
 
     @staticmethod
     def flatten (list):
@@ -1492,26 +1624,23 @@ class StdPairPrinter:
 
         def __init__(self, val):
             self.val = val
-            self.which = 'first'
+            self.which = 0
 
         def __iter__(self):
             return self
 
         def __next__(self):
-            if self.which is None:
+            if self.which > 1:
                 raise StopIteration
             which = self.which
-            if which == 'first':
-                self.which = 'second'
-            else:
-                self.which = None
-            return (which, self.val[which])
+            self.which += 1
+            return ('[%d]' % which, self.val['first' if which == 0 else 'second'])
 
     def children(self):
         return self._iter(self.val)
 
     def to_string(self):
-        return None
+        return self.val.type.name
 
 class StdCmpCatPrinter:
     "Print a comparison category object"
@@ -1552,15 +1681,6 @@ class StdErrorCodePrinter:
         return None
 
     @classmethod
-    def _find_standard_errc_enum(cls, name):
-        for ns in ['', _versioned_namespace]:
-            try:
-                qname = 'std::{}{}'.format(ns, name)
-                return cls._find_errc_enum(qname)
-            except RuntimeError:
-                pass
-
-    @classmethod
     def _match_net_ts_category(cls, cat):
         net_cats = ['stream', 'socket', 'ip::resolver']
         for c in net_cats:
@@ -1601,10 +1721,10 @@ class StdErrorCodePrinter:
             is_errno = cls._system_is_posix
         if typ.tag.endswith('::future_error_category'):
             name = 'future'
-            enum = cls._find_standard_errc_enum('future_errc')
+            enum = cls._find_errc_enum('std::future_errc')
         if typ.tag.endswith('::io_error_category'):
             name = 'io'
-            enum = cls._find_standard_errc_enum('io_errc')
+            enum = cls._find_errc_enum('std::io_errc')
 
         if name is None:
             try:
@@ -1697,7 +1817,7 @@ class StdSpanPrinter:
             return '[%d]' % count, (self.begin + count).dereference()
 
     def __init__(self, typename, val):
-        self.typename = strip_versioned_namespace(typename)
+        self.typename = typename
         self.val = val
         if val.type.template_argument(1) == gdb.parse_and_eval('static_cast<std::size_t>(-1)'):
             self.size = val['_M_extent']['_M_extent_value']
@@ -1734,7 +1854,7 @@ class StdAtomicPrinter:
     "Print a std:atomic"
 
     def __init__(self, typename, val):
-        self.typename = strip_versioned_namespace(typename)
+        self.typename = typename
         self.val = val
         self.shptr_printer = None
         self.value_type = self.val.type.template_argument(0)
@@ -2004,7 +2124,7 @@ class FilteringTypePrinter(object):
         self.enabled = True
 
     class _recognizer(object):
-        "The recognizer class for FilteringTypePrinter."
+        "The recognizer class for TemplateTypePrinter."
 
         def __init__(self, match, name):
             self.match = match
@@ -2160,8 +2280,6 @@ def build_libstdcxx_dictionary ():
     libstdcxx_printer = Printer("libstdc++-v6")
 
     # libstdc++ objects requiring pretty-printing.
-    # In order from:
-    # http://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen/a01847.html
     libstdcxx_printer.add_version('std::', 'basic_string', StdStringPrinter)
     libstdcxx_printer.add_version('std::__cxx11::', 'basic_string', StdStringPrinter)
     libstdcxx_printer.add_container('std::', 'bitset', StdBitsetPrinter)
@@ -2180,6 +2298,7 @@ def build_libstdcxx_dictionary ():
     libstdcxx_printer.add_version('std::', 'stack', StdStackOrQueuePrinter)
     libstdcxx_printer.add_version('std::', 'unique_ptr', UniquePointerPrinter)
     libstdcxx_printer.add_container('std::', 'vector', StdVectorPrinter)
+    libstdcxx_printer.add_container('std::', 'array', StdArrayPrinter)
     # vector<bool>
 
     if hasattr(gdb.Value, 'dynamic_type'):

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2022-08-09 10:36 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-08-09 10:35 Unify container pretty printers [PR65230] Ulrich Drepper

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).