Tenho um arquivo de cabeçalho que apresenta uma macro que declara uma estrutura Stack e algumas funções associadas que funcionam com essa estrutura. O conteúdo do arquivo de cabeçalho (Stack.h) é mostrado abaixo:
#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
Tenho um arquivo Main.c que inclui Stack.h e outro arquivo de cabeçalho chamado Some.h. Some.h também inclui Stack.h para usar a macro mencionada para declarar uma função que utiliza a estrutura de pilha declarada de alguma forma. O arquivo Some.c fornece a definição da função de forma mais simples. Eu compilo e tento vincular o programa ao seguinte 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)
Aqui está o conteúdo de Main.c, Some.h e Some.c: 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;
}
Alguns.h:
#include "Stack.h"
#include <stdio.h>
#ifndef STACK_INT
#define STACK_INT
DECL_STACK(int, int);
#endif
int Use_stack(Stack_int *stack);
Alguns.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);
}
Arquivos de objeto são compilados com sucesso, mas recebo um erro na etapa de vinculação que afirma que várias definições de função estão presentes para as funções Stack_int_New(), Stack_int_Free(), etc. Várias definições estão presentes apenas para funções, não para a própria struct Stack_int (o que me surpreende, na verdade). Eu esperava que as proteções de expansão de macro inseridas ao redor da macro DECL_STACK impedissem a expansão de múltiplas macros e, portanto, impediriam a redefinição da função (e da struct). Mas isso não aconteceu. Tentei remover os arquivos de cabeçalho das receitas de compilação no makefile, mas não ajudou. Com eles ou sem eles, o comando gcc Main.c/Some.c -E
mostra que o pré-processador insere a macro e a expande de qualquer maneira. Como posso impedir a expansão de múltiplas macros se eu quiser incluir meu arquivo Stack.h em vários arquivos .c e declarar a pilha com o mesmo Id, ou seja, a mesma struct e funções para ela, mas sem que isso resulte em erros de vinculação?