I want to track all the memory(size allocated by std lib) allocated by all STL containers like map,list,vector etc. I just want to track STL container not regular object creation. Basically want to override new and delete of std lib.
Example
class demo {
public:
int i;
std::list<int> mylist;
}
int main() {
demo dd = new demo(); // -> Don't want to track this. Just want to track
// mylist(size of my list)
}
I found out that std has it's own allocator option. For example list has it is allocator
template < class T, class Alloc = allocator<T> > class list;
What is the default allocator if I don't defined anything. I have thousand of list and none of then has allocator and I don't want to change each one of them manually. So, what I was thinking if there is way where I can replace default allocator with mine.
How to do this ?
The default allocator for the standard containers is std::allocator
, it is used for all standard containers (std::vector
, std::list
, etc) when an allocator is not provided.
To track allocations and deallocations you will have to create an allocator that you can use for tracking. You could use something like this:
template<typename _Ty>
struct MyAllocator
{
typedef _Ty value_type;
static _Ty* allocate(std::size_t n)
{
//Code that runs every allocation
...
return std::allocator<_Ty>{}.allocate(n);
}
static void deallocate(_Ty* mem, std::size_t n)
{
//Code that runs every deallocation
...
std::allocator<_Ty>{}.deallocate(mem, n);
}
};
MyAllocator
mirrors std::allocator
but it allows you to run some of your own code when an allocation occurs. What you want to put there is up to you.
There are two ways for you to make all of the containers use your allocator.
You can replace all instances of std::list
(or std::vector
, std::map
, etc.) with a template alias. For std::list
the alias would look like this:
template<typename _Ty>
using MyList = std::list<_Ty, MyAllocator<_Ty>;
Replace all instances of std::list
with MyList
. Now your allocator is used by all of your containers. To apply this to another container change list
to the name of the container (Ex. for vector
rename the alias to MyVector
and change std::list
to std::vector
).
If you are unable to edit the file or you don't want to modify it there is another option. You can use a macro to replace all instances of list
with a class you have defined. This class will have to have been declared in namespace std
and you will have to make sure to include <list>
before setting up the macro. Setting it up for std::list
would look like this:
#include <list>
namespace std
{
template<typename _Ty, typename _Alloc = MyAllocator<_Ty>>
using tracked_list = list<_Ty, _Alloc>;
}
#define list tracked_list
For a different container, change list
to whatever container you want to replace (Ex. for vector
change tracked_list
to tracked_vector
in both locations and replace list
with vector
in all three locations. Make sure that this code is before any other includes that could use std::list
. If you put it in a header file, include that header before anything else. If it is in a source file, put it at the top of the file. This code will not override user supplied allocators, but it will make your allocator the default allocator.
This method will change variable names, and that could affect your code. If possible, you should use method 1. However, if you have code that you can't change or code that is in external headers and this needs to be applied to that too, this method should work.
If your main priority is short term low implementation cost (as opposed to longer term maintainability), one way to get around it is to wrap your demo
class into an outer
namespace that contains a std
namespace. Inside outer::std
you can redefine vector
, list
, etc. with your custom allocator. By doing so, all references to the std
namespace will resolve to outer::std
.
The only work needed (in addition to implementing your custom allocator) is to wrap all your code into the outer namespace and to provide all the required definitions in outer::std
. That part might easily become a pain. Future contributors might suffer.
Something like this:
#include <iostream>
#include <vector>
namespace outside
{
template<typename T>
struct MyAllocator
{
typedef typename std::allocator<T>::value_type value_type;
typedef typename std::allocator<T>::pointer pointer;
typedef typename std::allocator<T>::const_pointer const_pointer;
typedef typename std::allocator<T>::reference reference;
typedef typename std::allocator<T>::const_reference const_reference;
typedef typename std::allocator<T>::size_type size_type;
pointer allocate (size_type n, typename std::allocator<void>::const_pointer hint=0)
{
std::cerr << "allocate..." << std::endl;
return std::allocator<T>{}.allocate(n, hint);
}
void deallocate (pointer p, size_type n)
{
std::cerr << "deallocate..." << std::endl;
return std::allocator<T>{}.deallocate(p, n);
}
template <class Type> struct rebind
{
typedef MyAllocator<Type> other;
};
MyAllocator()
{
}
MyAllocator(const MyAllocator<T>& other )
{
}
template< class U >
MyAllocator( const MyAllocator<U>& other )
{
}
};
} // namespace outside
namespace outer
{
namespace std
{
template<class T>
using vector = ::std::vector<T, outside::MyAllocator<T>>;
} // namespace std
class demo
{
public:
int i;
std::vector<int> myVector;
};
} // namespace outer
int main()
{
using outer::demo;
std::cerr << "creating new demo" << std::endl;
demo *dd = new demo();
std::cerr << "resizing dd->myList" << std::endl;
dd->myVector.resize(10,3);
delete dd;
}