Debugging Custom C++ Types in Codeblocks (Code::Blocks)

  • Posted on: 14 December 2014

When I develop C++ on Linux, and I'm not in a purist/makefile/command-line mood, I like to use Code::Blocks. It doesn't quite have the feature set that Eclipse does, but Eclipse always feels a bit cumbersome to me. On the flipside, Code::Blocks seems much more lightweight.

In this article, I'm going to explain how to debug custom types with Code::Blocks. First, let's look at a motivating example. Here's a cpp file that defines and uses a simple array class, myarray:


#include <iostream>
#include <vector>
#include <string>
#include <math.h>
#include <cstdlib>

class myarray
{
    public:

    myarray(int sz):
        n(sz),
        arr( new double[sz] )
    {}

    ~myarray(){ delete[] arr;}

    double* arr;
    int     n;

};


int main ()
{
    std::cout << "custom type test" << std::endl;

    int n = 5;

    myarray the_array(5);

    for(int i = 0; i < the_array.n; i++){
            the_array.arr[i] = rand();
    }


  return 0;
}

Nothing overly complicated there.

Now let's debug the program, and 'watch' the_array:


Debugging in Code::Blocks.

We can see the members of the_array, but not in the most informative way. We'd like to see the data behind the pointer, and maybe some other information, too (although, there isn't much information to be had for this object).

The point is, we want Code::Blocks' debugger to recognize our new type, and create a more useful output. What we are really doing is registering a new type with Linux's defacto debugger, gdb, but you could remain unaware for some time before you realize that that's where the debugging magic happens (purists are frowning now).

To do this in a simple (if incomplete) way, we just need to modify two files that should have been installed when Codeblocks was installed:

  • gdb_types.script (for me, this was found at /usr/share/codeblocks/scripts)
  • stl-views-1.0.3.gdb (for me, this was found at /usr/share/codeblocks/scripts)

First, let's tackle modifying stl-views-1.0.3.gdb. This file is used for prettified output of STL types, like vector, and string. Ideally, we add another file alongside this one for our own types, but, to keep things simple, we'll just add our type into this file.

Since our new type is somewhat similar to vector, I first copied that, and then modified it to my needs. Here's what I came up with:

#
# myarray type
#

define pmyarray
	if $argc == 0
	else
		set $size = $arg0.n
		set $size_max = $size - 1
	end
	if $argc == 1
		set $i = 0
		while $i  0
		printf "Size = %u\n", $size
		printf "Element "
		whatis *$arg0.arr
		printf "More custom stuff! "

	end
end

Hopefully, this code isn't too terrifying. The function finds the size of whatever instance of my_array we're printing, prints that many elements of the array (using p), and then prints some other information about the object.

The semantics of how gdb calls this isn't quite on the 'critical path' for this tutorial, so we'll leave it alone for now.

Now, we have to 'register' the new type, which, in a way, helps Code::Blocks and gdb know when to use the code we just wrote above. This is done in gdb_types.script.

First, we need to add the following boilerplate-type block to the function RegisterTypes(driver),

    //our new type -- myarray
    driver.RegisterType(
        _T("myarray, hi!"),
        _T("[^[:alnum:]_]*myarray[^[:alnum:]_]*"),	//a regexp to match our type
        _T("Evaluate_myarray"),						//name of our evalulate function (to be written)
        _T("Parse_myarray")							//name of our parse function (to be written)
    );

This should appear analogous to other types already registered in gdb_types.script. By the way, this file is written in Squirrel, which I'm not too knowledgable of.

Next, we need to add a function in this file called Evaluate_myarray. This function decides what to do when we watch a variable of type myarray in Code::Blocks. In our case, we're just going to tell gdb to use the 'overloaded' print function we wrote in stl-views-1.0.3.gdb:

function Evaluate_myarray(type, a_str, start, count)
{
    local result = _T("pmyarray ") + a_str;
    return result;
}

Finally, we need to add Parse_myarray. This function basically modifies the raw output from gdb, before something ends up in Code::Blocks' 'watch' window. To keep things super simple, we'll just pass the raw output from gdb to Code::Blocks:

function Parse_myarray(a_str,start)
{
    return a_str;
}

Ok, that's it! Now, when we watch the_array during debugging, we see something like this:


Debugging in Code::Blocks.

Clearly, we have some custom debug output for our new type. However, we still have remnants of the raw gdb output (for example, the '$x's). These artifacts can be removed in function Parse_myarray(a_str,start), however, at the moment, I'm not sure how to do this, since I'm pretty unfamiliar with Squirrel. Check back soon, and I may have an answer!

I hope this served as a quick-and-dirty intro to debugging with custom types in Code::Blocks. Yes, there's a lot more to it, in terms of flexibility, and understanding exactly what's going on, but this should serve as a good starting point.