Reputation: 2888
I want to create a debugging application over my libraries for testing, debugging, ... purposes. But I don't want to give the end-user additional non-necessary APIs.
For example, consider an application that visualizes the program's plugin usage of the library to the end-user. So I can't use standard debuggers like GDB or LLDB with a Release build containing some debugging information. How could be the library/debugging application designed for that?
I just introduce an additional symbol into my main library class as the class friend:
Complete code: MyLibrary.hh
class MyClassPrivate;
class MyClass {
public:
friend class MyClassDebugger;
int value() const;
private:
friend class MyClassPrivate;
std::unique_ptr<MyClassPrivate> impl_;
};
And then I export the class header and the class private header to the user and the user could just define the MyClassDebugger
symbol and use it to access to the MyClass
private implementations:
Complete code: main.cc
class MyClassDebugger {
public:
void modify(MyClass& object)
{
object.impl_->value = 100;
}
};
int main()
{
MyClass object;
MyClassDebugger().modify(object);
std::cout << object.value() << std::endl;
}
The complete code of the example: https://gist.github.com/gccore/397fb6147280bd32b6fe340aa6ce579a
Upvotes: 1
Views: 118
Reputation: 21946
I would consider dropping your “No additional public APIs” requirement, and implement some reflection interface for your objects, which allows users to list properties exposed to debug interface, get property values, and update these values. Here’s an example how that API may look like.
// Change this enum to contain types of various properties you have in your classes
// For instance, if you don't have nested objects, remove the corresponding entry from the enum
enum struct ePropertyType: uint8_t
{
Empty = 0,
Int32,
FP32,
String,
NestedObject
};
struct iDebugView;
// Variant structure for the values
struct sPropVariant
{
// The type of the value
ePropertyType type = ePropertyType::Empty;
// The value itself
union
{
int int32;
float fp32;
const char* string;
iDebugView* nestedObject;
};
};
// Describes a single field of some particular type
struct sPropertyDesc
{
std::string name;
ePropertyType type;
};
// Debugger interface to view and change private fields of objects
struct iDebugView
{
// Get descriptor for all properties exposed by this object.
// Note this doesn't include the values i.e. only depends on the object's type but not the instance
// Your implementation should return reference of a global variable, possibly lazily initialized on first use
virtual const std::vector<sPropertyDesc>& listProperties() const = 0;
// Get value of the property identified by 0-based index
// Returns ePropertyType::Empty if the index was out of range
virtual sPropVariant getValue( uint32_t index ) const = 0;
// Set value of the property identified by 0-based index.
// Returns false for errors such as index out of range, type mismatch, or trying to set NestedObject property
virtual bool setValue( uint32_t index, const sPropVariant& value ) = 0;
};
While not terribly complicated, that new API should enable GUI similar to PropertyGrid in C#, allowing to inspect and modify private properties of your classes.
Upvotes: 1