Parameterizing type definition at compile time

188 Views Asked by At

What is a good way to define this graph data structure:

typedef struct
{
    unsigned short isExists : 1;
    WEIGHT_TYPE weight;
} Weight;

typedef struct
{
    Weight adjacencyMatrix[VERTICES_NUM][VERTICES_NUM];
    VERTEX_TYPE vertices[VERTICES_NUM];
} Graph;

using a value for VERTICES_NUM defined at compile time?

I am looking for a better solution than defining VERTICES_NUM before including the .h file that contains the above, and I do not want to use dynamic allocation.

3

There are 3 best solutions below

15
jxh On BEST ANSWER

I would first approach the problem with a macro that will define the types for you.

#define DEFINE_GRAPH(NAME, WEIGHT_TYPE, VERTEX_TYPE, VERTICES_NUM) \
        DEFINE_WEIGHT_TYPE(NAME, WEIGHT_TYPE); \
        DEFINE_GRAPH_TYPE(NAME, VERTEX_TYPE, VERTICES_NUM)

#define DEFINE_WEIGHT_TYPE(NAME, WEIGHT_TYPE) \
        typedef struct { \
            unsigned short isExists : 1; \
            WEIGHT_TYPE weight; \
        } WEIGHT(NAME)

#define DEFINE_GRAPH_TYPE(NAME, VERTEX_TYPE, VERTICES_NUM) \
        typedef struct { \
            WEIGHT(NAME) adjacencyMatrix[VERTICES_NUM][VERTICES_NUM]; \
            VERTEX_TYPE vertices[VERTICES_NUM]; \
        } GRAPH(NAME)

#define WEIGHT(NAME) NAME ## _Weight
#define GRAPH(NAME) NAME ## _Graph

Now, a user could do something like:

DEFINE_GRAPH(Test, char, float, 10);

And in their code, they would use the data structure like this:

int main () {
    GRAPH(Test) g;
    /* ... */
}
0
gulpr On

I know I can define these constants before including the .h file, but I'm looking for a better solution.

Usually two methods are used.

  1. Create .h file with common definitions like your sizes and include this file when needed.

  2. Use compiler command line options. gcc has -D option (-DVERTICES_NUM=10)

0
Jiri Volejnik On

Using macros is not a good idea. Perhaps you can try something like this.

The advantage is that you can easily work with multiple graphs of different sizes.

#include <stdio.h>

typedef int WEIGHT_TYPE;
typedef int VERTEX_TYPE;

typedef struct
{
    unsigned short isExists : 1;
    WEIGHT_TYPE weight;
} Weight;

typedef struct
{
    int verticesNum;
    VERTEX_TYPE* vertices;
    Weight* adjacencyMatrix;
} Graph;

Graph newGraph(int n, VERTEX_TYPE* v, Weight* a ) {
    Graph g;
    g.verticesNum = n;
    g.vertices = v;
    g.adjacencyMatrix = a;
    return g;
}

VERTEX_TYPE* getVertex(Graph* g, int n) {
    return g->vertices + n;
}

Weight* getAdjacency(Graph* g, int x, int y) {
    return g->adjacencyMatrix + y * g->verticesNum + x;
}

void setAdjacency(Graph* g, int x, int y, int exists, WEIGHT_TYPE weight) {
    Weight* w = g->adjacencyMatrix + y * g->verticesNum + x;
    w->isExists = exists;
    w->weight = weight;
}

void clearGraph(Graph* g) {
    for (int i = 0; i < g->verticesNum; ++i) {
        g->vertices[i] = 0;        
    }
    for (int y = 0; y < g->verticesNum; ++y) {
        for (int x = 0; x < g->verticesNum; ++x) {
            Weight* w = getAdjacency(g, x, y);
            w->isExists = 0;        
            w->weight = 0;
        }
    }
}

void printAdjacency(Graph* g) {
    for (int y = 0; y < g->verticesNum; ++y) {
        for (int x = 0; x < g->verticesNum; ++x) {
            Weight* w = getAdjacency(g, x, y);
            if (w->isExists)
                printf("%d ", w->weight);
            else
                printf(". ");
        }
        printf("\n");
    }
}

int main() {
    // Stack allocated data
    int n = 10;
    Weight adjacency[n*n];
    VERTEX_TYPE vertices[n];
    
    // Stack allocated graph that makes use of the stack allocated data above
    Graph g = newGraph(n, vertices, adjacency);
    clearGraph(&g);
    setAdjacency(&g,1,1,1,3);
    printAdjacency(&g);

    return 0;
}