Introduction
In a lot many C++ maintenance / enhancement projects, public
member variables are a reality, specially when these projects are converted from C to C++. In these projects, one issue which I faced constantly while debugging is where and how to set a breakpoint in order to find out when a certain public
member variable gets modified or is being accessed. These member variables are typically of the basic C, C++ types like int
, double
etc. The common reason to end up in this situation is due to the promotion of the C structs to C++ classes without any accessor or modifier methods, it mostly happens during the C to C++ migration, in order to speed up the project or sometimes because some lazy colleague takes a short cut with public
member variables.
Why is it so frustrating?
It's so frustrating to see the same member variable name being used in 10 different classes and then those 10 different classes are being used in 100 different files. And when you search for that variable you get 1000 hits and you keep wondering which one of these is the right place where my instance of the class is getting modified. Consider yourself lucky if you are facing this kind of trouble routinely.
What are my options while debugging?
There are certain solutions available for this problem in Visual Studio debugger. The favorite one is to set a breakpoint when the member variable gets modified. This solution works well in most of the cases. But there is a limitation - the breakpoint has to be set at runtime for the modification in the context of the instance of the class and you have to know the flow of execution well in order to get the right instance. When you have a lot of instances of the class you don't know which particular class is really of your interest. You will have to sweat a lot before setting the breakpoint in the right context or in the worst case you don't know the flow of the execution and won't be able to find any context of your interest.
Anyway, there is no way to set a breakpoint to check when a member variable is being accessed.
The 'Problem Code'
Let's see how the code looks for the debugging issue which I am talking about ...
class RequestA
{
public:
int v1;
int v2;
int status;
};
class RequestB
{
public:
int v3;
int v4;
int status;
};
void UserFunction1(int condition)
{
RequestA reqA1;
RequestA reqA2;
RequestB reqB1;
RequestB reqB2;
reqA1.status = 0;
reqA2.status = reqA1.status;
reqB1.status = reqA1.status;
if (reqB1.status == 0)
{
reqB2.status = reqB1.status;
}
switch(condition)
{
case 10:
reqA2.status = 0;
reqB1.status = 0;
reqA1.status = 2;
case 11:
reqA1.status = 3;
reqA2.status = 0;
reqB1.status = 0;
case 16:
reqA1.status = 3;
reqB2.status = 4;
case 19:
reqA1.status = 5;
reqA1.status = 3;
reqB1.status = 3;
}
}
void UserFunction2(int anotherCondition)
{
RequestA reqA1;
RequestA reqA2;
RequestB reqB1;
RequestB reqB2;
reqA1.status = 1;
reqA2.status = 2;
reqB1.status = 3;
reqB2.status = 4;
reqA1.status = reqA2.status;
reqA2.status = reqB1.status ;
reqB1.status = reqB2.status ;
reqB2.status = reqA2.status;
}
My solution to set the breakpoint for modifications or accesses
In this context, I find the following simple C++ trick is very useful to catch the code which modifies the public
variables. I define a wrapper class around the basic int
, let's call it BasicIntWrapper
. It provides the operators necessary to extract int
value and assign it back.
class BasicIntWrapper
{
int m_i;
public:
operator int()
{
return m_i;
}
int operator = (const int& value)
{
return (m_i = value);
}
};
Now for the debug build let's refine the class member status as BasicIntWrapper
status; Let's see the redefined class RequestB
.
class RequestB
{
public:
int v3;
int v4;
#ifdef _DEBUG
BasicIntWrapper status;
#else
int status;
#endif
};
An even better approach will be to define this wrapper class as a member class of RequestB
. This helps in 2 ways:
- Catches the modifications per class
- Takes care of name conflicts
class RequestB
{
public:
int v3;
int v4;
#ifdef _DEBUG
class BasicIntWrapper
{
int m_i;
public:
operator int()
{
return m_i;
}
int operator = (const int& value)
{
return (m_i = value);
}
};
BasicIntWrapper status;
#else
int status;
#endif
};
Now, if you set a breakpoint in the operators of the class BasicIntWrapper
, you get all the class wide modifications caught.
Final Comments
My experience is - There is no silver bullet for this issue, but this trick helps refining a breakpoint location to a great extent, before using the break points in Visual Studio.
Author: Vishal Jadhav
email : vishalya@yahoo.com