I have 2 libraries made from scratch in c, vector.h
and string.h
with the source file string.c
(the vector.h
only has define directives, no need for source file), and used the #pragma once
directive so that the header files would only be included one time. The string library uses the vector library, and then both are included in main.c
.
With that setup, i ran into two problems:
- if i simply did
gcc main.c -o build/main.exe
, it couldn't find the function definitions instring.c
for the function declarations instring.h
(undefined reference to `stringFunction') - if i first compiled the source files into objects, and then compiled them into the executable (
gcc -c string/string.c -o build/obj/string.o
,gcc -c main.c -o build/obj/main.o
,gcc -o build/main.exe build/obj/main.o build/obj/string.o
,./build/main.exe
), it said there were multiple definitions of the same functions (build/obj/string.o: in function `vectorCreate_String': string.c:(.text 0x0): multiple definition of `vectorCreate_String'; build/obj/main.o:main.c:(.text 0x0): first defined here)
Folder structure:
PROJECT
- string
- string.c
- string.h
- vector
- vector.h
- main.c
vector.h
#pragma once
#include <stdlib.h>
#define DEFINE_VECTOR(type) typedef struct { \
unsigned int size; \
size_t bytesize; \
type *data; \
} Vector_##type; \
Vector_##type *vectorCreate_##type(unsigned int initialSize, type *initialElements) { \
Vector_##type *v = (Vector_##type*)malloc(sizeof(Vector_##type)); \
v->size = initialSize; \
v->bytesize = initialSize * sizeof(type); \
v->data = (type*)malloc(v->bytesize); \
for (unsigned int i=0; i<initialSize; i ) v->data[i] = initialElements[i]; \
return v; \
} \
void vectorPush_##type(Vector_##type *v, type element) { \
v->size ; \
v->bytesize = sizeof(type); \
v->data = (type*)realloc(v->data, v->bytesize); \
v->data[v->size-1] = element; \
} \
void vectorFree_##type(Vector_##type *v) { \
free(v->data); \
free(v); \
} \
Vector_##type *vectorSlice_##type(Vector_##type *v, unsigned int start, unsigned int end) { \
Vector_##type *slice = (Vector_##type*)malloc(sizeof(Vector_##type)); \
slice->size = end - start; \
slice->bytesize = slice->size * sizeof(type); \
slice->data = (type*)malloc(slice->bytesize); \
for (unsigned int i=0; i<slice->size; i ) slice->data[i] = v->data[start i]; \
return slice; \
}
#define Vector(type) Vector_##type
#define vectorCreate(type, initialSize, initialElements) vectorCreate_##type(initialSize, initialElements)
#define vectorPush(type, vector, element) vectorPush_##type(vector, element)
#define vectorFree(type, vector) vectorFree_##type(vector)
#define vectorSlice(type, vector, start, end) vectorSlice_##type(vector, start, end)
string.h
#pragma once
#include "../vector/vector.h"
typedef struct {
unsigned int length;
char *str;
} String;
DEFINE_VECTOR(String);
String *stringCreate(char *str);
String *stringAppend(String *string, char *str);
void stringFree(String *string);
String *stringSlice(String *string, unsigned int start, unsigned int end);
Vector(String) *stringSplit(String *string, char *delimiter);
string.c
#include "string.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
String *stringCreate(char *str) {
String *string = malloc(sizeof(String));
string->length = strlen(str);
string->str = malloc(string->length 1);
strcpy(string->str, str);
return string;
}
String *stringAppend(String *string, char *str) {
string->length = strlen(str);
string->str = realloc(string->str, string->length 1);
strcat(string->str, str);
return string;
}
void stringFree(String *string) {
free(string->str);
free(string);
}
String *stringSlice(String *string, unsigned int start, unsigned int end) {
String *newString;
newString->length = end - start;
newString->str = malloc(newString->length 1);
for (unsigned int i=0; i<newString->length; i ) newString->str[i] = string->str[start i];
newString->str[newString->length] = '\0';
return newString;
}
// not implemented yet
Vector(String) *stringSplit(String *string, char *delimiter) {
return NULL;
}
main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "./string/string.h"
// stuff to test if everything is working properly
int main(int argc, char *argv[]) {
Vector(String) *strings = vectorCreate(String, 0, NULL);
vectorPush(String, strings, *stringCreate("Hello"));
vectorPush(String, strings, *stringCreate("World"));
printf("%s %s\n", strings->data[0].str, strings->data[1].str);
vectorFree(String, strings);
return 0;
}
CodePudding user response:
A "function declaration" is usually put into a .h file.
A "function definition" is usually put into a .c file.
DEFINE_VECTOR
provides function definitions and you use it in string.h
.
Which means the function vectorCreate_String()
for example is defined both in string.c
and main.c
due to both including string.h
.
I suggest you add another macro in vector.h
that only generates function declarations and move that typedef struct from DEFINE_VECTOR
to that macro. Use that macro in string.h
, and then use DEFINE_VECTOR
in string.c
to provide definitions for the declared functions in string.h
.