twalk without globals - c

I am trying to traverse a binary tree using twalk() with <search.h>
#define _GNU_SOURCE /* Expose declaration of tdestroy() */
#include <search.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
void *root = NULL;
void *
xmalloc(unsigned n)
{
void *p;
p = malloc(n);
if (p)
return p;
fprintf(stderr, "insufficient memory\n");
exit(EXIT_FAILURE);
}
int
compare(const void *pa, const void *pb)
{
if (*(int *) pa < *(int *) pb)
return -1;
if (*(int *) pa > *(int *) pb)
return 1;
return 0;
}
void
action(const void *nodep, const VISIT which, const int depth)
{
int *datap;
switch (which) {
case preorder:
break;
case postorder:
datap = *(int **) nodep;
printf("%6d\n", *datap);
break;
case endorder:
break;
case leaf:
datap = *(int **) nodep;
printf("%6d\n", *datap);
break;
}
}
int
main(void)
{
int i, *ptr;
void *val;
srand(time(NULL));
for (i = 0; i < 12; i++) {
ptr = (int *) xmalloc(sizeof(int));
*ptr = rand() & 0xff;
val = tsearch((void *) ptr, &root, compare);
if (val == NULL)
exit(EXIT_FAILURE);
else if ((*(int **) val) != ptr)
free(ptr);
}
twalk(root, action);
tdestroy(root, free);
exit(EXIT_SUCCESS);
}
As you can see there is no way to pass to or return any variable from action().
why is so hermetic? I can't use any global because the program uses threads, my question is: how can I traverse (and share nodep with non-global variable) in thread-safe mode?
Excuse my poor english
EDIT:
As unwind said, the solution is to re-invent this particular wheel, redefine the structure used at tsearch.c solves the problem:
/* twalk() fake */
struct node_t
{
const void *key;
struct node_t *left;
struct node_t *right;
unsigned int red:1;
};
static void tmycallback(const xdata *data, const void *misc)
{
printf("%s %s\n", (const char *)misc, data->value);
}
static void tmywalk(const struct node_t *root, void (*callback)(const xdata *, const void *), const void *misc)
{
if (root->left == NULL && root->right == NULL) {
callback(*(xdata * const *)root, misc);
} else {
if (root->left != NULL) tmywalk(root->left, callback, misc);
callback(*(xdata * const *)root, misc);
if (root->right != NULL) tmywalk(root->right, callback, misc);
}
}
/* END twalk() fake */
if (root) tmywalk(root, tmycallback, "Hello walker");

I guess nobody can answer the "why" exactly, except those who specified and implemented the functions. I guess "shortsightedness", or maybe "historical reasons" (they did it before thread programming became a common thing).
Anyway, this API seems a bit "toyish" to me due to this limitation, as do in fact all APIs that fail to include a user-owned void * that is just opaquely passed around between API and any callbacks.
So, the solution I guess is to re-invent this particular wheel, and write your own functions to traverse a binary tree.

You can use thread-local storage to be able to use a global variable and still be thread-safe. Apparently you can use the __thread keyword for this purpose. Also check Using __thread in c99.

Related

Manipulation of stack with full usage of pointers and arrays

I was given this code from Uni and I am trying to do operations with it , the push function ask for two pointers and I could not figure out how can I pass the data to the item part.I have tried using another structure containing the data but I failed. I am stuck at the end of the code and I would like to learn how to push data using this code. How can I proceed ?
It would be preferable if the data itself was stored in a structur.
Thanks in advance.
typedef struct stk
{
struct stk* elems[MAX]; int top;
} stack, *stackptr;
void Init(stack* s)
{
s->top = 0;
}
int IsEmpty(stack s)
{
return (s.top == 0);
}
void Push(struct stk* item, stack* s)
{
if (s->top == MAX)
printf("Stack voll!");
s->elems[s->top] = item;
s->top++;
}
struct stk* Pop(stack* s)
{
if (IsEmpty(*s)) return NULL;
s->top--;
return s->elems[s->top];
}
int main()
{
stack* ptr = (stackptr)malloc(sizeof(stack));
Init(ptr);
printf("%d\n", ptr->top); // Ist 0 , OK
}
Here is a working stack implementation that store ints. This will afford you the opportunity to test that the operations work as expected. If you really want to store stack * replace the type. It seems unnecessarily confusing for a entry level class to have an assignment of storing pointers to the same thing you are building.
When you deal with pointers you want to make sure the object they point to outline the pointer. You may also want to think of shallow and deep copies with pointers. If you Pop followed by a Push the pointer that was returned from Pop now will point to the new value which would be surprising. Consider a different designs:
Pass in a reference to a variable (aka out parameter) so Pop(stack *s, *v) (and use an enum or define constants for error values).
return a value instead of a pointer; error would not be an out parameter.
return a pointer to a copy of the value and require client to free it.
#include <stdio.h>
#include <stdlib.h>
#define MAX 10
typedef struct stack {
int elems[MAX];
int top;
} stack;
void Init(stack *s) {
if(!s)
return;
s->top = 0;
}
int IsEmpty(stack *s) {
return (s->top == 0);
}
void Push(stack *s, int elem) {
if (s->top == MAX) {
printf("Stack voll!");
return;
}
s->elems[s->top++] = elem;
}
int *Pop(stack *s) {
if (IsEmpty(s))
return NULL;
return &s->elems[--(s->top)];
}
int main() {
stack *s = malloc(sizeof *s);
Init(s);
printf("%d\n", s->top); // Ist 0 , OK
Push(s, 42);
int *v = Pop(s);
printf("%d\n", *v);
}
and example run:
0
42
Consider using a name prefix like "Stack" for all your symbols to avoid name conflicts.
In c we don't cast void * (from malloc()).

