2

I made a generic vector in C using macros. Is the concept viable or do I get a one-way trip to the bonfire for even thinking about it?

#ifndef VECTOR_H
#define VECTOR_H

#define vector_at(vector, pos) ((vector).data[pos])

#define VECTOR_DEFINITION(type)\
typedef struct {\
    size_t size;\
    size_t capacity;\
    type *data;\
} vector_ ## type ## _t;\
void vector_ ## type ## _reserve(vector_ ## type ## _t *vector, size_t size) {\
    size_t capacity = 1;\
    while (capacity < size) capacity *= 2;\
    if (size == 0) capacity = 0;\
    if (capacity != vector->capacity)\
    {\
        vector->capacity = capacity;\
        if (size == 0) {\
            free(vector->data);\
            vector->data = NULL;\
        } else {\
            vector->data = realloc(vector->data, vector->capacity * sizeof(type));\
        }\
    }\
}\
void vector_ ## type ## _resize(vector_ ## type ## _t *vector, size_t size) {\
    vector->size = size;\
    vector_ ## type ## _reserve(vector, size);\
}\
void vector_ ## type ## _push_back(vector_ ## type ## _t *vector, type value) {\
    if (vector->size >= vector->capacity) {\
        if (vector->capacity == 0) vector->capacity = 1;\
        else vector->capacity *= 2;\
        vector->data = realloc(vector->data, vector->capacity * sizeof(type));\
    }\
    vector->data[vector->size++] = value;\
}\
type vector_ ## type ## _pop_back(vector_ ## type ## _t *vector) {\
    return vector->data[--vector->size];\
}\
void vector_ ## type ## _init(vector_ ## type ## _t *vector, size_t size) {\
    vector->size = size;\
    vector->capacity = 0;\
    vector->data = NULL;\
    vector_ ## type ## _reserve(vector, size);\
}\
void vector_ ## type ## _destroy(vector_ ## type ## _t *vector) {\
    free(vector->data);\
    vector->size = 0;\
    vector->capacity = 0;\
    vector->data = NULL;\
}\

#endif

The code can then be used like so:

#include <stdio.h>
#include <stdlib.h>
#include "vector.h"

typedef unsigned int uint;
typedef char* str;

VECTOR_DEFINITION(uint)
VECTOR_DEFINITION(str)

int main()
{
    vector_uint_t vector;
    vector_uint_init(&vector, 10);

    for (unsigned int i = 0; i < vector.size; ++i)
        vector.data[i] = i;

    for (unsigned int i = 0; i < 10; ++i)
        vector_uint_push_back(&vector, i);

    for (unsigned int i = 0; i < vector.size; ++i)
        printf("%d ", vector.data[i]);
    printf("\n");

    vector_uint_destroy(&vector);

    vector_str_t sentence;
    vector_str_init(&sentence, 0);

    vector_str_push_back(&sentence, "Hello");
    vector_str_push_back(&sentence, "World!");
    vector_str_push_back(&sentence, "How");
    vector_str_push_back(&sentence, "are");
    vector_str_push_back(&sentence, "you?");

    for (unsigned int i = 0; i < sentence.size; ++i)
        printf("%s ", sentence.data[i]);
    printf("\n");

    vector_str_destroy(&sentence);

    return 0;
}

and produces the following output:

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 
Hello World! How are you? 

additionally, would there be a better way to implement generic containers with type safety?

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
Siapran
  • 65
  • 6
  • What happens when someone puts `VECTOR_DEFINITION(uint)` in two different places in the source code? – aruisdante Mar 10 '15 at 21:12
  • 1
    I don't think the code would compile since the definitions would be made twice. I can't use a ifdef trick to prevent that, because the macro would expand before the definition is called. now that I think about it, I should make two definition macros for headers and source files, so that programmers may define the containers to suit their needs in a single header/source couple. – Siapran Mar 10 '15 at 21:16
  • 1
    There are already a few other QA's with interesting discussions on the topic, ex: http://stackoverflow.com/questions/16522341/pseudo-generics-in-c – aruisdante Mar 10 '15 at 21:20
  • oh, indeed! so do I have to understand that it IS viable? – Siapran Mar 10 '15 at 21:24
  • I couldn't find the original source while googling, but I remember reading that Bjarne experimented with huge preprocessor macros like the one above in the `Cfront` compiler (the first C++ compiler). Might've been how the first versions did templates if it generated C code I guess, or maybe it was just an experiment in conjunction with working on them. (It also seems to have stored template instances in a separate template repository.) – Ulfalizer Mar 10 '15 at 21:46
  • Definitely viable. Less defensible when seen in production C++ code though — we have some C++ simulation software filled with this sort of thing! – halfflat Mar 10 '15 at 22:07

1 Answers1

1

Commenters have suggested that this concept is viable, and therefore I will consider this question answered.

For more information, you can take a look at the following links:

Thank you.

Community
  • 1
  • 1
Siapran
  • 65
  • 6