#include <iostream>
#include <vector>
#include <list>
#include <limits>

using namespace std;

struct memory_manager
{
    memory_manager() : allocations(0) {}

    void* allocate(size_t s)
    {
        ++allocations;
        return new char[s];
    }

    void deallocate(void* mem)
    {
        delete[] reinterpret_cast<char*>(mem);
        --allocations;
    }

    size_t allocations;
} 
the_manager;

template <typename T>
class custom_allocator
{
public:
    typedef T value_type;
    typedef value_type* pointer;
    typedef value_type const* const_pointer;
    typedef value_type& reference;
    typedef value_type const& const_reference;
    typedef size_t size_type;
    typedef ptrdiff_t difference_type;

    pointer allocate(size_type size, const void* = nullptr)
    {
        void* mem = the_manager.allocate(size * sizeof(value_type));
        return reinterpret_cast<pointer>(mem);
    }

    void deallocate(pointer mem, size_type)
    {
        the_manager.deallocate(mem);
    }

    size_type max_size() const
    {
        return numeric_limits<size_t>::max() / sizeof(value_type);
    }

    ////////////////////////////////////////////////////////////////////////////
    // boilerplate follows
    custom_allocator() {}

    custom_allocator(const custom_allocator&) {}

    template <typename Other>
    custom_allocator(const custom_allocator<Other>&) {}

    custom_allocator& operator=(const custom_allocator&) { return *this; }

    template <class other>
    custom_allocator& operator=(const custom_allocator<other>&) { return *this; }

    template <typename Other>
    struct rebind { typedef custom_allocator<Other> other; };

    pointer address(reference ref) const
    {
        return &ref;
    }

    const_pointer address(const_reference ref) const
    {
        return &ref;
    }

    void construct(pointer ptr, const value_type& val)
    {
        ::new(ptr)value_type(val);
    }

    void destroy(pointer ptr)
    {
        ptr->~value_type();
    }
};

template <typename T>
using custom_vector = std::vector<T, custom_allocator<T>>;

template <typename T>
using custom_list = std::list<T, custom_allocator<T>>;

struct custom_tag {};
custom_tag the_tag;

void* operator new(size_t size, const custom_tag&)
{
    return the_manager.allocate(size);
}

void operator delete(void* mem, const custom_tag&)
{
    the_manager.deallocate(mem);
}


#define custom_new new(the_tag)
#define custom_delete(mem) ::operator delete(mem, the_tag);
#define custom_delete_ar(mem) ::operator delete[](mem, the_tag);

int main()
{
    auto x = custom_new custom_list<int>;

    custom_delete(x);

    cout << the_manager.allocations << endl;

    return 0;
}
