accre / lstore

LStore - A fault-tolerant, performant distributed data storage framework.
http://www.lstore.org
Apache License 2.0
4 stars 5 forks source link

Make consistent constructor/destructor/new/del for structs in toolbox #60

Open PerilousApricot opened 8 years ago

PerilousApricot commented 8 years ago

For objects intended to be opaque, provide the following consistent API:

//malloc and init a new stack object
tbx_stack_t * tbx_stack_new();

// given an already allocated self of the proper size, initialize
// the memory
tbx_stack_t * tbx_stack_init(tbx_stack_t *self);

// return sizeof(tbx_stack_t)
size_t tbs_stack_size();

// destruct but DON'T free a stack object
void tbx_stack_fini(tbx_stack_t *self);

// destruct and free() a stack object
void tbx_stack_del();
PerilousApricot commented 8 years ago

In the same vein, module-level initialization/shutdown should take the names/signatures:

// Returns 0 on success, error code otherwise
int tbx_stack_startup();
int tbx_stack_shutdown();

If additional parameters are needed for startup/shutdown, they can be exposed by other functions, but sensible defaults should be exposed by a parameterless function.

PerilousApricot commented 8 years ago

Can destructors throw? Should the function signature be void?

PerilousApricot commented 8 years ago
// given an already allocated self of the proper size, initialize
// the memory
tbx_stack_t * tbx_stack_init(tbx_stack_t *self);

Perhaps this should be

// given an already allocated self of the proper size, initialize
// the memory
int tbx_stack_init(tbx_stack_t *self);
PerilousApricot commented 8 years ago

Strawman. Of course, C doesn't have C++'s idea of perfect forwarding, so additional constructor parameters are kinda annoying. _new() would need to be split into the allocation half, and then N other halves that forward to each version of _init()

//******************************************************************************
// Boilerplate for tbx_sl_t
//******************************************************************************
tbx_sl_t *tbx_sl_new()
{
    tbx_sl_t *sl = (tbx_sl_t *)malloc(sizeof(tbx_sl_t));
    if (!sl) // Do we log here?
        goto error_1;

    apr_status_t ret = apr_pool_create(&(sl->pool), NULL);
    if (ret != APR_SUCCESS)
        goto error_2;

    ret = apr_thread_mutex_create(&(sl->lock), APR_THREAD_MUTEX_DEFAULT, sl->pool);
    if (ret != APR_SUCCESS)
        goto error_3;

    if (tbx_sl_init(sl)) {
        goto error_4;
    }

    return sl;

error_4:
    apr_thread_mutex_destroy(sl->lock);
error_3:
    apr_pool_destroy(sl->pool);
error_2:
    free(sl);
error_1:
    return NULL;
}

void tbx_sl_del(tbx_sl_t * self)
{
    tbx_sl_fini(sl);
    apr_thread_mutex_destroy(self->lock);
    apr_pool_destroy(self->pool);
    free(self);
}

int tbx_sl_init(self)
{
    return tbx_sl_init_full(self, 20, 0.25, 0, &tbx_sl_compare_int, NULL, NULL, NULL); 
}

void tbx_sl_fini(tbx_sl_t * self)
{
    tbx_sl_empty(self);
    destroy_skiplist_node(self, self->head);
}

size_t tbx_sl_size()
{
    return sizeof(tbx_sl_t);
}
int tbx_sl_init_full(tbx_sl_t *self, 
                                 int maxlevels, double p, int allow_dups,
                                 tbx_sl_compare_t *compare,
                                 tbx_sl_key_t *(*dup)(tbx_sl_key_t *a),
                                 void (*key_free)(tbx_sl_key_t *a),
                                 void (*data_free)(tbx_sl_data_t *a))
{
    // Perform initialization
}