Renato Tavares Renato Tavares - 3 months ago 43
C Question

Creating a queue with structs in C

I have a code (at the end of this post) that implements a circular queue system. Everything works perfectly, but as can be seen in the function

createQueue
the queue is implemented only for integers. I would like to modify this code to accept a struct informed by the user.

I could create a known struct and replace all sites with integer, but this way I would be coupling the code to a known struct. Bad idea...

How could pass to the function
createQueue
a struct for memory allocation without needing to know the struct previously? The struct Queue should also be changed, in int *elements the value should be changed from integer to void?

#include <stdio.h>
#include <stdlib.h>

typedef struct Queue
{
int capacity;
int size;
int front;
int rear;
int *elements;
}Queue;

Queue * createQueue(int maxElements)
{
/* Create a Queue */
Queue *Q;
Q = (Queue *)malloc(sizeof(Queue));
/* Initialise its properties */
Q->elements = (int *)malloc(sizeof(int)*maxElements);
Q->size = 0;
Q->capacity = maxElements;
Q->front = 0;
Q->rear = -1;
/* Return the pointer */
return Q;
}
void Dequeue(Queue *Q)
{
/* If Queue size is zero then it is empty. So we cannot pop */
if(Q->size==0)
{
printf("Queue is Empty\n");
return;
}
/* Removing an element is equivalent to incrementing index of front by one */
else
{
Q->size--;
Q->front++;
/* As we fill elements in circular fashion */
if(Q->front==Q->capacity)
{
Q->front=0;
}
}
return;
}
int front(Queue *Q)
{
if(Q->size==0)
{
printf("Queue is Empty\n");
exit(0);
}
/* Return the element which is at the front*/
return Q->elements[Q->front];
}
void Enqueue(Queue *Q,int element)
{
/* If the Queue is full, we cannot push an element into it as there is no space for it.*/
if(Q->size == Q->capacity)
{
printf("Queue is Full\n");
}
else
{
Q->size++;
Q->rear = Q->rear + 1;
/* As we fill the queue in circular fashion */
if(Q->rear == Q->capacity)
{
Q->rear = 0;
}
/* Insert the element in its rear side */
Q->elements[Q->rear] = element;
}
return;
}
int main()
{
Queue *Q = createQueue(5);
Enqueue(Q,1);
Enqueue(Q,2);
Enqueue(Q,3);
Enqueue(Q,4);
printf("Front element is %d\n",front(Q));
Enqueue(Q,5);
Dequeue(Q);
Enqueue(Q,6);
printf("Front element is %d\n",front(Q));
}

Answer

Even if you aren't good friends with C++, you can create a pseudo-template:

#include <stdio.h>
#include <stdlib.h>

#define QUEUE_TEMPLATE(ELTTYPE) \
typedef struct \
{\
        int capacity;\
        int size;\
        int front;\
        int rear;\
        ELTTYPE *elements;\
}Queue_##ELTTYPE;\
\
Queue_##ELTTYPE * createQueue_##ELTTYPE(int maxElements)\
{\
        /* Create a Queue */\
        Queue_##ELTTYPE *Q;\
        Q = (Queue_##ELTTYPE *)malloc(sizeof(Queue_##ELTTYPE));\
        /* Initialise its properties */\
        Q->elements = malloc(sizeof(ELTTYPE)*maxElements);\
        Q->size = 0;\
        Q->capacity = maxElements;\
        Q->front = 0;\
        Q->rear = -1;\
        /* Return the pointer */\
        return Q;\
}\
void Dequeue_##ELTTYPE(Queue_##ELTTYPE *Q)\
{\
        /* If Queue size is zero then it is empty. So we cannot pop */\
        if(Q->size==0)\
        {\
                printf("Queue is Empty\n");\
                return;\
        }\
        /* Removing an element is equivalent to incrementing index of front by one */\
        else\
        {\
                Q->size--;\
                Q->front++;\
                /* As we fill elements in circular fashion */\
                if(Q->front==Q->capacity)\
                {\
                        Q->front=0;\
                }\
        }\
        return;\
}\
ELTTYPE front_##ELTTYPE(Queue_##ELTTYPE *Q)\
{\
        if(Q->size==0)\
        {\
                printf("Queue is Empty\n");\
                exit(0);\
        }\
        /* Return the element which is at the front*/\
        return Q->elements[Q->front];\
}\
void Enqueue_##ELTTYPE(Queue_##ELTTYPE *Q,ELTTYPE element)\
{\
        /* If the Queue is full, we cannot push an element into it as there is no space for it.*/\
        if(Q->size == Q->capacity)\
        {\
                printf("Queue is Full\n");\
        }\
        else\
        {\
                Q->size++;\
                Q->rear++;\
                /* As we fill the queue in circular fashion */\
                if(Q->rear == Q->capacity)\
                {\
                        Q->rear = 0;\
                }\
                /* Insert the element in its rear side */ \
                Q->elements[Q->rear] = element;\
        }\
        return;\
}

QUEUE_TEMPLATE(int);
QUEUE_TEMPLATE(float);

int main()
{
        Queue_int *Q = createQueue_int(5);
        Queue_float *QF = createQueue_float(5);
        Enqueue_int(Q,1);
        Enqueue_int(Q,2);
        Enqueue_int(Q,3);
        Enqueue_int(Q,4);
        printf("Front element is %d\n",front_int(Q));
        Enqueue_int(Q,5);
        Dequeue_int(Q);
        Enqueue_int(Q,6);
        printf("Front element is %d\n",front_int(Q));

        Enqueue_float(QF,1);
        Enqueue_float(QF,2);
        Enqueue_float(QF,3);
        Enqueue_float(QF,4);
        printf("Front element is %f\n",front_float(QF));
        Enqueue_float(QF,5);
        Dequeue_float(QF);
        Enqueue_float(QF,6);
        printf("Front element is %f\n",front_float(QF));
}

I have added 2 instanciations with 2 different types. Output is:

Front element is 1
Front element is 2
Front element is 1.000000
Front element is 2.000000

Drawback of this method: compilation errors on the macro code may be painful to track down. You could create the code with a known type, debug/improve it, and then use a sed filter to generate the macro code, replacing the type by ELTTYPE and adding the #define and the trailing backslashes