Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a C++ class gdb-friendly?

Tags:

c++

gdb

Consider the following example:

std::string s = "Hello!!";

(gdb) p s
$1 = "Hello!!";

Essentially, just providing the variable name is good enough to display the string. I don't have to, for example, type "p s.c_str()."

Is gdb using any implicit operator to get the display string? I need to do something similar for my class. Here is a trivial example of my class:

class MyClass {

private:
  std::string _name;
};
like image 613
Peter Avatar asked Sep 16 '20 17:09

Peter


2 Answers

You need to write a pretty-printer for your class. It is not something you do in your C++ class, but something you do in gdb (although matching your C++ class). The easiest way to do that is through gdb's Python API (you can also use the Guile language).

GDB already comes with pretty-printers for most of the standard library classes and that is why you can easily see an std::string object, an std::vector, etc. If you type info pretty-printer in gdb it will tell you about the pretty-printers it currently knows about and you will notice many std::something pretty printers.

If you use pass /r to the print command in gdb it will print the variable without using any possible registered pretty-printer that matches it. Try that with an std::string to see how it would be printed if gdb didn't come with a pretty-printer for it.


So, how can you write your own pretty-printers? For that, you should read GDB's documentation on this topic. But I find it much easier to start by reading and tweaking some existing pretty-printer you can find and then read gdb's documentation for the details.

For instance, I have a Coordinate class in one of my projects as below

class Coordinate {
private:
    double x;
    double y;
    double z;

public:
    ...
}

It's very easy to write a pretty-printer for this class. You create a python file with the following code

class CoordinatePrinter:
    def __init__(self, val):
        # val is the python representation of you C++ variable. 
        # It is a "gdb.Value" object and you can query the member 
        # atributes of the C++ object as below. Since the result is
        # another "gdb.Value" I'am converting it to a python float
        self.x = float(val['x'])
        self.y = float(val['y'])
        self.z = float(val['z'])

    # Whatever the `to_string` method returns is what will be printed in  
    # gdb when this pretty-printer is used
    def to_string(self):
        return "Coordinate(x={:.2G}, y={:.2G}, z={:.2G})".format(self.x, self.y, self.z)



import gdb.printing
# Create a "collection" of pretty-printers
# Note that the argument passed to "RegexpCollectionPrettyPrinter" is the name of the pretty-printer and you can choose your own
pp = gdb.printing.RegexpCollectionPrettyPrinter('cppsim')
# Register a pretty-printer for the Coordinate class. The second argument is a 
# regular expression and my Coordinate class is in a namespace called `cppsim`
pp.add_printer('Coordinate', '^cppsim::Coordinate$', CoordinatePrinter)
# Register our collection into GDB
gdb.printing.register_pretty_printer(gdb.current_objfile(), pp, replace=True)

Now all we need to do is to source this python file in gdb. For that, write in your .gdbinit file

source full_path_to_your_python_file_with_pretty_printers.py

When you start gdb it will run your .gdbinit file, which will load your pretty-printers. Note that these pretty-printers will often also work inside IDEs that use gdb.

If you are interested in more examples, I have created pretty-printers to some classes in the Armadillo library (vector, matrices and general linear algebra) which are available here.

like image 153
darcamo Avatar answered Sep 27 '22 15:09

darcamo


If you have libstdc++ pretty-printers installed (you already have them installed) your class is already gdb-friendly because they will be invoked when printing members of your class. If you have a lot of other class members besides _name you can also use set print pretty for easier distinguishing between them:

(gdb) p my_class 
$1 = {_name = ""}
(gdb) set print pretty 
(gdb) p my_class 
$2 = {
  _name = ""
}
(gdb) 
like image 42
ks1322 Avatar answered Sep 27 '22 15:09

ks1322