Динамическая отправка в C с использованием таблицы виртуальных методов

Я надеюсь найти подсказку (желательно на хорошем примере) для реализации динамической диспетчеризации в C.

Я изучаю C и на практике хочу перевести с Java на C, используя таблицу виртуальных методов динамической отправки.

например, у меня есть код Java:

abstract class Foo {
    public abstract int val(); 
    public abstract Boolean error();
}

class Fail extends Foo {
    public int val(){ return 0;}
    public Boolean error(){return true;}
}

class IntFoo extends Foo {
    int v;
    public IntFoo(int value){this.value=v;}
    public int val(){ return v;}
    public Boolean error(){return False;}
}

и я мог бы просто перевести некоторые основные вещи вроде этого:

typedef struct Foo{
    void(**vtable);
}Foo;

typedef struct Fail{
    void(**vtable);
    struct Foo inherited;
}Fail;

typedef struct IntFoo{
    void(**vtable);
    struct Foo inherited;
}IntFoo;

Я застрял, пытаясь завершить это, потому что я не знаю:

  1. Как определить эти методы в c.
  2. Установите адреса этих методов в vtable, чтобы компилятор распознал правильный метод для вызова.
  3. Что еще определить, чтобы заставить его работать.

person Solix    schedule 31.03.2013    source источник
comment
Вместо того, чтобы пытаться втиснуть круглый стержень в квадратное отверстие, чтобы попытаться навязать структуру ООП на не-ООП-языке, я бы использовал для этого C++, а не C.   -  person Hovercraft Full Of Eels    schedule 31.03.2013
comment
@HovercraftFullOfEels Я хочу сделать это только на c, как меня попросил мой профессор.   -  person Solix    schedule 31.03.2013
comment
Источником подобных материалов является ooc. pdf Акселя Тобиаса Шрайнера.   -  person luser droog    schedule 01.04.2013
comment
@luserdoog позже сегодня я получил этот pdf-файл из одного из сообщений stackoverflow, но я не смог найти конкретную часть, которую ищу. ты знаешь в какой главе об этом говорится?   -  person Solix    schedule 01.04.2013
comment
Я не знаю! Я так и не прошел вторую главу. Я просто предположил. Мне жаль.   -  person luser droog    schedule 01.04.2013


Ответы (2)


Нет простого способа сделать это. Тем не менее, вы можете вручную делать то, что делает компилятор C++. В вашем случае это будет выглядеть так:

typedef struct vtable_Fail
{
    int  (*val)(struct Fail *this_pointer);
    bool (*error)(struct Fail *this_pointer);
} vtable_Fail;

typedef struct vtable_IntFoo
{
    int  (*val)(struct IntFoo *this_pointer);
    bool (*error)(struct IntFoo *this_pointer);
} vtable_IntFoo;

int Fail_val(struct Fail *this_pointer)
{
    return 0;
}

...

void IntFoo_ctor(struct IntFoo *this_pointer, int value)
{
     this_pointer->v = value;
}

int IntFoo_val(struct IntFoo *this_pointer)
{
    return this_pointer->v;
}

...

struct Fail
{
    vtable_Fail *vtable;
};

struct IntFoo
{
    vtable_IntFoo *vtable;
    int v;
};

Идея состоит в том, что каждая структура должна иметь сопутствующую структуру виртуальной таблицы, в которой хранятся указатели на все виртуальные методы, реализуемые этой структурой. Каждая структура Vtable имеет только один экземпляр. Они должны находиться в статической памяти и должны быть инициированы указателями на функции. Эти указатели должны реализовывать виртуальные методы так, как это необходимо для каждой конкретной структуры. Сами структуры могут иметь множество экземпляров. Поля данных Vtable в экземпляре должны указывать на статическую структуру vtable своего класса.

Параметр this_pointer передает адрес экземпляра. Его нужно передавать вручную. Это C в конце дня.

Обратите внимание, что вам нужно выделить структуры vtable, инициализировать указатели vtable и т. д. И вам нужно сделать все это вручную с помощью собственного кода.

person Kirill Kobelev    schedule 31.03.2013

Подход, который я бы порекомендовал, состоит в том, чтобы посмотреть на какой-нибудь другой код C, использующий таблицу диспетчеризации, и посмотреть, как она структурирована. Конкретные примеры, о которых я знаю навскидку (возможно, не самые лучшие обучающие примеры, так как они просто случайное бесплатное программное обеспечение, над которым я работал), — это MIT Kerberos и Heimdal, оба из которых используют таблицы диспетчеризации повсюду. место и веб-сервер Apache и то, как он обрабатывает динамически загружаемые модули. Ни один из них не делает наследования, но добавить наследование относительно просто: вы видите, является ли метод, который вы пытаетесь вызвать, NULL, и если да, вы проверяете родительскую таблицу диспетчеризации для этого метода.

Краткая версия того, как обращаться с диспетчерской таблицей в C, заключается в том, что вы определяете структуру данных, аналогичную виртуальной таблице C++, которая содержит указатели на функции и какой-то способ выяснить, какой указатель использовать. Что-то типа:

typedef void (*dispatch_func)(void *);
struct dispatch {
    const char *command;
    dispatch_func callback;
};

— это суперуниверсальная версия, которая сопоставляет имена строковых методов с функциями, принимающими один анонимный указатель в качестве аргумента. Ваша фактическая таблица отправки будет состоять из таких массивов, как здесь (модифицированный пример, взятый из tinyleaf в исходном коде INN):

const struct dispatch commands[] = {
    { "help",  command_help },
    { "ihave", command_ihave },
    { "quit",  command_quit }
};

Очевидно, что чем больше вы знаете о функциях, тем более конкретным вы сможете сделать прототип и встроить в диспетчерскую таблицу другие критерии выбора (например, количество аргументов). (Фактический источник INN имеет минимальное и максимальное количество аргументов, передает структуру аргументов, а не просто void *, и включает описание команды в таблице отправки.)

Базовый код для поиска в диспетчерской таблице довольно прост. Предполагая, что у вас есть массив этих записей структуры отправки в vtable и длину таблицы в length, что-то вроде:

for (i = 0; i < length; i++)
    if (strcmp(command, vtable[i].command) == 0) {
        (*vtable[i].callback)(data);
        return;
    }

Чтобы реализовать наследование, вам понадобится родительский указатель, и если вы упадете с конца цикла, вы перейдете к родителю и повторите логику. (Очевидно, что это может быть полезным местом для рекурсивной функции.)

person rra    schedule 31.03.2013