design patterns - C++ object lifetime profiling

ObjectInfo class is a diagnostic class intended to track statistical data such as life time and number of objects. A specific class inherits from ObjectInfo as shown. A member of that specific class is then declared in the body of a profiled class.

Although the solution works it is hard to maintain as it requires the profiling class to be keep in sync with the profiled class as the class name is used to identify the later. It would also be hard to extend the profiling class to gather different information such as the size of object.

Propose a better solution where the dependencies between the profiled and profiling classes are minimal.

Is it possible to implement a check that would determine if the object of the profiled class was created on stack or heap?

-- ObjectInfo.h --

#pragma once

class ObjectInfo
{
 public:
  ObjectInfo(const char* objectName);
  virtual ~ObjectInfo(void);

 private:

   static int  m_counter;
   int         m_objectNumber;
   const char* m_className;
};

-- ObjectInfo.cpp --

#include "StdAfx.h"
#include "ObjectInfo.h"
#include <iostream>
#include "TimePrinter.h"

using namespace std;
int ObjectInfo::m_counter = 0;
ObjectInfo::ObjectInfo(const char* name) :
m_className(name)
{
   m_objectNumber = ++m_counter;
   cout << "Object: " << m_className << "# " << m_objectNumber << " created @ " <<
   TimePrinter()<< endl;
}

ObjectInfo::~ObjectInfo(void)
{
  cout << "Object: " << m_className << "# " << m_objectNumber << " destroyed @ " << 
  TimePrinter() << endl;
}

-- The use pattern --

struct _AInfo : public ObjectInfo {
    _AInfo() : ObjectInfo("_A") {}
};

struct _A {
  _AInfo m_info;
};

I originally thought this question is asking about using C++ reflection technique to gather the runtime information. However, I don't know if there is a way to measure the lifetime of objects using C++ reflection. Furhter, can you consider C++ reflection is a technique that reduces the dependencies between the profiled and profiling classes ?

1 Answer

  1. Frank- Reply

    2019-11-16

    This can track stack vs. heap object creations

    #include <iostream>
    
    template <class CRTP>
    struct AllocationTracker
    {
        AllocationTracker()
        {
            ++totalCreated;
        }
    
        void* operator new(size_t sz)
        {
            ++heapCreated;
            return ::operator new(sz);
        }
    
        static int totalCreated;
        static int heapCreated;
    };
    
    template <class CRTP>
    int AllocationTracker<CRTP>::totalCreated;
    template <class CRTP>
    int AllocationTracker<CRTP>::heapCreated;
    
    class Derived : public AllocationTracker<Derived>
    {
    };
    
    int main()
    {
        using namespace std;
        cout << Derived::heapCreated << "/" << Derived::totalCreated << endl; // 0/0
        Derived dStack;
        cout << Derived::heapCreated << "/" << Derived::totalCreated << endl; // 0/1
        Derived* dHeap = new Derived;
        cout << Derived::heapCreated << "/" << Derived::totalCreated << endl; // 1/2
    }
    

    This uses the CRTP that Bartek brought up in the comments on your question. This lets us track each derived type separately. It also wraps the standard new for the base class, which is inherited by derived classes, which allows us to track heap allocations. So we know how many instances are created, and how many on the heap, and we can infer that the rest were on the stack (unless you're using object pools or some other more exotic allocation strategies in your program).

Leave a Reply

Your email address will not be published. Required fields are marked *

You can use these HTML tags and attributes <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>