2012-03-20 2 views
3

Так что, ради «весело», я решил эмулировать функции-члены C++ в C с использованием функций указателя. Вот простой код:Использование функций указателя для эмулирования функций-членов в C-структурах

obj.h:

#ifndef OBJ_H 
#define OBJ_H 

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

struct Obj{ 
    struct pObjVar* pVar; 

    void (*read)(struct Obj*); 
    void (*set) (struct Obj*, int); 
}; 

struct Obj* newObj(); 
void deleteObj(struct Obj** obj); 

#endif 

obj.c:

#include "obj.h" 

void readValue(struct Obj* this_); 
void setValue (struct Obj* this_, int mValue_); 

struct pObjVar{ 
    int mValue; 
}; 

struct Obj* newObj(){ 
    struct Obj* tmp = (struct Obj*)  malloc(sizeof(struct Obj)); 
    tmp->pVar  = (struct pObjVar*) malloc(sizeof(struct pObjVar)); 

    tmp->pVar->mValue = 0; 
    tmp->read = readValue; 
    tmp->set = setValue; 

    return tmp; 
} 

void deleteObj(struct Obj **obj){ 
    free((*obj)->pVar); (*obj)->pVar = NULL; 
    free((*obj)); *obj = NULL; 
} 

void readValue(struct Obj *this_){ 
    printf("Value = %d\n",this_->pVar->mValue); 
} 

void setValue(struct Obj *this_, int mValue_){ 
    this_->pVar->mValue = mValue_; 
} 

main.c:

#include "obj.h" 

int main(void) 
{ 
    struct Obj* a = newObj(); 
    a->set(a, 10); 
    a->read(a); 
    deleteObj(&a); 

    return 0; 
} 

Выход:

>./a.out 
Value = 10  

При этом я решил, что мне нужно было эмулировать роль неявного указателя this, явно передавая его моим функциям-членам. Это прекрасно работает, я думаю, кроме того, что все это выглядит странно!

Если бы я хотел передать объект, почему бы реализовать функции как функции-члены? Единственный ответ, который я нашел, это, возможно, в тех случаях, когда вы хотели бы иметь унифицированный интерфейс, но различные реализации? (что-то похоже на C++ виртуальные функции?)

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

EDIT: При передаче объекта возникла проблема с исходным кодом. Я использовал &a по ошибке для функций read/set. Вам понадобится только для deleteObj, если вы хотите установить указатель на NULL внутри.

+1

Вы передаете _address_ указателя на функции, то есть 'Obj **'. Пропустите '&' в вызовах функций. –

+0

@JoachimPileborg: Вы правы.Пожалуйста, см. Мое редактирование. – GradGuy

+0

Мне когда-то приходилось поддерживать унаследованный проект с использованием аналогичного подхода со сложной иерархией наследования и добавленным извращением использования непрозрачных значений intpedef't, а не указателей на структуры. Излишне говорить, что это был кошмар для отладки/поддержки! – SirDarius

ответ

3

Еще один способ написания:

#define member(FUNC, ...) FUNC(this_, ## __VA_ARGS__) 
int reader(struct Obj *_this) { 
    member(read, a, b, c); 
    member(getch); 
    return 0; 
} 

Это может быть использовано для реализации интерфейсов, наследования и множество функций C++, которые были реализованы, как это в C с классами времени. В ядре Linux операции с файлами выполняются следующим образом. В файловой структуре хранятся указатели на функции, так что каждая файловая система может хранить собственные обработчики системных вызовов, которые работают с/с данными в структуре.

1

Нет, это невозможно сделать автоматически в C. Стандартный препроцессор недостаточно компетентен для выполнения преобразований.

Существует также способ найти функцию, которая была вызвана как a->func(10). Внутри функции это всего лишь func(10).

Когда Бьярне Страуструп начал разрабатывать C++, он написал специальный препроцессор/компилятор Cfront для этого.

В действительности, C++ на самом деле не хранит указатели на (не виртуальные) функции. Он просто преобразует a->set(10) в нечто вроде struct_Obj_set(a, 10) при компиляции кода.