How to remove all nodes from a POSIX binary (tsearch) tree?

The only way I see to access all nodes (if the keys are not known) is twalk. Is it allowed to use tdelete inside twalk? If not--how delete all nodes? (I don't wan't to use the non-portable GNU extension tdestroy.)
No, you don't need to use twalk, you can use tdelete with a comparison function (the same function used to insert), tdelete changes the root node, so passing and deleting while (root != NULL) will do the trick, something like:
typedef struct {
int key;
char value[50];
} t_data;
static int comp(const void *pa, const void *pb)
{
const t_data *a = pa, *b = pb;
if (a->key > b->key) return +1;
if (a->key < b->key) return -1;
return 0;
}
int main(void)
{
void *root = NULL;
t_data *data;
...
while (root != NULL) {
data = *(t_data **)root;
tdelete(data, &root, comp);
free(data);
}
...

Complex generic stack

I have been assigned to program a generic stack in ANSI C. It is meant to be for primitive datatypes. Until here there was no big problem whatsoever.
Afterwards I was asked to reprogram my application so that even complex data types can be used on my stack. I have searched and researched for the last week and I found nothing that could be helpful enough.
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <stddef.h>
#include "genstacklib.h"
void (*freefn) (void*);
/*
* ToDo
*/
void GenStackNew(genStack *s, int elemSize, void (*freefunk) (void*))
{
s->elems = malloc (elemSize * GenStackInitialAllocationSize);
freefn = freefunk;
assert (s->elems != NULL);
s->elemSize = elemSize;
s->logLength = 0;
s->allocLength = GenStackInitialAllocationSize;
}
/*
* ULStackPush adds an element to the stack and allocates new memory if
* needed. If there is not enough memory, ULStackPush does nothing.
*/
void GenStackPush (genStack *s, const void *elemAddr)
{
/*assert (sizeof(*elemAddr) == s->elemSize);*/
assert (s->elems != NULL);
if (s->logLength == s->allocLength)
{
void *temp = NULL;
temp = realloc (s->elems, 2 * s->allocLength * s->elemSize);
assert (temp != NULL);
s->allocLength = 2 * s->allocLength;
s->elems = temp;
}
memcpy(currentval(s), elemAddr, s->elemSize);
s->logLength = s->logLength + 1;
}
void GenStackPop (genStack *s, const void *elemAddr)
{
assert (s->elems != NULL);
assert (s->logLength != 0);
(s->logLength)--;
memcpy((void *)elemAddr, currentval(s), s->elemSize);
}
void *currentval(genStack *s)
{
assert (s->elems != NULL);
return ((size_t*)s->elems + s->logLength * s->elemSize);
}
bool GenStackEmpty (const genStack *s)
{
assert (s->elems != NULL);
return s->logLength == 0;
}
void GenStackDispose (genStack *s)
{
assert (s->elems != NULL);
s->logLength = 0;
free (s->elems);
freefn();
}
/*
* ToDO
*/
void *freefn (void *) {
free
And my header data is:
#ifndef GENSTACKLIB_H
#define GENSTACKLIB_H
#include <stdbool.h>
#define GenStackInitialAllocationSize 4
typedef struct
{
void *elems;
int elemSize;
int logLength;
int allocLength;
} genStack;
void GenStackNew (genStack * s, int elemSize);
bool GenStackEmpty (const genStack * s);
void GenStackPush (genStack * s, const void *elemAddr);
void GenStackPop (genStack * s, const void *elemAddr);
void GenStackDispose (genStack * s);
void *currentval(genStack *s);
#endif
In the first block of code, I believe that what has to be done is in the ToDo markings.
How can I make it to use my stack for complex data types?
Thanks in advance
I dont see any problem with "complex" types like strings... there is no real difference bewteen pointer to string and pointer to int. So just store pointers (or pointers to pointers) and that should work.
So instead of element to be "int".. element is pointer to pointer.
Basic idea in form of very "pseudo" C code
typedef struct Wrapper
{
void * primitiveData;
} Wrapper;
void PrimitivePush(void * data)
{
Wrapper * w = malloc();
w->primitiveData = malloc();
memcpy(w->primitiveData, data);
ClassicComplexTypePush(&w)
}
ClassicComplexTypePush(void ** data)
{
push data to stack
}
Consider using a singularly linked list for implementation, since when
using a stack, we don't know how many items may be needed.
Use a byte* or (char*) to store the contents of memory, instead of a void* (which would also work, but we may need to pad the allocation, to include structs)
Copy memory into a new allocation, which is pushed onto the stack,
then delete that used upon pop.
each node has to be of the same type, or at-least the same size,
errors using wrong type though may be undesired
pop can be either used to check if the stack is empty by passing (NULL)
or to actually pop the stack, by referencing the memory you want to set.
typedef unsigned char byte;
Create the structures which will be used to keep track of the stack
struct gStackNode {
byte *data;
struct gStackNode *next;
};
struct gStack {
unsigned size;
struct gStackNode *head;
};
Initialize the stack, including the size of the type we will be using
void stack_initalize(struct gStack *stk, unsigned size) {
if (!stk)
return;
stk->size = size;
stk->head = (void*)0;
}
Always, we need to manually free the stack, in-case not all were popped
void stack_free(struct gStack *stk) {
if (!stk)
return;
struct gStackNode *temp;
/* step through the remaining stack, deleting each item */
while(stk->head) {
temp = stk->head->next;
free((byte*)stk->head->data);
free((struct gStackNode *)stk->head);
stk->head = temp;
}
}
push an item onto the stack
void stack_push(struct gStack *stk, void *data) {
struct gStackNode *node = (struct gStackNode*)malloc(sizeof(struct gStackNode));
struct gStackNode *temp = stk->head;
node->next = temp;
node->data = (byte*)malloc(sizeof(byte)*(stk->size));
byte * src = (char*)(data);
byte * dest = (char*)(node->data);
unsigned n = stk->size;
/* fill the new allocation with source data */
for(;n;n--)
*(dest++) = *(src++);
/* the node becomes the new head */
stk->head = node;
}
Sometimes we don't want to use a local variable ie: stack_pop_(stack, &type) we can use stack_push_arg_no_ref(stack, 10).
void stack_push_arg_no_ref(struct gStack *stk, void *data) {
stack_push(stk, &data);
}
Now we can pop, and use the same to peek, passing (NULL) to data will result in a peek,
returning (1) if there is an item in the stack, and a (0) if its empty
int stack_pop(struct gStack *stk, void * data) {
if (!stk)
return 0;
if (!stk->head)
return 0;
if (data == (void*)0) {
/*
simply check to see if the stack is empty or not
don't actually pop the stack
*/
return ((!stk->head == (void*)0));
} else {
struct gStackNode *next = stk->head->next;
struct gStackNode *node = stk->head;
unsigned i;
byte *c_temp = (byte*)data;
for(i=0;i<stk->size;i++)
*c_temp++ = node->data[i];
free((byte*)node->data);
free((struct gStackNode*)node);
stk->head = next;
}
}
Finally we can implement the stack
using any ANSI C data types
the size of a character string needs to be fixed
structs can also be used
Using a character string
CAUTION, for this example, the strings need to be NULL terminated, though
it is possible to use non-NULL terminated strings
char ta[32] = "ta: text 1";
char tb[32] = "tb: text 2";
char tc[32];
struct gStack stack_char; stack_initalize(&stack_char, sizeof(ta));
stack_push(&stack_char, ta);
stack_push(&stack_char, tb);
while (stack_pop(&stack_char, &tc))
printf("%s\n", tc);
be sure to free the stack
stack_free(&stack_char);
Using integers
int a = 120, b = -32, c;
struct gStack stack_int; stack_initalize(&stack_int, sizeof(int));
stack_push(&stack_int, &a);
stack_push(&stack_int, &b);
/* or we can use */
stack_push_arg_no_ref(&stack_int, 1776);
/* we can now see the contents of the stack */
while (stack_pop(&stack_int, &c))
printf("%d\n", c);
stack_free(&stack_int);

Re-declaring struct

I'm trying to avoid globals using twalk() in search.h
As you can see twalk callback a function but fails to include a void * param
/* Walk the nodes of a tree */
void
twalk(const void *vroot, void (*action)(const void *, VISIT, int))
{
node *root = (node *)vroot;
if (root != (node *)0 && action != (void (*)(const void *, VISIT, int))0)
trecurse(root, action, 0);
}
void
action(const void *nodep, const VISIT which, const int depth)
{
int *datap;
switch (which) {
case preorder:
break;
case postorder:
datap = *(int **) nodep;
printf("%6d\n", *datap);
break;
case endorder:
break;
case leaf:
datap = *(int **) nodep;
printf("%6d\n", *datap);
break;
}
}
What is the behaviour of re-declare the same struct (node_t in tsearch.c) with the same name in my own files?
/* twalk() fake */
struct node_t
{
const void *key;
struct node_t *left;
struct node_t *right;
unsigned int red:1;
};
static void tmycallback(const xdata *data, const void *misc)
{
printf("%s %s\n", (const char *)misc, data->value);
}
static void tmywalk(const struct node_t *root, void (*callback)(const xdata *, const void *), const void *misc)
{
if (root->left == NULL && root->right == NULL) {
callback(*(xdata * const *)root, misc);
} else {
if (root->left != NULL) tmywalk(root->left, callback, misc);
callback(*(xdata * const *)root, misc);
if (root->right != NULL) tmywalk(root->right, callback, misc);
}
}
/* END twalk() fake */
if (root) tmywalk(root, tmycallback, "Hello walker");
What is the behaviour of re-declare the same struct (node_t in
tsearch.c) with the same name in my own files?
Though there appears to be no mention of it in the C11 standard, many compilers will issue you a constraint violation diagnostic (error message) if you try to declare a struct twice in the same translation unit. It'd be a good idea to put the struct node_t declaration into it's own header file (perhaps tree_node.h) and use include guards to prevent duplicate declarations.

What is the best way to write class template-like generic code in C?

I need to write AVL-tree with generic type in C. The best way I know is to use [ void* ] and to write some functions for creating, copying, assignment and destruction. Please, tell me some better way.
I will give you an example on how you can achieve generics functionality in C. The example is on a linked list, but I am sure you can adapt it on your AVL tree if necessary.
First of all you will need to define a structure for list element. A possible (most simple implementation):
struct list_element_s {
void *data;
struct list_element_s *next;
};
typedef struct list_element_s list_element;
Where 'data' will act as the "container" where you are going to keep your information, and 'next' is the reference to the direct linked element. (NOTE: Your binary tree element should include a reference to the right / left children elements).
After you create you element structure, you will need to create your list structure. A good practice is to have some members that are pointing to functions: destructor (needed to free the memory being hold by 'data'), and comparator (to be able to compare two of your list elements).
A list structure implementation could look like this:
struct list_s {
void (*destructor)(void *data);
int (*cmp)(const void *e1, const void *e2);
unsigned int size;
list_element *head;
list_element *tail;
};
typedef struct list_s list;
After you design your data structure, you should design your data structure interface. Let's say our list will have the following, most simple, interface:
nmlist *list_alloc(void (*destructor)(void *data));
int list_free(list *l);
int list_insert_next(list *l, list_element *element, const void *data);
void *list_remove_next(list *l, list_element *element);
Where:
list_alloc : will alocate memory for your list.
list_free : will free memory allocated for list, and all 'data' being held by list_element(s).
list_insert_next : will insert a new element next to 'element' . If 'element' is NULL, the insertion will be made at the head of the list.
list_remove_next : will remove & return (void*)'data' being held by 'element->next' . If 'element' is NULL, it will perform "list->head removal".
And now the functions implementation:
list *list_alloc(void (*destructor)(void *data))
{
list *l = NULL;
if ((l = calloc(1,sizeof(*l))) != NULL) {
l->size = 0;
l->destructor = destructor;
l->head = NULL;
l->tail = NULL;
}
return l;
}
int list_free(list *l)
{
void *data;
if(l == NULL || l->destructor == NULL){
return (-1);
}
while(l->size>0){
if((data = list_remove_next(l, NULL)) != NULL){
list->destructor(data);
}
}
free(l);
return (0);
}
int list_insert_next(list *l, list_element *element, const void *data)
{
list_element *new_e = NULL;
new_e = calloc(1, sizeof(*new_e));
if (l == NULL || new_e == NULL) {
return (-1);
}
new_e->data = (void*) data;
new_e->next = NULL;
if (element == NULL) {
if (l->size == 0) {
l->tail = new_e;
}
new_e->next = l->head;
l->head = new_e;
} else {
if (element->next == NULL) {
l->tail = new_e;
}
new_e->next = element->next;
element->next = new_e;
}
l->size++;
return (0);
}
void *list_remove_next(list *l, list_element *element)
{
void *data = NULL;
list_element *old_e = NULL;
if (l == NULL || l->size == 0) {
return NULL;
}
if (element == NULL) {
data = l->head->data;
old_e = l->head;
l->head = l->head->next;
if (l->size == 1) {
l->tail = NULL;
}
} else {
if (element->next == NULL) {
return NULL;
}
data = element->next->data;
old_e = element->next;
element->next = old_e->next;
if (element->next == NULL) {
l->tail = element;
}
}
free(old_e);
l->size--;
return data;
}
And now, how to use your simple generic linked list implementation. In the following example the list is acting like a stack:
#include <stdlib.h>
#include <stdio.h>
#include "nmlist.h"
void simple_free(void *data){
free(data);
}
int main(int argc, char *argv[]){
list *l = NULL;
int i, *j;
l = list_alloc(simple_free);
for(i = 0; i < 10; i++){
j = calloc(1, sizeof(*j));
if(j != NULL){
*j = i;
list_insert_next(l, NULL, (void*) j);
}
}
for(i = 0; i < 10; i++){
j = (int*) list_remove_next(l, NULL);
if(j != NULL){
printf("%d \n", *j);
}
}
list_free(l);
return (0);
}
Note that instead of "int *j" you can use a pointer that references more complex structures. If you do, don't forget to modify your 'list->destructor' function accordingly.
What Alex said. In c, void * is what there is.
Assuming you must work in C, though... Why do you need to provide the create/copy/assignment/destruction functions to the library? Which features of this library require the AVL-tree code to use those operations?
The major operations on a search tree are insert, delete and lookup, correct? You will need to provide a comparison function for all of those operations, but you should let the clients of this library handle all of the other operations. Simple is probably better in this case.
To do true, performant generics in C, you hack with the preprocessor. This approach has many of the same disadvantages of the C++ template approach; namely that all (most, anyway) code must live in header files, and debugging and testing are a pain. The advantages are also there; that you can get superior performance and let the compiler do all sorts of inlining to speed things up, minimize allocations by reducing indirection, and a modicum of type safety.
The definition looks like (let's imagine we have a hash set)
int my_int_set(int x);
#define HASH_SET_CONTAINED_TYPE int
#define HASH_SET_TYPE my_int_set
#define HASH_SET_FUNC hash_int
#include "hash_set.h"
And then to use it, you simply use the type you created above:
my_int_set x;
my_int_set_init(&x);
my_int_set_add(&x, 7);
if (my_int_set_check(&x, 7)) printf("It worked!\n");
...
// or, if you prefer
my_int_set *x = my_int_set_create();
Internally, this is implemented by a whole bunch of token pasting, etc., and (as noted above) is a huge pain to test.
So something like:
#ifndef HASH_SET_CONTAINED_TYPE
#error Must define HASH_SET_CONTAINED_TYPE
#endif
... /// more parameter checking
#define HASH_SET_ENTRY_TYPE HASH_SET_TYPE ## _entry
typedef struct HASH_SET_ENTRY_TYPE ## _tag {
HASH_SET_CONTAINED_TYPE key;
bool present;
} HASH_SET_ENTRY_TYPE;
typedef struct HASH_SET_TYPE ## _tag {
HASH_SET_TYPE ## _entry data[];
size_t elements;
} HASH_SET_TYPE;
void HASH_SET_TYPE ## _add(HASH_SET_CONTAINED_TYPE value) {
...
}
...
#undef HASH_SET_CONTAINED_TYPE
... // remaining uninitialization
You can even add options; like #define HASH_SET_VALUE_TYPE or #define HASH_SET_DEBUG.

Resources