How to stop duplicate function definitions in C that are created by macro expansion

Advertisements

for example, let’s say I have a macro that expands into a struct definition with corresponding functions like so:

array.h

#ifndef ARRAY_H
#define ARRAY_H

#include <assert.h>

#define Array(t, size) Array_##t ##_##size

#define define_array(t, size) \
typedef struct Array(t, size) \
{ \
    t data[size]; \
} Array(t, size); \
void array_insert_##t(Array(t, size) *arr, t elm, int index) \
{ \
    assert(index < size); \
    arr->data[index] = elm; \
} \
t array_get_##t(Array(t, size) *arr, int index) \
{ \
    assert(index < size); \
    return arr->data[index]; \
}

#endif

and I want to use this in multiple files like so:

s1.c

#include <stdio.h>
#include "array.h"

define_array(int, 10)

Array(int, 10) get_ints()
{
    Array(int, 10) ret;
    for(int i = 0 ; i < 10 ; i++)
    {
        array_insert_int(&ret, i, i);
    }
    return ret;
}

And now I want to use the type and functions that were expanded by define_array(int, 10) in other files. So what I did is:

s2.c

#include <stdio.h>
#include "array.h"

define_array(int, 10)

extern Array(int, 10) get_ints();

int main()
{
    Array(int, 10) arr;
    arr = get_ints();
    
    printf("%d\n", array_get_int(&arr, 0));
    return 0;
}

However I get a linking error telling me that I have mutiple definitions of the same functions. And so I thought putting guards around the expansion would help:

s1.c + s2.c

#ifndef ARRAY_INT_10
#define ARRAY_INT_10
define_array(int, 10)
#endif

but I still get the same error for some reason. Why is this error happening and how do I fix it?

>Solution :

Why is this error happening and how do I fix it?

You may not have more than one definition of the same external identifier in the same program. You are defining external functions, and by #includeing your header in more than one translation unit, you obtain a definition of each associated function in each translation unit, which is not allowed.

The simplest fix would be to modify the macros to declare the functions static. Their identifiers will then be internal (to each translation unit) instead of external.

It would also be possible to arrange to have array.h provide only prototypes of the functions, and then to have a separate, single source file for the function implementations. This would be a lot messier, though, at least because

  • you would need a way to determine what functions needed to be implemented in that one source, and
  • to avoid name collisions, you would need to incorporate the array sizes into the function names, too, not just into the structure tags and typedef names.

Leave a ReplyCancel reply