- - - continuation from main list of the thread: Re: Circular buffers (Was: C-based data structures library (like Boost but for C)?) - - -
<CONTENT-WARNING beware-list="armchair-expert, handwaving, computer language ramblings" /> Stewart Stremler wrote: > begin quoting James G. Sack (jim) as of Sat, Sep 30, 2006 at 01:43:51AM > -0700: > [snip] >> Hmmm, your code is storing pointers to the data, which I _don't think_ >> is the usual need (especially for interrupt server routines, for example). > > For interrupt server routines, you'd rewrite the code for your > particular data type anyway. For a reusable routine, void * is > the way to go, as you can then handle *any* sort of data. > Turns out your code is probably closer to al's requirements, since he wanted to avoid copying data. (I wanted to say 'ajl', for some reason, but can't find a MI for andy -- don't know where I got 'ajl'?) In his application, insert/put might really mean 'give me (a ptr to) the next place to dump my data into'. My code wouldn't work that way without some mangling. >> It's interesting, I think that we both implemented a required-hole >> method for keeping full and empty from being confused, and incidentally >> avoid any critical section worries. > > Yup. It seemed more elegant to do that than to allocate another element > in the structure to keep track of empty/non-empty status, and then to > have the additional overhead of checking such things. > >> Other than the data vs pointer thing, you expressed in indices what I >> did with pointers, and I choose a slightly roundabout pointer >> initialization (using pLastPut instead of pNextPut). > > It seemed a clearer way to express what I was doing. To me, at least. :) > On reflection, there was no real benefit from my using a 'pLastPut' variable, and it would have been easier reading if I had not done that. I might now include words to the effect: The consumer retrieves from and advances the head of the queue (pNextGet), but the head is never allowed to pass the tail (pNextPut). The empty condition occurs when the head and tail are equal, which is tested for before a data retrieval. The producer stores to and advances the tail of the queue (pNextPut), but the tail is never allowed to catch the head (pNextGet). The full condition occurs when an insert/put would violate this constraint, which is tested for before a data store. Technically, this means that the buffer must have one wasted data item as the cost for making it easy to distinguish between empty and full. >.. > I've forgotton how to remap a system call. The unit test will need to > replace malloc() with it's own version, that will check a global > variable that would limit the size of a malloc request, and then if > the requested amount of space is less than the limit, pass on the > call to malloc. I know *how* to test it... > > But it's that first trick I don't remember how to do. Wanna pick > this up in LPSG? Hmmm, it's been a long time, but I think I always relied on debug-build behaviors that diddled malloc/free . In this simple case, I suppose one might consider a manual substition of myMalloc myFree wrappers -- but somebody's already done that for us, I'm sure. Hmmm, know anything about mtrace (man 3 mtrace -- from glibc-utils)? I also see mention of memwatch and dmalloc. These above found via Memory Leak Detection in Embedded Systems http://www.linuxjournal.com/article/6059 Now, I've also heard of valgrind, but never used it. I'm sure a 'regular' c-programmer must know about and use all these things, everyday. > >> I might explain my choice of interface returning true/false and >> requiring a buffer on the get. NULL on failure is certainly a time >> honored tradition, but I believe one can argue against it. No so much >> here as in things like getc() where the return type has to be butchered >> to be able to indicate failure. But anyway, I kind of like making the >> user pass his own buffer which I fill. It perhaps calls attention to the >> need to test the return value. > > I picked up a dislike of "allocate your own buffer" some time ago; I > like my modules being responsible for creating and destroying the > data structures, and the code that uses it treating it as an opaque > type. I had pondered making the functions take void* instead of > CircBuf*, and hiding the structure in the .c file to enforce that, > but that wasn't the point of the exercise. :) Well, the producer (if we're copying data into/outof buffers) has to provide a source data container, so in that scenario, it might not be surprising if the consumer has to provide (and manage) a destination data container. >.. Looking at the original question, as well as past posts on related subjects, it is fun to ask/remark: People have probably been writing circular buffer code since before Carl was in undergrad school -- why don't we have some nice reusable choices in widely available libraries. C does not make it easy to write object-like code. One thing in particular, you have to use some type-casting which kind of detracts from the benefits argued for static typing. Hmmm.. wonder what Objective-C is like? Thinking about Chuck Esterbrook's posting, makes me admit that Microsoft's (or whoever they stole it from) work on Common language Runtime addresses a long-standing need. 'course, I sure would like the library code to be open, because that's one of the big things that chased me away from MS originally. It's now an even stronger requirement (open code), given the potential for bad behavior by the bad guys. Regards, ..jim -- [email protected] http://www.kernel-panic.org/cgi-bin/mailman/listinfo/kplug-lpsg
