通用数据结构接口

时间:2021-01-29 21:00:10

标签: c

假设我有一个 Stack,我想用它来存储各种不同的类型。如果我定义接口如下:

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

假设我想支持两种不同的堆栈类型,所以我创建了:

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

Stack animal_stack;
animal_stack->push = push_to_animal_stack;

如何才能使 push_to_<type>_stack 有效地​​私有并且该堆栈的调用者只能看到 Stack->push 函数?或者,这在 C 中是不可能的,您需要一种面向对象的语言来做到这一点,但是有一个统一接口的例子是什么?

1 个答案:

答案 0 :(得分:1)

您可以使用函数指针来模拟其他 OOP 语言中的方法,但您仍然需要将实例传递给该方法,否则无法知道要推送到哪个堆栈上。同样使用 void* 会产生比它解决的更多的问题。

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

这是我使用宏在 c 中模拟模板/泛型的方法(参考: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;
}

如果你想调试,宏很难用调试器调试流程,所以我使用了一个 python 脚本在编译之前生成宏的扩展到源文件和头文件(我用 scons 构建,它是基于python,所以我自动生成源文件)

相关问题