
Hi Luke, [...] I have two ideas. First, put a counter in monotonic that is
incremented when allocations happen and decremented when deallocations happen. If the counter is ever zero you blow away all the buffers automatically.
This is a good idea; I'll add it. I currently keep track of allocation counts for informative reasons only for debug builds.
Second, you create a scoped object that encapsulates the idea of the monotonic storage's lifetime and delete the buffers when the object is destructed.
see https://svn.boost.org/svn/boost/sandbox/monotonic/boost/monotonic/local.hpp. monotonic::local<> storage; { std::list<int, monotonic::allocator<int> > list; // uses local storage, not global } Finally, you add a template parameter to monotonic to allow many programmers
working in the same application to declare their own monotonic type that has separate buffers and lifetime from eachother.
see https://svn.boost.org/svn/boost/sandbox/monotonic/boost/monotonic/region_all.... The nterface is: /// a monotonic region allocator uses a specified storage. /// Each region uses independent /// storage that may be used and reset. template <class T, size_t Region = 0> struct region_allocator; This is used like: typedef std::list<int, monotonic::region_allocator<int, 0> > List; typedef std::list<int, monotonic::region_allocator<int, 42> > OtherList; // .... monotonic::reset_region<42>(); etc. Basically, it allows you to have up to monotonic::DefaultSizes::MaxRegions of independantly controlled regions of storage. In this way managing the memory owned monotonic can be easy and safe. The
only problem left is therefore misuse. I'm trying to understand the risk of misuse, and it looks like running out of memory is the primary risk.
I mentioned earlier that it may be beneficial to add introspection to debug builds that warn about misuse, and I increasingly think it will be necessary.
Perhaps I missed this part of the discussion, but why is it named monotonic?
The name has been questioned earlier as well. The motivation for the name came from the fact that memory usage only ever increases - monotonically. I agree that the name is somewhat vague, especially considering that it can be used as a general stack+heap storage system, as well as an allocator.
I would think something that describes its intended usage would be better. boost::scratch_memory, for example. Anyone who keeps objects allocated with an allocator called scratch_memory around for the lifetime of his program has some obvious explaining to do.
I like this because it makes it clear that it is primarily for temporay use. It also works for the following: boost::scratch_memory::storage<> storage; { string &str = storage.create<string>("spam"); boost::array<Foo, 16> &foos = storage.create<boost::array<Foo, 16> >(); storage.destroy(str); storage.destroy(foos); } The usage is a bit clearer than boost::monotonic::storage<>
I also think that a more general interface would be to allow the user to supply and address and size of memory to use as the initial scratch memory with the automatic (on by default) ability to grow beyond that buffer if needed with system allocation on the heap using the chain.
This is a good idea and can be adopted. Currently, storage<StackSize, MinHeapIncrement> uses the stack first, then the heap. You can of course make it just on the stack with storage<N, 0> or just on the heap with storage<0, N>. However it could be made so that the first link in the chain is supplied by the user. See https://svn.boost.org/svn/boost/sandbox/monotonic/boost/monotonic/storage.hp... .
Thread safty should be controlled by a template parameter and enabled by default.
Currently, thread-safety is controlled by the allocator type: monotonic::allocator<T> // single-threaded monotonic::shared_allocator<T> // multi-threaded via mutex monotonic::local_allocator<T> // uses thread-local-storage for the storage Regards, Christian