I have been writing a lot of C code recently, and I've been running into a problem similar to the one I ran into with Go where I have a lot of code that looks like this:
if (foo() != 0) {
return -1;
}
bar();
which is similar to the constant if err != nil checks I see in Golang. I think I've come up with an interesting pattern for dealing with these error-prone sequences. I was inspired by functional languages that have andThen sequences for chaining together computations which may or may not succeed. I tried implementing a naive callback setup, but I realized this is practically impossible in C without lambdas, and it would be callback hell even with them. Then I thought about using jump, and I realized there might be a good way to do it. The interesting part is below. Without using this pattern, there would be a lot of if (Buffer_strcpy(...) != 0) checks or a mess of callback hell.
switch (setjmp(reference)) {
case -1:
// error branch
buffer->offset = offset;
Continuation_error(continuation, NULL);
case 0:
// action 0
Buffer_strcpy(buffer, "(", andThenContinuation);
case 1:
// action 1 (only called if action 0 succeeds)
Node_toString(binaryNode->left, buffer, andThenContinuation);
case 2:
Buffer_strcpy(buffer, " ", andThenContinuation);
case 3:
Node_toString(binaryNode->right, buffer, andThenContinuation);
case 4:
Buffer_strcpy(buffer, ")", andThenContinuation);
case 5:
Continuation_success(continuation, buffer->data + offset);
}
And here is a self-contained program which runs it:
#include <string.h>
#include <stdio.h>
#include <setjmp.h>
/*
* A continuation is similar to a Promise in JavaScript.
* - success(result)
* - error(result)
*/
struct Continuation;
/*
* The ContinuationVTable is essentially the interface.
*/
typedef struct {
void (*success)(struct Continuation *, void *);
void (*error)(struct Continuation *, void *);
} ContinuationVTable;
/*
* And the Continuation is the abstract class.
*/
typedef struct Continuation {
const ContinuationVTable *vptr;
} Continuation;
void Continuation_success(Continuation *continuation, void *result) {
continuation->vptr->success(continuation, result);
}
void Continuation_error(Continuation *continuation, void *result) {
continuation->vptr->error(continuation, result);
}
/*
* This is the "Promise" implementation we're interested in right now because it makes it easy to
* chain together conditional computations (those that should only proceed when upstream
* computations succeed).
*/
typedef struct {
// Superclass (this way the vptr will be in the expected spot when we cast this class)
Continuation super;
// Stores a reference to the big struct which contains environment context (basically a bunch
// of registers). This context is pretty similar to the context that you'd need to preserve
// during a function call.
jmp_buf *context;
// Allow computations to return a result.
void **result;
// The sequence index in the chain of computations.
int index;
} AndThenContinuation;
void AndThenContinuation_success(Continuation *continuation, void *result) {
AndThenContinuation *andThenContinuation = (AndThenContinuation *) continuation;
if (andThenContinuation->result != NULL) {
*andThenContinuation->result = result;
}
++andThenContinuation->index;
longjmp(*andThenContinuation->context, andThenContinuation->index);
}
void AndThenContinuation_error(Continuation *continuation, void *result) {
AndThenContinuation *andThenContinuation = (AndThenContinuation *) continuation;
if (andThenContinuation->result != NULL) {
*andThenContinuation->result = result;
}
longjmp(*andThenContinuation->context, -1);
}
const ContinuationVTable andThenContinuationVTable = (ContinuationVTable) {
.success = AndThenContinuation_success,
.error = AndThenContinuation_error,
};
void AndThenContinuation_init(AndThenContinuation *continuation, jmp_buf *context, void **result) {
continuation->super.vptr = &andThenContinuationVTable;
continuation->index = 0;
continuation->context = context;
continuation->result = result;
}
This part is an example of its use:
/*
* I defined a buffer class here which has methods to write to the buffer, which might fail if the
* buffer is out of bounds.
*/
typedef struct {
char *data;
size_t offset;
size_t capacity;
} Buffer;
void Buffer_strcpy(Buffer *buffer, const void *src, Continuation *continuation) {
size_t size = strlen(src) + 1;
if (buffer->offset + size > buffer->capacity) {
Continuation_error(continuation, NULL);
return;
}
memcpy(buffer->data + buffer->offset, src, size);
buffer->offset += size - 1; // don't count null character
Continuation_success(continuation, NULL);
}
/*
* A Node is just something with a toString method.
*/
struct NodeVTable;
typedef struct {
struct NodeVTable *vptr;
} Node;
typedef struct NodeVTable {
void (*toString)(Node *, Buffer *, Continuation *);
} NodeVTable;
void Node_toString(Node *node, Buffer *buffer, Continuation *continuation) {
node->vptr->toString(node, buffer, continuation);
}
/*
* A leaf node is just a node which copies its name to the buffer when toString is called.
*/
typedef struct {
Node super;
char *name;
} LeafNode;
void LeafNode_toString(Node *node, Buffer *buffer, Continuation *continuation) {
LeafNode *leafNode = (LeafNode *) node;
Buffer_strcpy(buffer, leafNode->name, continuation);
}
NodeVTable leafNodeVTable = (NodeVTable) {
.toString = LeafNode_toString,
};
void LeafNode_init(LeafNode *node, char *name) {
node->super.vptr = &leafNodeVTable;
node->name = name;
}
/*
* A binary node is a node whose toString method should simply return
* `(${toString(left)} ${toString(right)})`. However, we use the continuation construct because
* those toString calls may fail if the buffer has insufficient capacity.
*/
typedef struct {
Node super;
Node *left;
Node *right;
} BinaryNode;
void BinaryNode_toString(Node *node, Buffer *buffer, Continuation *continuation) {
BinaryNode *binaryNode = (BinaryNode *) node;
jmp_buf reference;
AndThenContinuation andThen;
AndThenContinuation_init(&andThen, &reference, NULL);
Continuation *andThenContinuation = (Continuation *) &andThen;
/*
* This is where the magic happens. The -1 branch is where errors are handled. The 0 branch is
* for the initial computation. Subsequent branches are for downstream computations.
*/
size_t offset = buffer->offset;
switch (setjmp(reference)) {
case -1:
// error branch
buffer->offset = offset;
Continuation_error(continuation, NULL);
case 0:
// action 0
Buffer_strcpy(buffer, "(", andThenContinuation);
case 1:
// action 1 (only called if action 0 succeeds)
Node_toString(binaryNode->left, buffer, andThenContinuation);
case 2:
Buffer_strcpy(buffer, " ", andThenContinuation);
case 3:
Node_toString(binaryNode->right, buffer, andThenContinuation);
case 4:
Buffer_strcpy(buffer, ")", andThenContinuation);
case 5:
Continuation_success(continuation, buffer->data + offset);
}
}
NodeVTable binaryNodeVTable = (NodeVTable) {
.toString = BinaryNode_toString,
};
void BinaryNode_init(BinaryNode *node, Node *left, Node *right) {
node->super.vptr = &binaryNodeVTable;
node->left = left;
node->right = right;
}
int main(int argc, char **argv) {
LeafNode a, b, c;
LeafNode_init(&a, "a");
LeafNode_init(&b, "b");
LeafNode_init(&c, "c");
BinaryNode root;
BinaryNode_init(&root, (Node *) &a, (Node *) &a);
BinaryNode right;
BinaryNode_init(&right, (Node *) &b, (Node *) &c);
root.right = (Node *) &right;
char data[1024];
Buffer buffer = (Buffer) {.data = data, .offset = 0};
buffer.capacity = sizeof(data);
jmp_buf reference;
AndThenContinuation continuation;
char *result;
AndThenContinuation_init(&continuation, &reference, (void **) &result);
switch (setjmp(reference)) {
case -1:
fprintf(stderr, "failure\n");
return 1;
case 0:
BinaryNode_toString((Node *) &root, &buffer, (Continuation *) &continuation);
case 1:
printf("success: %s\n", result);
}
return 0;
}
Really, I just want to know more about this style--what keywords should I be looking up? Is this style ever actually used?
Just to put my comment in an answer, here are a few thoughts. The first and foremost point, in my opinion, is that you are working in a procedural programming language where jumping is frowned upon and memory managing is a known pitfall. As such, it is probably best to go with a more known and much easier approach, which will be easily readable to your fellow coders:
if(foo() || bar() || anotherFunctions())
return -1;
If you need to return different error codes then yes, I would use multiple ifs.
Regarding answering the question directly, my second point is this is not very practical. You are implementing (quite cleverly I might add) a basic C++ classing systems along with something that almost looks like an exception system, albeit a basic one. The problem is, you rely heavily on the user of the framework to do a lot of management on their own - setting the jumps, initializing all the classes and using them correctly. It may be justified in the general class, but here you are implementing something not "native" to the language (and foreign to many of its users). The fact a "class" unrelated to your exception handling (the tree) needs to reference your Continuation directly is a red flag. A major improvement would probably be to provide a try function, such that the user just uses
if(try(f1, f2, f3, onError)) return -1;
Which would wrap all the usage of your structs, making them invisible, though still not disconnecting your continuation from the tree. Of course, this is getting quite close to that regular if above, and if you do it properly, you have a lot of memory management to do - threads, signals, what is supported? Can you make sure you never leak?
My final point, is not inventing the wheel. If you want try-except systems, change a language, or if you must use a preexisting library (I see exception4c is high on Google through SO, never used it though). If C is the tool of choice, return values, argument return values, and signal handlers would be my goto (pun intended?).
I would avoid setjmp/longjmp:
They make resource management hard.
Usage is uncommon, which makes code harder to understand and maintain.
For you particular example, you could avoid setjmp/longjmp with a state machine:
typedef enum {
error,
leftParen,
leftAction,
space,
rightAction,
rightParen,
done,
} State;
void* BinaryNode_toString(Node *node, Buffer *buffer) {
...
State state = leftParen;
while (true) {
switch (state) {
case error:
// error branch
return NULL;
case leftParen:
// action 0
state = Buffer_strcpy(buffer, "(", leftAction);
break;
case leftAction:
state = Node_toString(binaryNode->left, buffer, space);
break;
case space:
state = Buffer_strcpy(buffer, " ", rightAction);
break;
case rightAction:
state = Node_toString(binaryNode->right, buffer, rightParen);
break;
case rightParen:
state = Buffer_strcpy(buffer, ")", done);
break;
case done:
return buffer->data + offset;
}
}
}
State Buffer_strcpy(Buffer *buffer, const void *src, State nextState) {
size_t size = strlen(src) + 1;
if (buffer->offset + size > buffer->capacity) {
return error;
}
memcpy(buffer->data + buffer->offset, src, size);
buffer->offset += size - 1; // don't count null character
return nextState;
}
although personally I would just go with if checks with goto for error-handling, which is much more idiomatic in C:
void* BinaryNode_toString(Node *node, Buffer *buffer) {
...
if (!Buffer_strcpy(...)) goto fail;
if (!Node_toString(...)) goto fail;
if (!Buffer_strcpy(...)) goto fail;
...
fail:
// Unconditionally free any allocated resources.
...
}```
Related
i need to create a thread pool it,it works but in function do_work that the function pthread_create calls i have problem in the free (memory leak) just when calling pthread_exit()
*in function create threadpool i just initilaize the struct and call
function do work *
void* do_work(void* p)
{
threadpool* pool = (threadpool*)p;
work_t* work;
while(1)
{
pthread_mutex_lock(&pool->qlock);
if(pool->shutdown == 1)
{
pthread_mutex_unlock(&pool->qlock);
//pthread_exit(EXIT_SUCCESS);// here is the free problem when deleting it all good
return NULL;
}
while(!pool->qsize)
{
if(pthread_cond_wait(&pool->q_not_empty,&pool->qlock))
perror("pthread_cond_wait\n");
if(pool->shutdown)
break;
}
//Check if the system is shutting down
if(pool->shutdown == 1)
{
pthread_mutex_unlock(&pool->qlock);
//pthread_exit(EXIT_SUCCESS);y
return NULL;
}
work = pool->qhead; //set the cur variable.
pool->qsize--; //decriment the size.
if(pool->qsize == 0) {
pool->qhead = NULL;
pool->qtail = NULL;
}
else {
pool->qhead = work->next;
}
if(pool->qsize == 0 && ! pool->shutdown) {
//the q is empty again, now signal that its empty.
pthread_cond_signal(&(pool->q_empty));
}
pthread_mutex_unlock(&(pool->qlock));
(work->routine) (work->arg); //actually do work.
free(work);
}
}
The pattern looks like a fairly standard thread pool:
typedef struct {
pthread_mutex_t qlock;
pthread_cond_t q_not_empty;
volatile work_t *qhead;
volatile work_t *qtail;
size_t qsize; /* Not needed */
volatile int shutdown;
} threadpool;
Properly indenting OP's code would make it more readable.
However, the implementation looks odd. I would expect
void *do_work(void *poolptr)
{
threadpool *const pool = poolptr;
work_t *work;
pthread_mutex_lock(&(pool->qlock));
while (!(pool->shutdown)) {
if (!(pool->qhead)) {
/* Queue empty */
pthread_cond_wait(&(pool->q_not_empty), &(pool->qlock));
continue;
}
work = pool->qhead;
pool->qhead = work->next;
if (!pool->qhead)
pool->qtail = NULL;
work->next = NULL;
pthread_unlock(&(pool->qlock));
work->process(work);
pthread_lock(&(pool->qlock));
}
pthread_mutex_unlock(&(pool->qlock));
return (void *)0;
}
and the code that appends a new work item to the queue to be
void append(threadpool *pool, work_t *work)
{
work->next = NULL;
pthread_mutex_lock(&(pool->qlock));
if (pool->qtail) {
pool->qtail->next = work;
pool->qtail = work;
} else {
pool->qhead = work;
pool->qtail = work;
}
pthread_cond_signal(&(pool->q_not_empty));
pthread_mutex_unlock(&(pool->qlock));
}
It is difficult to say where OP's implementation leaks memory. The most likely candidate is OP's arg member in each work_t, if it is dynamically allocated.
My implementation above passes the entire work_t to the routine function, which I renamed to process. It is responsible for freeing the work structure, too. The minimal definition for the work structure is
typedef struct work_t {
struct work_t *next;
void (*process)(struct work_t *);
/* Optional other fields */
} work_t;
Other possible causes for the memory leaks is if the qsize member is not properly updated everywhere. Because there is no use for it, really, I just omit it altogether.
The simpler the code, the easier it is to avoid bugs.
i have a task in class to the return an array of struck Symbol from huffman tree.
the function getSL get a huffman tree(only) and return struck of Symbol.
each spot in the array contain a char from the "leaf" of the tree and the
length of his code(how many cross section till the leaf).
my main problem was to find how i advance the cnt of the arry that it will not overright the arry.
thank you.
typedef struct HNode {
char chr;
struct HNode *left, *right;
} HNode;
typedef struct {
char chr;
int counter;
}Symbol;
this is what i did till now.
Symbol * getSL(HNode *root) {
if (root->left == NULL && root->right == NULL) {
Symbol* b = (Symbol*)malloc(100);
b->counter=0;
b->chr = root->chr;
return b;
}
Symbol* a = (Symbol*)malloc(100);
if (root->left != NULL) {
a= getSL(root->left);
a->counter++;
}
if (root->right != NULL) {
a= getSL(root->right);
a->counter++;
}
return a;
}
Apart from the malloc problem (see the comments already), you have a fundamental problem: You allocate a new struct, but then replace it with the one returned from the recursive call. So you lose the one created before (actually, memory leaking!).
Easiest variant would now be converting your Symbol to linked list nodes; then you simply could do:
Symbol* lastLeafFound; // probaly a function parameter!
if(!(root->left || root->right))
{
// leaf found:
Symbol* a = (Symbol*)malloc(sizeof(Symbol));
a->chr = root->chr;
a->counter = /* ... */;
a->next = NULL;
lastLeafFound->next = a;
// you might return a now as last leaf found, using it in the next recursive call
}
Sure, above code is incomplete, but should give you the idea...
If you cannot modify your struct, then you need to create an array and pass it on to every new recursive call (prefer not to use global variables instead):
void doGetSL
(
HNode* root,
Symbol** symbols, // your array to be used
unsigned int* count, // number of symbols contained so far
unsigned int* capacity // maximum possible symbols
)
Passing all data as pointers allows the function to modify them as needed and they are still available from outside...
Symbol* getSL(HNode* root)
{
if(!root)
return NULL;
unsigned int count = 0;
unsigned int capacity = 128;
// allocate a whole array:
Symbol* array = malloc(capacity*sizeof(Symbol));
if(array) // malloc could fail...
{
doGetSL(root, &array, &count, &capacity);
// as you cannot return the number of leaves together with
// the array itself, you will need a sentinel:
array[count].chr = 0;
// obvious enough, I'd say, alternatively you could
// set counter to 0 or -1 (or set both chr and counter)
}
return array;
}
doGetSL will now use above set up "infrastructure":
{
if(!(root->left || root->right))
{
if(*count == *capacity)
{
// no memory left -> we need a larger array!
// store in separate variables:
unsigned int c = *capacity * 2;
Symbol* s = realloc(symbols, c * sizeof(Symbol));
// now we can check, if reallocation was successful
// (on failure, s will be NULL!!!):
if(s)
{
// OK, we can use them...
*symbols = s; // <- need a pointer for (pointer to pointer)!
*capacity = c;
}
else
{
// re-allocation failed!
// -> need appropriate error handling!
}
}
(*symbols)[count].chr = root->chr;
(*symbols)[count].counter = /*...*/;
++*count;
}
else
{
if(root->left)
{
doGetSL(root->left, symbols, count, capacity);
}
if(root->right)
{
doGetSL(root->right, symbols, count, capacity);
}
}
}
One thing yet omitted: setting the counter. That would be quite easy: add another parameter to doGetSL indicating the current depth, which you increment right when entering doGetSL, you can then just assign this value when needed.
You can further improve above variant (especially readability), if you introduce a new struct:
struct SLData
{
Symbol* symbols, // your array to be used
unsigned int count, // number of symbols contained so far
unsigned int capacity // maximum possible symbols
};
and pass this one instead of the three pointers:
doGetSL(HNode*, struct SLData*, unsigned int depth);
struct SLData data =
{
.count = 0;
.capacity = 128;
.array = malloc(capacity*sizeof(Symbol));
};
if(data.array)
doGetSL(root, &data, 0); // again passed as pointer!
I am getting a really strange error in my C program and therefore I need your help guys! So I have a recursive structure called path, where sometimes I store the address of the "parent" path in the structure field mother:
typedef struct path{
struct path* mother;
struct path** children;
int length;
uint8_t* inf;
} path;
So in my example I just generate one path like this:
int child_num=2;
int bytes=10;
path* my_path=malloc(sizeof(path));
if (path==NULL) throw error...
my_path->inf=malloc(sizeof(uint8_t)*bytes);
memset(my_path->inf, 4, bytes);
my_path->children=malloc(sizeof(path*)*child_num);
for(int i=0; i<child_num; i++){
my_path->children[i]->mother=my_path;
my_path->children[i]->inf=malloc(sizeof(uint8_t)*bytes);
memset(my_path->children[i]->inf, 5, bytes);
}
So now since I stored the link to the parent structure, I want to use another helping pointer to get access to its information:
path* my_pointer=my_path->children[0]->mother; //this is just for the example
So i checked the addresses and everything seems to be alright, but if I know use the pointer in another method, pointing to the field "inf", it works if I use the variable "path" so:
method(path->inf, bytes);
it is fine, but as soon as I do:
method(my_pointer->inf, bytes);
the method crashes at the marked line:
void method(uint8_t* element, int bytes) {
if (element==NULL) ... //<=== here it crashes
//do something
}
I really dont get what I am doing wrong, I printed the addresses and everything seems to be good, even if I access a certain byte over the variable "my_pointer", so like
my_pointer->inf[1]
it returns me the corresponding value, but in the separate method it doesnt work.
Like the comments indicate we can't exactly answer your question with the information provided, but we can point you in the right direction.
First, I noticed in your examples that you're using path as a variable name to a typedef'd path structure. You need to either be more verbose with your variable names or actually copy paste some code to make sure that we can look at the actual problem, because it could simply be an issue with naming.
All in all I think it would do you a world of good to employ a bit of code hygiene. Organize some of the functions you use for data structure overhead at file scope:
static int path_alloc(path* p);
static int path_alloc_kids(path* p, int num);
static int path_alloc(path* p) {
if(p == NULL) { return -1; }
p = (path*)malloc(sizeof(path));
if(p == NULL) { return -2; }
return 0;
}
static int path_alloc_kids(path* p, int num) {
if(p == NULL || num <= 0) { return -1; }
if(!path_alloc(p)) { /* Easier to read and understand, no error handling here to muddle things up */
/* You don't actually need a path**, do you? Think of char *argv[] a.k.a. char **argv, is that what you're actually going for? */
p->children = (path*)malloc(sizeof(path) * num);
if(p->children == NULL) { return -2; }
p->length = num;
} else { return -1; } /* Simple */
return 0;
}
This makes it a LOT easier to understand your code, which is the main issue with pointers. Add in some methods to free the allocated children and roots and you're set to use this path structure in a relatively abstracted way. You may want to consider using a path and a path_node in a linked-list fashion, that way you only allocate what you need.
struct spath_node; /* So it knows of itself */
typedef struct spath_node {
struct spath_node *parent;
struct spath_node *next;
uint8_t *data;
int data_size;
} path_node;
Then allocate by passing in a data size and parent, a NULL parent could mean it's a root node.
static int path_alloc_node(path_node *parent, int data_size, uint8_t *data);
This makes for relatively slow insert/traversal, but easier to understand where you went wrong.
EDIT: To be clear, this is how we would add children to the linked-list example:
static int path_alloc_node(path_node *parent, int data_size, uint8_t *data) {
path_node *tmp;
if(parent == NULL || data_size <= 0) { return -1; }
if(parent->next != NULL) { return -3; }
tmp = (path_node*)malloc(sizeof(path_node));
if(tmp == NULL) { return -2; }
else parent->next = tmp;
if(data == NULL) { /* Assume the caller is requesting a new data block of the given size */
data = (uint8_t*)malloc((size_t)data_size);
if(data == NULL) { return -2; }
}
parent->next->data = data;
parent->next->data_size = data_size;
parent->next->next = NULL;
parent->next->parent = parent;
return 0;
}
I'm back, and it appears I still haven't quite figured out memory management in C. While trying to design an event queue I decided to build a circular buffer as an exercise. After a bit of research I am trying to model it after the following implementation I saw here on SO: https://stackoverflow.com/a/827749 .
I decided to simplify it a bit, instead of building a buffer for any data type I wanted to build one just for integers. For the most part I was able to follow the logic, although I do not understand how come the author is occasionally casting values to (char *). I've tried my program with and without the (char *) casts but it yields the same incorrect output in both cases.
Once I write past a certain point, values that should not change in my buffer are being affected, which I assume has something to do with the way I've allocated memory for the buffer. It looks like the program is writing past the buffer I've allocated in my struct, and is overwriting values that should otherwise be static. I can't for the life of me figure out how to fix the error, but I have a sneaking suspicion it's something very obvious I've managed to overlook.
Here is my code:
typedef struct Circular_buffer
{
void *buffer;
void *buffer_end;
size_t capacity; // The maximum number of items allowed in buffer
size_t count; // Current number of items in buffer
size_t item_size; //Size of each item;
void *head;
void *tail;
} Circular_buffer;
int main(void)
{
int i;
Circular_buffer my_buffer;
c_buff_init( &my_buffer, 10 );
printf("Buffer Capacity: %d\n", my_buffer.capacity);
for (i = 0; i < 7; i++) {
c_buff_write( &my_buffer, i);
}
printf("Capacity: %d Count: %d\n", my_buffer.capacity, (int)my_buffer.count);
cleanup_c_buff( my_buffer );
return 0;
}
void *c_buff_init( Circular_buffer *buffer, int length )
{
buffer->item_size = sizeof(int);
buffer->buffer = malloc( length * buffer->item_size );
buffer->buffer_end = buffer->buffer + buffer->capacity * buffer->item_size;
buffer->capacity = length;
buffer->count = 0;
buffer->head = buffer;
buffer->tail = buffer;
}
void c_buff_write( Circular_buffer *buffer, const int data)
{
if (buffer->count == buffer->capacity) {
printf( "Your buffer is full\n" );
exit(0);
}
printf("Buffer Capacity: %d Buffer Count %d\n", buffer->capacity, buffer->count);
memcpy( buffer->head, &data, buffer->item_size); // memcpy args = (dest, src, size)
buffer->head = (char*)buffer->head + buffer->item_size;
if (buffer->head == buffer->buffer_end) // If head has reached end of buffer
buffer->head = buffer->buffer; // Set head to start of buffer
buffer->count++;
}
When this program runs, it produces the expected output up until it tries to add the fifth element (heh), where it seems to all of a sudden write over my capacity value.
What gives?
You're using the same name, buffer, for two different things. One is for the circular buffer, and the other is for the storage that you create via malloc. Look at where you're setting buffer->head and buffer->tail. You're setting them to the structure itself, so you're going to overwrite it. You need to set them to the storage that you create via malloc.
I'm using Visual c++.
I'm trying to implement a circular buffer, this CB must handle a specific type of data...in fact, it's a structure data where we have some kind of raw data to be stored in a char type and a date associated to that data...this has been implemented using a strucuture.
here is the code for more details:
#include <stdio.h>
#include <time.h>
#include <windows.h>
//data=date_label+raw_data
typedef struct DataFragment
{
char data[4];
clock_t date;
}DataFragment;
typedef struct CircularBuffer
{
DataFragment *buffer; // data buffer
DataFragment *buffer_end; // end of data buffer
size_t capacity; // maximum number of items in the buffer
size_t count; // number of items in the buffer
size_t sz; // size of each item in the buffer
DataFragment *head; // pointer to head
DataFragment *tail; // pointer to tail
} CircularBuffer;
void cb_init(struct CircularBuffer *cb, size_t capacity, size_t sz)
{
if((cb->buffer = (DataFragment*) malloc(capacity * sz))!=NULL)
puts("success alocation");
//if(cb->buffer == NULL)
//handle error
cb->buffer_end = (DataFragment *)cb->buffer + (capacity-1)*sz;
cb->capacity = capacity;
cb->count = 0;
cb->sz = sz;
cb->head = cb->buffer;
cb->tail = cb->buffer;
}
void cb_free(struct CircularBuffer *cb)
{
free(cb->buffer);
// clear out other fields too, just to be safe
}
void cb_push_back(struct CircularBuffer *cb, const DataFragment *item)
{
//if(cb->count == cb->capacity)
//handle error when it's full
memcpy(cb->head->data, item->data,4);
cb->head->date=item->date;
cb->head = (DataFragment*)cb->head + cb->sz;
if(cb->head == cb->buffer_end)
cb->head = cb->buffer;
cb->count++;
}
void cb_pop_front(struct CircularBuffer *cb, DataFragment *item)
{
//if(cb->count == 0)
//handle error
memcpy(item->data, cb->tail->data,4);
item->date=cb->tail->date;
cb->tail = (DataFragment*)cb->tail + cb->sz;
if(cb->tail == cb->buffer_end)
cb->tail = cb->buffer;
cb->count--;
}
int main(int argc, char *argv[])
{
struct CircularBuffer pbuf;
pbuf.buffer=NULL;
pbuf.buffer_end=NULL;
pbuf.capacity=0;
pbuf.count=0;
pbuf.head=NULL;
pbuf.sz=0;
pbuf.tail=NULL;
struct CircularBuffer *buf= &pbuf;
size_t sizz = sizeof(DataFragment);
//initialisation of the circlar buffer to a total bytes
//of capacity*sizz=100*sizeof(struct DataFragment)
cb_init(buf,100,sizz);
//temporary container of data
DataFragment temp,temp2;
for(int i=0;i<4;i++)
temp.data[i]='k';
for(int i=0;i<4;i++)
temp2.data[i]='o';
//pushing temporary buffer to the CB...40*2=80<capacity of the CB
for(int i=0;i<40;i++)
{
Sleep(20);
temp.date=clock();
cb_push_back(buf,&temp);
Sleep(10);
temp2.date=clock();
cb_push_back(buf,&temp2);
}
DataFragment temp3;
for(int i=0;i<20;i++)
{
cb_pop_front(buf,&temp3);
printf("%d\n", temp3.data); //print integers....no need of end caracter
}
cb_free(buf);
return 0;
}
When I compile the code, everything is fine, but when I debug, I noticed a problem with the buffer_end pointer, it says bad_pointer....this happens if the capacity is greater than 56...I don't know why the pointer can't point to end of the buffer.But if the capacity is less than 56 the pointer points exactly on the end of the buffer
If anyone knows why this happens like this, and how to fix it, please help me..
thanks in advance
It seems you are misunderstanding pointer arithmetic
cb->buffer_end = (DataFragment *)cb->buffer + (capacity-1)*sz;
cb->head = (DataFragment*)cb->head + cb->sz;
cb->tail = (DataFragment*)cb->tail + cb->sz;
Pointer arithmetic already takes into account the size of the underlying type. All you really need is
++cb->head;
++cb->tail;
If the idea is to hack around sizeof(DataFragment) - perhaps to allocate more storage for one item than the struct's size - for some evil purpose - you'll need to first cast the pointer to a char* (because sizeof(char) == 1).
cb->tail = (DataFragment*)((char*)cb->tail + cb->sz);
Design-wise the struct appears to have too many members: buffer_end and capacity duplicate each other (given one you can always find the other), and the sz member is not necessary (it should always be sizeof(DataFragment).
Also, I believe you can just assign structs
*(cb->head) = *item;
there seem to be completely unnecessary casts (probably resulting from the misunderstanding of pointer arithmetic):
cb->buffer_end = (DataFragment *)cb->buffer + (capacity-1)*sz;
And if it is supposed to be C++, then it contains lots of "C-isms" (typedeffing structs, using struct XXX var; - despite having it typedeffed, etc), and the code is generally designed in a purely C style (not taking advantage of C++'s greatest strength, automatic resource management with RAII).
May I also point out that clock() hardly gives you a date :)
I think you need to remove the * sz. (And I don't think you need the cast.)
cb->buffer_end = cb->buffer + (capacity-1);
Arithmetic on pointers automatically accounts for the size of the type pointed to.
I should also point out boost::circular_buffer.
you are assuming that pointers are 4 byte wide. This may not be the case on all platforms (x86_64). Hence, the memcpy()'s should make use of the sizeof operator.
There seems to be another bug with "end = buffer + (capacity - 1 ) * size. In conjunction with cb_push_back() you are allocating one element too much (or you are not using the last element of the ringbuffer). cb_count gets increased in every push_back too, so your buffer can have more "counts" than elements.
If you are going to code in C++, at least use STL. Try std::list