key_callback을 래퍼 클래스 인스턴스와 어떻게 연관시킬 수 있습니까?


11

GLFW3 호출을 단일 클래스로 래핑하려고합니다.

class WindowManager {
private:
    GLFWwindow* window_;
    GLFWmonitor* monitor_;
    Keyboard* keyboard_;
...
}

그리고 실행 중에 키 누름을 수집하는 싱글 톤 키보드 클래스를 설정하려고합니다. GLFW key_callback에서 클래스 정의 외부에있는 함수 (무료 함수)를 설정할 수 있습니다 .

WindowManager::WindowManager() {
    ...
    glfwSetKeyCallback(window_, key_callback);
    ...
}

// not a class function
void key_callback(GLFWwindow* window, int key, int scan code, int action, int mods) {
    ...
}

객체 값을 WindowManager설정할 수 있도록 콜백과 인스턴스를 어떻게 연결 keyboard_합니까? 해당 함수가 WindowManager 클래스의 멤버가되고 클래스의 C ++ 멤버 함수에서 이름이 바뀌므로 key_callback멤버 함수를 만들 수 없습니다 WindowManager.

답변:


11

나는 이것과 비슷한 문제가 있었다. glfwSetWindowUserPointer 및 glfGetWindowUserPointer 사용에 대한 문서가 거의 없다는 것이 귀찮습니다. 문제에 대한 나의 해결책은 다음과 같습니다.

WindowManager::WindowManager() {
    // ...
    glfwSetUserPointer(window_, this);
    glfwSetKeyCallback(window_, key_callback_);
    // ...
}

void WindowManager::key_callback(GLFWwindow *window, int, int ,int, int) {
    WindowManager *windowManager =
      static_cast<WindowManager*>(glfwGetUserPointer(window));
    Keyboard *keyboard = windowManager->keyboard_;

    switch(key) {
        case GLFW_KEY_ESCAPE:
             keyboard->reconfigure();
             break;
     }
}

어쨌든 이것은 C ++ 클래스와 함께 GLFW를 사용하는 최고의 결과 중 하나이므로 C ++ 클래스에서 glfwWindow를 캡슐화하는 방법도 제공합니다. 전역, singleton 또는 unique_ptrs를 사용하지 않아도되고 프로그래머가 훨씬 더 많은 OO / C ++-y 스타일로 창을 조작하고 하위 클래스를 만들 수 있기 때문에 이것이 가장 우아한 방법이라고 생각합니다. 약간 더 복잡한 헤더 파일).

// Window.hpp
#include <GLFW/glfw3.h>
class Window {
public:
    Window();
    auto ViewportDidResize(int w, int h)             -> void;
    // Make virtual you want to subclass so that windows have 
    // different contents. Another strategy is to split the
    // rendering calls into a renderer class.
    (virtual) auto RenderScene(void)                 -> void;
    (virtual) auto UpdateScene(double ms)            -> void;
    // etc for input, quitting
private:
    GLFWwindow *m_glfwWindow;

    // Here are our callbacks. I like making them inline so they don't take up
    // any of the cpp file
    inline static auto WindowResizeCallback(
        GLFWwindow *win,
        int w,
        int h) -> void {
            Window *window = static_cast<Window*>(glfwGetUserPointer(win));
            window->ViewportDidResize(w, h);
    }
    inline static auto WindowRefreshCallback(
        void) -> void {
            Window *window = static_cast<Window*>(glfwGetUserPointer(win));
            window->RenderScene(void);
    }
    // same for input, quitting
}

그리고:

// Window.cpp
#include <GLFW/glfw3.h>
#include "Window.hpp"
Window::Window() {
    // initialise glfw and m_glfwWindow,
    // create openGL context, initialise any other c++ resources
    glfwInit();
    m_glfwWindow = glfwCreateWindow(800, 600, "GL", NULL, NULL);        

    // needed for glfwGetUserPointer to work
    glfwSetWindowUserPointer(m_glfwWindow, this);

    // set our static functions as callbacks
    glfwSetFramebufferSizeCallback(m_glfwWindow, WindowResizeCallback);
    glfwSetWindowRefreshCallback(m_glfwWindow, WindowRefreshCallback);
}

