cheadertypedefdirectiveallocation

Using if directives in headers


I have a working generic graph. The type for vertex is currently defined as:

typedef struct vertex {
  void *data;
  char *label;
  bool inGraph;
} vertex;

The void pointer can then be customized for any algorithm, like BFS, DFS, etc. This works, but I want to simplify the code by using a separate header file for a vertex that specifies its type as needed, and is included in graph.h. Something like this:

#ifndef VERTEX_H_INCLUDED
#define VERTEX_H_INCLUDED

#include <stdlib.h>
#include <stdbool.h>

#if defined (GRAPH_VERTEX1)
typedef struct vertex {
  struct vertex *parent;      
  char *label;        
  bool inGraph;       
} vertex;

#elif defined (GRAPH_VERTEX2)
typedef struct vertex {
  size_t dist;    
  char *label;        
  bool inGraph;       
} vertex;

#else  // default
typedef struct vertex {   
  char *label;        
  bool inGraph;       
} vertex;

#endif

#endif

This does not work, however. The right vertex type is not selected and allocated.

I have made an example to reproduce the issue.

main.c

#include <stdio.h>

#define GRAPH_VERTEX1

#include "graph.h"

int main() {
  vertex *v = createVertex("test");
  v->inGraph = true;
  vertex *parent = createVertex("parent");

  printf("Vertex: %s\n", v->label);
  printf("Parent: %s\n", parent->label);
  printf("In graph: %d\n", parent->inGraph);

  freeVertex(v);
  freeVertex(parent);
  return 0;
}

graph.h

#ifndef GRAPH_H_INCLUDED
#define GRAPH_H_INCLUDED

#include <stdlib.h>

#include "vertex.h"


vertex *createVertex(char *label);

void freeVertex(vertex *v);


#endif

graph.c

#include "graph.h"
#include <string.h>

vertex *createVertex(char *label) {
  vertex *v = calloc(1, sizeof(vertex));
  v->label = calloc(strlen(label) + 1, sizeof(char));
  v->inGraph = false;
  strcpy(v->label, label);
  return v;
}

void freeVertex(vertex *v) {
  free(v->label);
  free(v);
}

The example works fine if I put a single definition in vertex.h. However, if I try to use the if directives, I get the following errors:

==986== Memcheck, a memory error detector
==986== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==986== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==986== Command: ./a.out
==986==
==986== Invalid write of size 1
==986==    at 0x1090FD: main 
==986==  Address 0x4a8e050 is 0 bytes after a block of size 16 alloc'd
==986==    at 0x484DA83: calloc 
==986==    by 0x109279: createVertex 
==986==    by 0x1090F5: main 
==986==
==986== Invalid read of size 1
==986==    at 0x10913A: main 
==986==  Address 0x4a8e0f0 is 0 bytes after a block of size 16 alloc'd
==986==    at 0x484DA83: calloc 
==986==    by 0x109279: createVertex 
==986==    by 0x109108: main 
==986==
Vertex: (null)
Parent: (null)
In graph: 0
==986== 
==986== HEAP SUMMARY:
==986==     in use at exit: 0 bytes in 0 blocks
==986==   total heap usage: 5 allocs, 5 frees, 4,140 bytes allocated
==986==
==986== All heap blocks were freed -- no leaks are possible
==986==
==986== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
==986==
==986== 1 errors in context 1 of 2:
==986== Invalid read of size 1
==986==    at 0x10913A: main 
==986==  Address 0x4a8e0f0 is 0 bytes after a block of size 16 alloc'd
==986==    at 0x484DA83: calloc 
==986==    by 0x109279: createVertex 
==986==    by 0x109108: main 
==986==
==986==
==986== 1 errors in context 2 of 2:
==986== Invalid write of size 1
==986==    at 0x1090FD: main 
==986==  Address 0x4a8e050 is 0 bytes after a block of size 16 alloc'd
==986==    at 0x484DA83: calloc 
==986==    by 0x109279: createVertex 
==986==    by 0x1090F5: main 
==986==
==986== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

Solution

  • graph.c needs to have the same #define GRAPH_VERTEX1 as you have in main.c - otherwise that translation unit will get the default vertex definition (since neither GRAPH_VERTEX1 nor GRAPH_VERTEX2 will be defined).

    You could put #define GRAPH_VERTEX1 in a separate header file that you include from both main.c and graph.c:

    #ifndef SOMENAME_H
    #define SOMENAME_H
    
    #define GRAPH_VERTEX1
    #include "graph.h"
    
    #endif
    

    ... or just put it at the top of graph.h.

    Alternatively, tell your compiler to define it from the command line. Example:

    gcc -DGRAPH_VERTEX1 ...