ฉันจะเชื่อมโยง key_callback กับอินสแตนซ์คลาสของ wrapper ได้อย่างไร


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_วัตถุได้อย่างไร ฉันไม่สามารถสร้างkey_callbackฟังก์ชั่นสมาชิกของWindowManagerเพราะจะไม่ทำงานเนื่องจากฟังก์ชั่นนั้นจะเป็นสมาชิกของคลาส WindowManager และในฟังก์ชั่นสมาชิก C ++ ของชั้นเรียนได้รับชื่อห้อยต่องแต่ง

คำตอบ:


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;
     }
}

อย่างไรก็ตามเนื่องจากเป็นหนึ่งในผลลัพธ์อันดับต้น ๆ สำหรับการใช้ GLFW กับคลาส C ++ ฉันจะให้วิธีการของฉันในการห่อหุ้ม glfwWindow ในคลาส C ++ ฉันคิดว่านี่เป็นวิธีที่ดีที่สุดในการทำเพราะหลีกเลี่ยงการใช้ globals, singletons หรือ 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 ++ มีประโยชน์ในการตั้งค่าประเภทการคืนค่าฟังก์ชั่นเป็นอัตโนมัติแล้วพิมพ์คำใบ้โดยใช้ -> โมฆะ?
ArmenB

5

การเรียกกลับต้องเป็นฟังก์ชันอิสระหรือฟังก์ชันคงที่ตามที่คุณค้นพบ การเรียกกลับใช้GLFWwindow*เป็นอาร์กิวเมนต์แรกแทนที่thisตัวชี้อัตโนมัติ

ด้วย GLFW คุณสามารถใช้glwSetWindowUserPointerและglfwGetWindowUserPointerจัดเก็บและดึงข้อมูลอ้างอิงWindowManagerหรือWindowอินสแตนซ์ต่อหน้าต่างได้

โปรดจำไว้ว่า GLFW ไม่ได้ใช้ฟังก์ชั่นเสมือนใด ๆ ที่มีความหลากหลายในรูปแบบโดยตรงเนื่องจากเป็น C API บริสุทธิ์ API ดังกล่าวจะถือว่าฟังก์ชั่นฟรี (C ไม่มีคลาสหรือฟังก์ชั่นสมาชิกเลยเสมือนหรืออย่างอื่น) และผ่าน "อินสแตนซ์ของวัตถุ" เป็นพารามิเตอร์อย่างชัดเจน (โดยปกติจะเป็นพารามิเตอร์แรก; C ไม่มีthis) Good C APIs ยังรวมถึงฟังก์ชั่นตัวชี้ผู้ใช้ (บางครั้งเรียกว่า "ข้อมูลผู้ใช้" เหนือสิ่งอื่นใด) ดังนั้นคุณไม่จำเป็นต้องใช้ globals

คำตอบเก่า:

หากคุณต้องการเข้าถึงข้อมูลอื่นในWindowManager(หรือระบบอื่น ๆ ) ของคุณคุณอาจต้องเข้าถึงข้อมูลเหล่านั้นได้ทั่วโลกหากคุณต้องการเข้าถึงข้อมูลจากการโทรกลับ ตัวอย่างเช่นมีโกลบอลstd::unique_ptr<Engine>ที่คุณสามารถใช้เพื่อเข้าถึงตัวจัดการหน้าต่างหรือเพียงแค่สร้างโกลบอลstd::unique_ptr<WindowManager>(แทนที่std::unique_ptrด้วยบางสิ่งที่ "ดีกว่าสำหรับซิงเกิลตัน" หากคุณต้องการ)

หากคุณต้องการการรองรับหลายหน้าต่างคุณจะต้องWindowManagerมีโครงสร้างข้อมูลบางอย่างเพื่อจับคู่GLFWwindow*' values to your ownWindow classes in some way, e.g. using astd :: unordered_map or the like. Your callback could then access the global and query the datastructure using theGLFWwindow * `พวกเขาได้รับเพื่อค้นหาข้อมูลที่ต้องการ


ขอขอบคุณสำหรับความช่วยเหลือของคุณ. ในสถานการณ์เช่นนี้นี่เป็นวิธีการจัดการโดยปกติหรือไม่ (ใช้ global unique_ptr เพื่อติดตามการป้อนข้อมูลด้วยแป้นพิมพ์) ฉันต้องการหลีกเลี่ยงตัวแปรระดับโลกเช่นนี้และต้องการที่จะส่งผ่านพอยน์เตอร์ของแป้นพิมพ์ไปยังคนที่มันต้องการ แต่มันฟังดูเป็นไปไม่ได้ใช่มั้ย
ArmenB

1
ไม่ใช่โดยทั่วไปจะเป็น unique_ptr แต่ไม่ใช่เรื่องแปลกที่จะใช้ซิงเกิล GLFW ยังมีฟังก์ชั่นการตั้งค่าข้อมูลผู้ใช้สำหรับ windows ที่สามารถหลีกเลี่ยงความต้องการทั่วโลก APIs "ดี" ส่วนใหญ่มีบางอย่างที่คล้ายกัน อาจมีคำตอบให้อัปเดตเพื่อแนะนำว่าเมื่อฉันกลับไปที่คอมพิวเตอร์จริง
Sean Middleditch
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.