Here is the initial crude implmentation which compiles under Comeau with no
warnings:
_______________ _______________ _______________ _______________ _______
#include <cassert>
#include <cstdlib>
#include <new>
#include <list>
template<typena me T, std::size_t BUFDEPTH = 1024>
class region_allocato r {
struct region {
T m_buf[BUFDEPTH];
std::size_t m_idx;
void ctor() {
m_idx = 0;
}
void dtor() {
for (std::size_t i = 0; i < m_idx; ++i) {
m_buf[i].~T();
}
}
void* allocate() {
std::size_t const idx = m_idx;
if (idx + 1 BUFDEPTH) {
return NULL;
}
return (void*)&m_buf[idx];
}
void commit() {
++m_idx;
}
void flush() {
dtor();
ctor();
}
};
std::list<regio n*m_regions;
struct allocation {
region* m_region;
void* m_mem;
};
region* prv_expand(){
region* r = (region*)std::m alloc(sizeof(*r ));
if (! r) {
throw std::bad_alloc( );
};
r->ctor();
m_regions.push_ front(r);
return r;
}
inline void prv_allocate(al location* const a) {
typename std::list<regio n*>::iterator i;
for (i = m_regions.begin (); i != m_regions.end() ; ++i) {
a->m_mem = (*i)->allocate();
if (a->m_mem) {
a->m_region = (*i);
return;
}
}
a->m_region = prv_expand();
a->m_mem = a->m_region->allocate();
assert(a->m_mem);
}
#define REGION_PRV_ALLO CATE(mp_params) \
allocation a; \
prv_allocate(&a ); \
T* const obj = new (a.m_mem) T mp_params; \
a.m_region->commit(); \
return obj
public:
struct flush_guard {
region_allocato r& m_ralloc;
public:
flush_guard(reg ion_allocator& ralloc) : m_ralloc(ralloc ) {
m_ralloc.flush( );
}
~flush_guard() {
m_ralloc.flush( );
}
};
region_allocato r() {
prv_expand();
}
~region_allocat or() {
flush();
std::free(m_reg ions.front());
m_regions.pop_f ront();
}
void flush() {
std::size_t const depth = m_regions.size( );
for (std::size_t i = 1; i < depth; ++i) {
region* const r = m_regions.back( );
r->dtor();
std::free(r);
m_regions.pop_b ack();
}
m_regions.front ()->flush();
}
inline T* allocate() {
REGION_PRV_ALLO CATE(());
}
template<typena me P1>
inline T* allocate(P1 p1) {
REGION_PRV_ALLO CATE((p1));
}
template<typena me P1, typename P2>
inline T* allocate(P1 p1, P2 p2) {
REGION_PRV_ALLO CATE((p1, p2));
}
template<typena me P1, typename P2, typename P3>
inline T* allocate(P1 p1, P2 p2, P3 p3) {
REGION_PRV_ALLO CATE((p1, p2, p3));
}
// [and on and on for more params...];
};
/* Usage Example
_______________ _______________ _______________ _______________ __*/
#include <cstdio>
#include <string>
class foo {
unsigned const m_id;
public:
foo(unsigned const id) : m_id(id) {
std::printf("(% p)->foo::foo(%u)\n ", (void*)this, id);
}
~foo() {
std::printf("(% p)->foo::~foo() - %u\n", (void*)this, m_id);
}
};
class foo2 {
unsigned const m_id;
std::string const m_name;
public:
foo2(unsigned const id, std::string const name)
: m_id(id), m_name(name) {
std::printf("(% p)->foo2::foo2(% u, %s)\n",
(void*)this, id, name.c_str());
}
~foo2() {
std::printf("(% p)->foo2::~foo2( ) - %u, %s\n",
(void*)this, m_id, m_name.c_str()) ;
}
};
struct node {
node* m_next;
node(node* next) : m_next(next) {
std::printf("(% p)->node::node(%p) \n",
(void*)this, (void*)next);
}
~node() {
std::printf("(% p)->node::~node(%p )\n",
(void*)this, (void*)m_next);
}
};
int main(void) {
{
region_allocato r<foo, 2foo_alloc;
{
region_allocato r<foo, 2>::flush_guar d fguard(foo_allo c);
foo* f1 = foo_alloc.alloc ate(1);
foo* f2 = foo_alloc.alloc ate(2);
foo* f3 = foo_alloc.alloc ate(3);
foo* f4 = foo_alloc.alloc ate(4);
}
foo* f1 = foo_alloc.alloc ate(5);
foo* f2 = foo_alloc.alloc ate(6);
foo* f3 = foo_alloc.alloc ate(7);
foo* f4 = foo_alloc.alloc ate(8);
{
region_allocato r<foo2foo2_allo c;
foo2* f2_1 = foo2_alloc.allo cate(123, "Chris");
foo2* f2_2 = foo2_alloc.allo cate(456, "John");
foo2* f2_3 = foo2_alloc.allo cate(789, "Amy");
foo2* f2_4 = foo2_alloc.allo cate(777, "Kim");
foo2* f2_5 = foo2_alloc.allo cate(999, "Jane");
}
{
region_allocato r<unsigned, 64int_alloc;
unsigned* a1 = int_alloc.alloc ate(1);
unsigned* a2 = int_alloc.alloc ate(2);
unsigned* a3 = int_alloc.alloc ate(3);
unsigned* a4 = int_alloc.alloc ate();
*a4 = 123456789;
std::printf("%u - %u - %u - %u\n", *a1, *a2, *a3, *a4);
}
}
{
region_allocato r<nodenode_allo c;
node* head = NULL; // linked-list
// fill
for (unsigned i = 0; i < 512; ++i) {
node* n = node_alloc.allo cate(head);
head = n;
}
// empty list
head = NULL;
node_alloc.flus h();
// refill
for (unsigned i = 0; i < 2048; ++i) {
node* n = node_alloc.allo cate(head);
head = n;
}
}
return 0;
}
_______________ _______________ _______________ _______________ _______
Notice how there are no explicit calls to a per-object deallocation
function? The `region_allocat or<T, ...>::flush_gua rd' object uses RAII to
automatically invoke the `region_allocat or<T, ...>::flush()' procedure which
calls the dtor of all contained objects and automatically frees excess
region memory. One nice thing is that it allows one to pass variable number
of arguments to the objects ctor.
Also, please take notice of how I can create a linked-list, and simple call
`flush()' to automatically destroy all of its nodes _without_ explicitly
traversing it. IMVHO, that ability can come in handy.
Well, what do you think of the initial design? Is it crap? How would you
improve it?
Thanks for all of your time! I really do appreciate it.
:^)