With a suitable convention established for finding the end, sure.
But the simplest way to find the end is to put a size field or
pointer at the beginning, and we're back to the same alignment
question. So it's hard to explore that option without making the
entire issue more complicated.
I agree that a disadvantage to accounting at the end is the convention
for finding the end.
The example code offered elsethread has a function that takes a 'size_t'
which must match what was passed to the allocator, and passing this
around is certainly overhead.
For a simple string-accounting struct which keeps track of the size, one
can pass 'sizeof *my_string + my_string->max_length' (or whatever), but
you're right; that seems like an awkward thing to pass to a
reference-releasing function.
For statically-sized objects, a macro makes things a bit easier (note
that 'ptr' is only evaluated once, for whatever that's worth):
#define release(ptr) (release_((ptr), sizeof *(ptr)))
Here's a code example, below. It ought to compile as one file. If
anyone has time to review it, what're some advantages and disadvantages
for the approach?:
#define ALL_IN_ONE 1
/**** ref_cnt.h */
/* For 'size_t' */
#include <stddef.h>
/*** Macros */
#define GetRefForFixedSizeObj(obj) \
(GetRefFromPtrAndSize((obj), sizeof *(obj)))
/*** Object types */
typedef void ** ref_counted_obj;
/*** Function types */
typedef void f_ref_cnt_free(ref_counted_obj);
/*** Functions */
extern ref_counted_obj GetRefFromPtrAndSize(void *, size_t);
extern ref_counted_obj NewRefCountedObj(size_t, f_ref_cnt_free *);
extern ref_counted_obj GetRef(ref_counted_obj);
extern void PutRef(ref_counted_obj);
/**** ref_cnt.c */
#include <stdlib.h>
#if !ALL_IN_ONE
#include <stddef.h>
#include "ref_cnt.h"
#endif
/*** Types */
struct ref_counted_obj_ {
void * obj;
unsigned int ref_count;
f_ref_cnt_free * dealloc;
};
/*** Functions */
static ptrdiff_t ref_at(size_t size) {
size_t result;
result = size;
result += sizeof (struct ref_counted_obj_);
/* Check for wrap-around */
if (result < size)
return 0;
--result;
result /= sizeof (struct ref_counted_obj_);
return (ptrdiff_t) result;
}
ref_counted_obj GetRefFromPtrAndSize(
void * ptr,
size_t size
) {
ptrdiff_t ref_index;
struct ref_counted_obj_ * ref;
ref_index = ref_at(size);
if (!ptr || !ref_index)
return NULL;
/* Point to the accounting info */
ref = ptr;
ref += ref_index;
/* Increment ref count and check for wrap-around */
if (!++ref->ref_count) {
--ref->ref_count;
return NULL;
}
return &ref->obj;
}
ref_counted_obj NewRefCountedObj(
size_t size,
f_ref_cnt_free * dealloc
) {
ptrdiff_t ref_index;
size_t new_size;
struct ref_counted_obj_ * ref;
ref_index = ref_at(size);
if (!ref_index)
return NULL;
/* Calculate required size */
new_size = (ref_index + 1) * sizeof *ref;
/* Check for wrap-around */
if (new_size < size)
return NULL;
/* Allocate and check for failure */
ref = malloc(new_size);
if (!ref)
return NULL;
/* Populate accounting info */
ref[ref_index].obj = ref;
ref += ref_index;
ref->ref_count = 1;
ref->dealloc = dealloc;
return &ref->obj;
}
ref_counted_obj GetRef(ref_counted_obj ref) {
struct ref_counted_obj_ * ref_;
if (!ref)
return NULL;
ref_ = (void *) ref;
/* Increment ref count and check for wrap-around */
if (++ref_->ref_count) {
--ref_->ref_count;
return NULL;
}
return ref;
}
void PutRef(ref_counted_obj ref) {
struct ref_counted_obj_ * ref_;
if (!ref)
return;
ref_ = (void *) ref;
/* Decrement ref count and check for zero */
if (!--ref_->ref_count) {
ref_->dealloc(ref);
free(ref_->obj);
}
return;
}
/**** Test program */
#include <stdio.h>
#include <string.h>
#if !ALL_IN_ONE
#include <stdlib.h>
#include "ref_cnt.h"
#endif
/*** Macros */
#define GetFooRef(foo) \
(GetRefFromPtrAndSize((foo), sizeof *(foo) + (foo)->len))
/*** Types */
struct foo {
double bobble;
size_t len;
char bear[1];
};
/*** Functions */
f_ref_cnt_free foo_dealloc;
void foo_dealloc(ref_counted_obj ptr) {
struct foo * bar;
bar = *ptr;
printf(
"foo_dealloc(%p): {%f, %d, \"%s\"}\n",
*ptr,
bar->bobble,
(int)bar->len,
bar->bear
);
return;
}
int main(void) {
ref_counted_obj bar_ref;
struct foo * bar;
char laughsalot[] = "Laughs-a-lot";
bar_ref = NewRefCountedObj(
sizeof *bar + sizeof laughsalot,
foo_dealloc
);
if (!bar_ref) {
puts("NewRefCountedObj() failed!");
return EXIT_FAILURE;
}
bar = *bar_ref;
bar->bobble = 3.14159;
bar->len = sizeof laughsalot;
memcpy(bar->bear, laughsalot, sizeof laughsalot);
/* Test re-getting the accounting info */
bar_ref = NULL;
bar_ref = GetFooRef(bar);
if (!bar_ref) {
puts("GetRefFromPtrAndSize() failed!");
free(bar); /* Yeah */
return EXIT_FAILURE;
}
/* We have two references */
PutRef(bar_ref);
PutRef(bar_ref);
return EXIT_SUCCESS;
}