在C动方法调度 [英] Dynamic method dispatching in C

查看:81
本文介绍了在C动方法调度的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我知道这听起​​来很愚蠢,我知道C是不是面向对象的语言。

I know it sounds silly and i know that C is not an Object Oriented Language.

但是,没有什么办法,动态方法调度可以用C实现?
二想过函数指针,但没有得到全部的想法。

But is there any way that dynamic method dispatching can be achieved in c? Ii thought about function pointers but don't get to entire idea.

如何能实现我这个

[任何资源学习的是AP preciated]

[any resource to learn is appreciated]

推荐答案

正如其他人所指出的,这当然是可以实现这一点C.它不仅是可能的,这是一个相当普遍的机制。最常用的例子可能是在UNIX文件描述符接口。 A 阅读()上一个文件描述符的通话将分派到特定于该提供的文件中描述的设备或服务的读取功能(是一个文件?是它的插座?是它的一些其它类型的设备?)。

As others have noted, it is certainly possible to implement this in C. Not only is it possible, it is a fairly common mechanism. The most commonly used example is probably the file descriptor interface in UNIX. A read() call on a file descriptor will dispatch to a read function specific to the device or service that provided that file descriptor (was it a file? was it a socket? was it some other kind of device?).

唯一的技巧就是将指针恢复到从抽象类型的具体类型。文件描述符,UNIX使用包含特定于该描述符的信息的查找表。如果您使用的是指向一个对象,通过该接口用户持有的指针基地式的,而不是派生类型。 C没有继承的本身的,但它确实保证该指针的第一个元素结构等于的指针含结构。所以,你可以使用这个通过使基地实例恢复派生类型是派生的第一个成员。

The only trick is to recover the pointer to the concrete type from the abstract type. For file descriptors, UNIX uses a lookup table that contains information specific to that descriptor. If you are using a pointer to an object, the pointer held by the user of the interface is the "base" type, not the "derived" type. C doesn't have inheritance per se, but it does guarantee that the pointer to the first element of a struct is equal to the pointer of the containing struct. So you can use this to recover the "derived" type by making the instance of the "base" be the first member of the "derived".

下面是一个简单的例子,用栈:

Here's a simple example with a stack:

struct Stack {
    const struct StackInterface * const vtable;
};

struct StackInterface {
    int (*top)(struct Stack *);
    void (*pop)(struct Stack *);
    void (*push)(struct Stack *, int);
    int (*empty)(struct Stack *);
    int (*full)(struct Stack *);
    void (*destroy)(struct Stack *);
};

inline int stack_top (struct Stack *s) { return s->vtable->top(s); }
inline void stack_pop (struct Stack *s) { s->vtable->pop(s); }
inline void stack_push (struct Stack *s, int x) { s->vtable->push(s, x); }
inline int stack_empty (struct Stack *s) { return s->vtable->empty(s); }
inline int stack_full (struct Stack *s) { return s->vtable->full(s); }
inline void stack_destroy (struct Stack *s) { s->vtable->destroy(s); }

现在,如果我想要实现使用固定大小的数组堆栈,我可以做这样的事情:

Now, if I wanted to implement a stack using a fixed sized array, I could do something like this:

struct StackArray {
    struct Stack base;
    int idx;
    int array[STACK_ARRAY_MAX];
};
static int stack_array_top (struct Stack *s) { /* ... */ }
static void stack_array_pop (struct Stack *s) { /* ... */ }
static void stack_array_push (struct Stack *s, int x) { /* ... */ }
static int stack_array_empty (struct Stack *s) { /* ... */ }
static int stack_array_full (struct Stack *s) { /* ... */ }
static void stack_array_destroy (struct Stack *s) { /* ... */ }
struct Stack * stack_array_create () {
    static const struct StackInterface vtable = {
        stack_array_top, stack_array_pop, stack_array_push,
        stack_array_empty, stack_array_full, stack_array_destroy
    };
    static struct Stack base = { &vtable };
    struct StackArray *sa = malloc(sizeof(*sa));
    memcpy(&sa->base, &base, sizeof(base));
    sa->idx = 0;
    return &sa->base;
}

如果我想实现一个堆栈中使用列表,而不是:

And if I wanted to implement a stack using a list instead:

struct StackList {
    struct Stack base;
    struct StackNode *head;
};
struct StackNode {
    struct StackNode *next;
    int data;
};
static int stack_list_top (struct Stack *s) { /* ... */ }
static void stack_list_pop (struct Stack *s) { /* ... */ }
static void stack_list_push (struct Stack *s, int x) { /* ... */ }
static int stack_list_empty (struct Stack *s) { /* ... */ }
static int stack_list_full (struct Stack *s) { /* ... */ }
static void stack_list_destroy (struct Stack *s) { /* ... */ }
struct Stack * stack_list_create () {
    static const struct StackInterface vtable = {
        stack_list_top, stack_list_pop, stack_list_push,
        stack_list_empty, stack_list_full, stack_list_destroy
    };
    static struct Stack base = { &vtable };
    struct StackList *sl = malloc(sizeof(*sl));
    memcpy(&sl->base, &base, sizeof(base));
    sl->head = 0;
    return &sl->base;
}

栈操作的实现会简单地把结构堆叠* 什么它知道它应该是。例如:

The implementations of the stack operations would simply cast the struct Stack * to what it knows it should be. For example:

static int stack_array_empty (struct Stack *s) {
    struct StackArray *sa = (void *)s;
    return sa->idx == 0;
}

static int stack_list_empty (struct Stack *s) {
    struct StackList *sl = (void *)s;
    return sl->head == 0;
}

在堆栈的用户调用堆栈实例栈操作,该操作将派遣到虚函数表进行相应的操作。这虚函数表通过创建功能与对应于其特定的实现的功能初始化。所以:

When a user of a stack invokes a stack operation on the stack instance, the operation will dispatch to the corresponding operation in the vtable. This vtable is initialized by the creation function with the functions that correspond to its particular implementation. So:

Stack *s1 = stack_array_create();
Stack *s2 = stack_list_create();

stack_push(s1, 1);
stack_push(s2, 1);

stack_push()被称为两个 S1 S2 。但是,对于 S1 ,就会分派到 stack_array_push(),而 S2 ,就会分派到 stack_list_push()

stack_push() is called on both s1 and s2. But, for s1, it will dispatch to stack_array_push(), while for s2, it will dispatch to stack_list_push().

这篇关于在C动方法调度的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