Code Focused

How Helper Variables Can Simplify Debugging

If the data you work with is complex and hard to understand, it's easy to get stuck on them when debugging. Add helper variables to make data much simpler to use and comprehend.

Debuggers offer excellent ways to view properties of objects in the memory. You can click on a variable to see its current state. Still, some objects are complex and to get the full picture you have to dig inside its contents or even class hierarchy. In some cases, it might be useful to add some helper variables to your objects. When you're debugging, that variable might bring vital information or just make your life easier.

For example, when you work with graph structures you probably often need to check surrounding elements. Usually those neighbor variables/containers (for example, m_vecNeighbours) are placed in a base class like a VertexBase, and if you're trying to see the context of MySpecialVertex that's three levels of class hierarchy lower, it's a pain to scan m_vecNeighbours every time. What if you update MySpecialVertex with some additional data from m_vecNeighbours? Then you can get the required information quickly without going through object hierarchies.

Figure 1 represents such a hierarchy. In this code, you operate on MySpecialVertex objects, but usually, you have only pointers to VertexBase:

auto myVertex = make_shared<MySpecialVertex>("Cracow");
  myVertex->AddVertex(make_shared<MySpecialVertex>("London"));
  myVertex->AddVertex(make_shared<MySpecialVertex>("Berlin"));
  myVertex->AddVertex(make_shared<MySpecialVertex>("Paric"));
  myVertex->AddVertex(make_shared<MySpecialVertex>("Warsaw"));
[Click on image for larger view.] Figure 1. Class Hierarchy for Vertex in a Graph

Figure 2 shows the debugging session in which you try to see what cities are surrounded by myVertex. Look how many steps need to be executed to see one of the cities.

[Click on image for larger view.] Figure 2. Complex Object Hierarchy While Debugging

In the example class structure, if we want to easily see the surrounding cities around myVertex, Listing 1 will do the trick.

Listing 1: MySpecialVertex Class Includes Helper Variables
class MySpecialVertex : public MapVertex
{
public:
  MySpecialVertex() { }
  MySpecialVertex(const string& name) { SetName(name); }

#ifdef _DEBUG
  void UpdateSurroundingNames() { 
    m_vecSurroundingNames.clear(); 
    for (const auto& v : m_vecNeighbours) {
      if (v->IsMapVertex())
        m_vecSurroundingNames.push_back(
          static_cast<const MapVertex *>(v.get())->GetName());
    }
  }
#endif

private:
  int m_val{ 0 };
  int m_size{ 0 };
#ifdef _DEBUG
  vector<string> m_vecSurroundingNames;
#endif
};

In this code, I've introduced a special debug container for city names. There's also a new method UpdateSurroundingNames. Now you can easily see the place where your vertex is located (see Figure 3).

[Click on image for larger view.] Figure 3. Debugging with Additional Variables

Isn't that easier? You have all the cities in one place. I'm not a fan of #ifdefs but that's the easier way to bring those new variables only into debug mode.

One disadvantage: how to maintain that additional state? The m_vecNeighbours variable might be easily changed, so you'd have to add some logic to sync that properly. And while that's true, maybe for debugging you don't need to have the full and perfect solution -- just a hint is sufficient. That's why the UpdateSurroundingNames method might be invoked only from time to time. Usually, you don't need all the cities to be synced, and you need two or three names to see the context location. Of course, if you need full and accurate information, then you're free to implement a more advanced solution.

I suggest adding new variables only into the debug configuration so that the final release builds are unaffected. Still, do it with care: If the debug code is a lot different than the final code you might get unexpected bugs. Also, new variables might change alignment of the object, so I wouldn't add it into critical/optimized modules.

Wrapping Up
In this tip, I wanted to share a basic idea of introducing extra debug variables into your objects. That new code might give you more information, and the process of understanding the full picture could be easier. It has some drawbacks, though: Code could get a bit uglier. Still, I believe helper variables can be effective.

The example code can be found in my GitHub repository.

About the Author

Bartlomiej Filipek is a software developer in Poland who specializes in C++, Windows and graphics programming. He worked for a number of companies developing multimedia, document editors, games, graphics drivers and flight planning systems, and has taught game development at local university. Follow Bart's blog at http://www.bfilipek.com and on Twitter @fenbf.

comments powered by Disqus

Featured

  • Compare New GitHub Copilot Free Plan for Visual Studio/VS Code to Paid Plans

    The free plan restricts the number of completions, chat requests and access to AI models, being suitable for occasional users and small projects.

  • Diving Deep into .NET MAUI

    Ever since someone figured out that fiddling bits results in source code, developers have sought one codebase for all types of apps on all platforms, with Microsoft's latest attempt to further that effort being .NET MAUI.

  • Copilot AI Boosts Abound in New VS Code v1.96

    Microsoft improved on its new "Copilot Edit" functionality in the latest release of Visual Studio Code, v1.96, its open-source based code editor that has become the most popular in the world according to many surveys.

  • AdaBoost Regression Using C#

    Dr. James McCaffrey from Microsoft Research presents a complete end-to-end demonstration of the AdaBoost.R2 algorithm for regression problems (where the goal is to predict a single numeric value). The implementation follows the original source research paper closely, so you can use it as a guide for customization for specific scenarios.

  • Versioning and Documenting ASP.NET Core Services

    Building an API with ASP.NET Core is only half the job. If your API is going to live more than one release cycle, you're going to need to version it. If you have other people building clients for it, you're going to need to document it.

Subscribe on YouTube