David542
David542

Reputation: 110143

Interface for a common data structure

Let's say I have Stack that I want to use to store various different types. If I define the interface as follows:

// stack.h
typedef struct Stack {
    size
    push // function pointers
    pop
    etc.
};

And let's say I want to support two different stack types, and so I create:

// stack.c
Stack person_stack;
person_stack->push = push_to_person_stack;

Stack animal_stack;
animal_stack->push = push_to_animal_stack;

How can I make it such that the push_to_<type>_stack is effectively private and the caller of that stack is only able to see the Stack->push function? Or maybe that's not possible in C and you need an OO language to do it, but what would be an example of having a unified interface for this?

Upvotes: 0

Views: 77

Answers (1)

thakee nathees
thakee nathees

Reputation: 945

You can use function pointers to emulate methods in other OOP languages but still you need to pass the instance to the method otherwise there is no way to know on which stack to push. Also using void* would make more problems than it solves.

struct Stack {
    void (*push)(Stack* self, void* data); //< self here
}

Here is a way that I use to emulate template/generics in c by using macros (reference : https://github.com/wren-lang/wren/blob/main/src/vm/wren_utils.h#L16)


#define DECLARE_STACK(type)                                   \
    typedef struct {                                          \
        type* data;                                           \
        int size;                                             \
        int capacity;                                         \
    } type##Stack;                                            \
    void type##Stack_push(type##Stack* self, type value);     \
    type type##Stack_pop(type##Stack* self);                  \
    void type##Stack_init(type##Stack* self);                 \

#define DEFINE_STACK(type)                                                        \
    void type##Stack_push(type##Stack* self, type value) {                        \
        if (self->capacity <= self->size + 1) {                                   \
            self->capacity = self->capacity * 2;                                  \
            self->data = realloc(self->data, sizeof(type) * self->capacity);      \
        }                                                                         \
        self->data[self->size] = value;                                           \
        self->size++;                                                             \
    }                                                                             \
                                                                                  \
    type type##Stack_pop(type##Stack* self) {                                     \
        self->size--;                                                             \
        return self->data[self->size];                                            \
    }                                                                             \
                                                                                  \
    void type##Stack_init(type##Stack* self) {                                    \
        self->size = 0;                                                           \
        self->capacity = 2;                                                       \
        self->data = malloc(sizeof(type) * self->capacity);                       \
    }                                                                             \
    

typedef struct Person {
    int id;
} Person;

DECLARE_STACK(Person); // in person.h
DEFINE_STACK(Person);  // in person.c


int main() {

    Person p1, p2, p3, p4;
    p1.id = 1; p2.id = 2; p3.id = 3; p4.id = 4;

    PersonStack stack;
    
    PersonStack_init(&stack);
    PersonStack_push(&stack, p1);
    PersonStack_push(&stack, p2);
    PersonStack_push(&stack, p3);

    Person p;
    p = PersonStack_pop(&stack); // p.id = 3
    p = PersonStack_pop(&stack); // p.id = 2

    PersonStack_push(&stack, p4);
    p = PersonStack_pop(&stack); // p.id = 4
    p = PersonStack_pop(&stack); // p.id = 1


    return 0;
}

And If you want to debug, macros are harder to debug the flow with a debugger, so I've used a python script to generate the expansion of the macro to source and header files before compile (I build with scons which is python based so I automated the source files generation)

Upvotes: 1

Related Questions