I have a header file that introduces a macro that declares a Stack structure and some asociated functions that work with that struct. Header file (Stack.h) content is shown below:
#ifndef STACK_H
#define STACK_H
#include <stddef.h> // size_t
#include <malloc.h> // malloc(), calloc()
#define STACK_DEFAULT_GROWTH_FACTOR 2.0
#define STACK_REALLOC_FAIL -1
#define DECL_STACK(T, Id) \
typedef struct Stack_##Id { \
size_t capacity; \
size_t size; \
float growth_factor; \
T *data; \
} Stack_##Id; \
\
Stack_##Id* Stack_##Id##_New(size_t capacity) { \
Stack_##Id *p = malloc(sizeof(Stack_##Id)); \
if (!p) return NULL; \
T *d = calloc(capacity, sizeof(T)); \
if (!d) { \
free(p); \
return NULL; \
} \
p->capacity = capacity; \
p->size = 0; \
p->growth_factor = STACK_DEFAULT_GROWTH_FACTOR; \
p->data = d; \
return p; \
} \
\
void Stack_##Id##_Free(Stack_##Id *stack) { \
free(stack->data); \
free(stack); \
} \
\
void Stack_##Id##_Push(Stack_##Id *stack, const T item) { \
if (stack->size >= stack->capacity) { \
size_t new_capacity = (size_t)(stack->capacity * stack->growth_factor); \
T *d = realloc(stack->data, new_capacity); \
if (!d) { \
free(stack); \
exit(STACK_REALLOC_FAIL); \
return; \
} \
stack->capacity = new_capacity; \
} \
stack->data[stack->size++] = item; \
} \
\
T Stack_##Id##_Pop(Stack_##Id *stack) { \
return stack->data[--(stack->size)]; \
} \
\
T Stack_##Id##_Peek(const Stack_##Id *stack) { \
return stack->data[stack->size - 1]; \
}
#endif // STACK_H
I have Main.c file that includes Stack.h and another header file called Some.h. Some.h also includes Stack.h in order to use the mentioned macro to declare a function that utilizes declared stack struct in some way. File Some.c provides definition of the function in some simpliest form. I compile and try to link the program with following makefile:
objects = Main.o Some.o
CC = gcc
flags = -Wall -Wextra
Some.o : Some.c Some.h Stack.h
$(CC) $(flags) -c Some.c Some.h Stack.h
Main.o : Main.c Some.h Stack.h
$(CC) $(flags) -c Main.c Some.h Stack.h
Main : $(objects)
$(CC) $(objects) -o Main
clean: Main
rm $(objects)
Here is the content of Main.c, Some.h and Some.c accordingly: Main.c:
#include <stdio.h>
#include "Stack.h"
#include "Some.h"
#ifndef STACK_INT
#define STACK_INT
DECL_STACK(int, int);
#endif
int main() {
Stack_int *stack = Stack_int_New(10);
Stack_int_Push(stack, 100);
printf("%d\n", Stack_int_Peek(stack));
Stack_int_Pop(stack);
Use_stack(stack);
Stack_int_Free(stack);
return 0;
}
Some.h:
#include "Stack.h"
#include <stdio.h>
#ifndef STACK_INT
#define STACK_INT
DECL_STACK(int, int);
#endif
int Use_stack(Stack_int *stack);
Some.c:
#include "Some.h"
int Use_stack(Stack_int *stack) {
Stack_int_Push(stack, 1);
printf("%d\n", Stack_int_Peek(stack));
Stack_int_Push(stack, 2);
printf("%d\n", Stack_int_Peek(stack));
Stack_int_Pop(stack);
return Stack_int_Pop(stack);
}
Object files compile successfully, but I get an error on the linking step that states that multiple function definitions are present for functions Stack_int_New(), Stack_int_Free(), etc. Multiple definitons are present only for functions, not for the struct Stack_int itself (that surpises me actually).
I expected that macro expansion guards that are inserted right around the DECL_STACK macro will prevent multiple macro expansion and so will prevent function (and the struct) redefinition. But that didn't happen.
I tried to remove header files from complilation recipies in the makefile, but it didn't help. With them or without them, command gcc Main.c/Some.c -E
shows that preprocessor inserts the macro and expands it anyway.
How can I prevent multiple macro expansion if I want to include my Stack.h file in multiple .c files and declare stack with the same Id, i.e. same struct and functions for it but without it resulting in linking errors?
The include guard STACK_H only works on a compilation unit level. So you can include stack.h directly or indirectly several times in the same source file without running into problems caused by duplicate definitions.
But during macro expansion you create functions like Stack_int_New() etc. in every source file that uses stack.h. These functions are defined in the global namespace of your program. During link time the duplicate global definitions are detected and you get error messages.
Possible solutions depend on your use case. If you want to create and access a stack from some.c and a separate one from main.c, as suggested by @chux you could declare the funtions as static ie.
static Stack_##Id* Stack_##Id##_New(size_t capacity)
This makes every implementation of that function local to the compile unit. That way you'll have multiple independent copies of your stack functions in the program.
If you want to share the same stack between object modules you should put the implementation in its own module, probably one per variable type required.