// Standard window methods are called for each window
auto
Window::ViewportDidResize(int w, int h) -> void
{
    glViewport(0, 0, (GLsizei)w, (GLsizei)h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
}

이것은 아마도 WindowManager / InputManager 클래스와 쉽게 통합 될 수 있지만 각 창 자체를 관리하는 것이 더 쉽다고 생각합니다.


나는 몇 년 후에 돌아와서 업데이트 된 답변을 보았습니다. 정말 좋은 사람 감사합니다
ArmenB

정적 함수에서는 클래스의 새 인스턴스 (예 :)를 만들고 Window *window 있습니다. 이것이 어떻게 문제를 해결합니까?
CroCo

새로운 C ++ 기능을 지원하도록 답변이 변경되었음을 알았습니다. 함수 반환 유형을 자동으로 설정 한 다음-> void를 사용하여 힌트를 입력하면 어떤 이점이 있습니까?
ArmenB

5

알다시피 콜백은 자유 함수 또는 정적 함수 여야합니다. 콜백 GLFWwindow*은 자동 this포인터 대신 첫 번째 인수로 사용됩니다 .

GLFW하면 사용할 수 있습니다 glwSetWindowUserPointerglfwGetWindowUserPointer저장에 대한 참조 검색 WindowManager또는 당 윈도우 Window인스턴스를.

GLFW는 순수한 C API이기 때문에 어떤 종류의 직접 다형성도 가상 함수를 사용하지 않습니다. 이러한 API는 항상 자유 함수 (C에는 클래스 또는 멤버 함수가 전혀 없거나 가상 또는 기타가 없음)를 가정하고 명시 적 "객체 인스턴스"를 매개 변수 (일반적으로 첫 번째 매개 변수로, C에는 없음 this)로 전달합니다. 좋은 C API에는 사용자 포인터 기능 (때로는 "사용자 데이터"라고도 함)이 포함되어 있으므로 전역을 사용할 필요가 없습니다.

오래된 대답 :

WindowManager콜백에서 액세스하려는 경우 다른 시스템에 있는 다른 데이터에 액세스 해야하는 경우 전역으로 액세스 할 수 있어야합니다. 예를 들어, std::unique_ptr<Engine>창 관리자에 액세스하는 데 사용할 수 있는 전역 을 사용 하거나 전역 을 만들 수 있습니다 std::unique_ptr<WindowManager>( std::unique_ptr원하는 경우 "더 나은 톤"으로 대체 ).

다중 창 지원을 원하는 경우 필요한 데이터를 찾기 위해 수신 한 Window std :: unordered_map GLFWwindow *` WindowManager를 매핑하는 데이터 구조 도 포함 해야합니다.GLFWwindow*' values to your ownclasses in some way, e.g. using aor the like. Your callback could then access the global and query the datastructure using the


도와 주셔서 감사합니다. 이와 같은 시나리오에서 이것이 정상적으로 처리되는 방법입니까 (키보드 입력을 추적하기 위해 전역 unique_ptr 사용)? 나는 이와 같은 전역 변수를 피하고 싶었고 키보드의 const 포인터를 필요한 사람에게 전달하는 것을 선호했지만 이것이 불가능한 것 같습니다. 맞습니까?
ArmenB

1
일반적으로 unique_ptr은 아니지만 싱글 톤을 사용하는 것은 드문 일이 아닙니다. GLFW에는 전역에 대한 필요성을 피할 수있는 윈도우 용 사용자 데이터 설정 기능이 있습니다. 대부분의 "좋은"C API에는 비슷한 것이 있습니다. 실제 컴퓨터로 돌아올 때 제안하는 답변을 업데이트 할 수 있습니다.
Sean Middleditch
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.