Функция обратного вызова в фриглуте от объекта

Я использую MSVC++ и freeglut для использования openGL. Теперь у меня есть класс Camera, который довольно прост, но также содержит функцию изменения формы моего окна.

Мой вопрос: как я могу установить glutReshapeFunc(void (*callback)(int,int)) на свою функцию в моей камере?

У меня есть следующий код, который не будет работать из-за ошибки компилятора:

int main(int argc, char **argv)
{
    Camera *camera = new Camera();
    glutReshapeFunc(camera->ReshapeCamera);
}

и мой класс Camera выглядит так в Camera.h:

class Camera
{
public:
    Camera(void);
    ~Camera(void);
    void ReshapeCamera(int width, int height);
};

Может быть, это просто более общий вопрос обратного вызова, но единственное, что я нашел в Интернете, это создание некоторого класса-оболочки для обратного вызова. Но не похоже, что это должно быть так сложно. Заранее спасибо.


person Marnix    schedule 15.02.2011    source источник


Ответы (4)


Нет чистого способа сделать это. C ничего не знает об объектах, поэтому указатели на члены не работают. C не использует шаблоны, поэтому функторы не работают. API даже не позволяет вам передать произвольный void *userData вашему обратному вызову, поэтому вы также не можете передавать объект таким образом.

Итак, действительно, вам придется создать функцию-оболочку, которая не является функцией-членом, и вам придется каким-то образом предоставить ей доступ к экземпляру объекта Camera через статическую и/или глобальную переменную.

Если это событие может прослушиваться несколькими объектами, вы можете создать одноэлементный класс, который позволит вам регистрировать произвольные прослушиватели.

person Thomas    schedule 15.02.2011

Вы не можете подключить его напрямую. ReshapeCamera — это функция-член, для запуска которой требуется экземпляр камеры. Обратный вызов переполнения — это функция C, у которой нет экземпляра камеры для вызова вашего метода. Вам нужно создать функцию, которая вызывает метод изменения формы камеры.

person fbafelipe    schedule 15.02.2011

Это не имеет ничего общего с "C vs C++". Вам просто нужно понять, что влечет за собой вызов функции-члена и, в основном, во что он компилируется.

myObject.memberFunction(); // This is what the programmer sees.
memberFunction(&myObject); // This is what the compiler sees.

Функция-член — это причудливое описание функции, которая просто принимает объект в качестве первого параметра. Это просто невидимо в фактическом списке параметров.

void MyClass::memberFunction() // This is the declaration the programmer sees.
void memberFunction(MyClass* this) // This is what the compiler sees.

Оттуда C++ добавляет специальную семантику, упрощающую работу в объектно-ориентированном стиле. Таким образом, даже если ваш объект имеет функцию-член формата void(int, int), на самом деле он имеет формат void(Camera*, int, int), а это не соответствует желаемому формату! Вот почему члены статических функций могут использоваться для таких указателей на функции. Вы когда-нибудь замечали, что статические функции-члены не могут получить доступ к членам «этого»? Это связано с тем, что статические функции-члены не передают экземпляр объекта, представляющего "это".

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

person TheBuzzSaw    schedule 15.02.2011
comment
Итак, я создал статическую функцию в своем классе, но похоже, что она не может преобразовать __thiscall в __decl. Error 1 error C2664: 'glutReshapeFunc' : cannot convert parameter 1 from 'void (__thiscall Camera::* )(int,int)' to 'void (__cdecl *)(int,int)'. И добавление явного __cdecl дает предупреждение о части Camera::. - person Marnix; 16.02.2011
comment
@Marnix: __thiscall подсказывает мне, что glutReshapeFunc не объявлено как static. - person Thomas; 16.02.2011
comment
@Thomas: Нет, glutReshapeFunc, вероятно, является функцией в C, функция, исходящая из моего класса Camera, является статической, но поэтому имеет другое пространство имен. Так что, похоже, этого не понять. - person Marnix; 16.02.2011
comment
glutReshapeFunc(Camera::ReshapeCamera); - person TheBuzzSaw; 27.02.2011

Ниже показано, как зарегистрировать функцию обратного вызова c из C++. Обычно она полезна, а не специфична для перенасыщения.

Вот ваша клиентская программа на С++

int main(int argc, char *argv[]) {

std::cout << "launching Camera ..." << std::endl;

Camera * camera = new Camera();

// ------ glut new window boilerplate ------- //

int WindowHandle = 0;
glutInit(&argc, argv);
WindowHandle = glutCreateWindow("hello there");
if(WindowHandle < 1) {
    std::cerr << "ERROR: Could not create a new rendering window" << std::endl;
    exit(EXIT_FAILURE);
}
// ------------------------------------------ //

camera->setup_callback();

glutMainLoop();

return 0;

}

Вот камера.cpp

Camera * ptr_global_instance = NULL;

extern "C" void ReshapeCamera_callback(int width, int height) {
    // c function call which calls your c++ class method
    ptr_global_instance->ReshapeCamera_cb(width, height);
}

void Camera::ReshapeCamera_cb(int width, int height) {
    std::cout << "width " << width << " height " << height << std::endl;
}

void Camera::setup_callback() {
    // c++ method which registers c function callback
    ::ptr_global_instance = this;
    ::glutReshapeFunc(::ReshapeCamera_callback);
}

и его заголовок Camera.h

class Camera {
public:
    void ReshapeCamera_cb(int width, int height);
    void setup_callback();
};

Обратите внимание на использование указателя глобального класса C++ ptr_global_instance.

person Scott Stensland    schedule 04.08.2013