In RAD Studio 10.2.1 we added support for debug visualizers for Delphi generic and C++ template types. A debug visualizer is an IDE plugin that allows you to change the display of a variable in the various debug windows, such as Local Variables and Watches.

"Great!", you may think, "but how do I actually write one of these visualizers?"

Pretty easily, as it turns out.

A debug vizualiser is an interfaced class that implements some specific Open Tools API interfaces (see toolsapi.pas), located in a DLL or package, and an instance of which is registered with the IDE when the package is loaded. Those interfaces are:

IOTADebuggerVisualizer, IOTADebuggerVisualizer250: name and description of the visualizer, list of types it's interested in; version 250 adds support for generics or templates by allowing you to specify that a type string is for a generic or template type.

IOTADebuggerVisualizerValueReplacer: this lets you replace the result of an expression returned by the debug evaluator with some other content - whatever you want. You can get more advanced than this, such as showing an external window for the results. This is just a simple example.

IOTAThreadNotifier, IOTAThreadNotifier160: let you handle an evaluation completing. A notifier, by convention in the ToolsAPI, also always has four other methods to do with saving, destroying and modification that are not meaningful in this example.

Generic and template debug visualizer example

Here's a complete visualizer code snippet. To test this out, create a new package, and add designide to the Requires. Then add a unit called GenericVisualizer.pas to it and paste in the following source code. To test it out, just right-click and Install, and then it's running right there inside your local IDE instance. (You can also debug the IDE with IDE if you want to do some more advanced work and load it while debugging an instance of the IDE with your first copy of the IDE.)

This example is longer than the C++ one, because it demonstrates both generic classes and interfaces. It also shows some descendant types, where you need to register the descendant type separately. (For a non-generic-type debug visualizer, the visualizer is called for descendants of the registered type automatically.)

Comments

Matjaz J12875
Thursday, 1 February 2018

Could you make c++ example for showing boost::shared_ptr . i did it by myself but when i evaluate expresion
".px" (.px is a pointer of type T), if T is integer its shown in watch field as integer but if T is string. evaluation stops at string object so in watch field is shown somethink like {npos=...} structure of string. Seems to me that visualizer not trigger next visualizer which is std::string visualizer. Please tell me if this is possible to do (that visualizers work in chain for boost::shared_ptr, first evaluator for shared_ptr:x then will call then evaluator for std::string.__MyValue ) and watch field shows for exampe "abc" not {npos:0xfffff, __My_Value:"abc\0akavadfa"}

Thanks for answers

Matjaz J12875
Thursday, 1 February 2018

David,
could you please publish a working C++Builder package for this example?
In C++Builder the described steps do not work. Example (.pas) does not compile because "uses ToolsAPI" fails.
I prefer source in c++, if is possible, thanks.

Johannes Weinert
Thursday, 1 February 2018

I published a C++ version of the snipped (mainly based on an example published by Remy, so it's his work) in the attachment group of the cppbuilder newsgroup. Unfortunately the debug visualizer is not registered. The code compiles and the package is installed - but without effect.
I wish EMB would bundle some nice generic debug visualizers with their product.

Johannes Weinert
Wednesday, 31 January 2018

David,
could you please publish a working C++Builder package for this example?
In C++Builder the described steps do not work. First the simple task of adding designide to the Requires is a nightmare as one has to specify the path to the .bpi. Second, even if that is done then the example does not compile because "uses ToolsAPI" fails. I have no clue where to add which path to get it work.

TIA, Hans

Kaspar N7812
Thursday, 7 December 2017

The above code does not compile:
There are two definitions of EvaluateComplete; therfore they should be marked with overload.

David Millington
Wednesday, 13 December 2017

There was a typo in the code. The original IOTAThreadNotifier interface had a method typo, 'EvaluteComplete'; this was fixed in the inherited interface. So there are indeed two different methods.

Kaspar N7812
Friday, 15 December 2017

Thanks Dave,
This one was hard to track...
Anyway, in the interface section the method name is EvaluteComplete, but in the implementation it is EvaluateComplete (note the missing letter "a"). Now it works fine.