Bir key_callback öğesini bir sarmalayıcı sınıf örneği ile nasıl ilişkilendirebilirim?


11

GLFW3 çağrılarımı tek bir sınıfa kaydırmaya çalışıyorum:

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

Ve yürütme sırasında tuşlara basan tek bir klavye sınıfı kurmaya çalışıyorum. GLFW'de birkey_callback sınıf tanımının dışında işlev (serbest işlev):

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

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

Nesne değerlerini WindowManagerayarlayabilmem için geri aramamı ve örneğimi nasıl ilişkilendirebilirim keyboard_? Ben yapamıyorum key_callbacküyesi işlevi WindowManagero işlevin beri değil çalışma WindowManager sınıfının üyesi ve bir sınıfın C ++ üye işlev isimleri dangled olsun olurdu çünkü.

Yanıtlar:


11

Buna benzer bir sorun yaşadım. GlfwSetWindowUserPointer ve glfGetWindowUserPointer kullanımıyla ilgili çok az belge olması can sıkıcı bir durum. Sorununuza benim çözümüm:

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

Her neyse, bu C ++ sınıfları ile GLFW kullanmak için en iyi sonuçlardan biri olduğu gibi, ben de bir C ++ sınıfında bir glfwWindow kapsülleme yöntemi sağlayacaktır. Ben bunu yapmak için en zarif yolu olduğunu düşünüyorum, çünkü globals, singletons veya unique_ptrs kullanmak zorunda kalmaz, programcı çok daha OO / C ++ - y tarzı pencereyi manipüle sağlar ve (sınıf pahasına) biraz daha karmaşık bir başlık dosyası).

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

Ve için:

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

Bu muhtemelen bir WindowManager / InputManager sınıfı ile oldukça kolay bir şekilde entegre edilebilir, ancak her pencerenin kendisini yönetmesinin daha kolay olduğunu düşünüyorum.


Birkaç yıl sonra geri döndüm ve güncellenmiş cevabı gördüm. Gerçekten iyi bir teşekkür ederim
ArmenB

Statik işlevlerde, bir sınıfın (ör. Window *window ) Yeni bir örneğini oluşturuyorsunuz . Bu sorunu nasıl çözer?
CroCo

Cevabın bazı yeni C ++ özelliklerini destekleyecek şekilde değiştiğini fark ettim. İşlev dönüş türünü otomatik olarak ayarlamanın ve -> void kullanarak ipucu yazmanın herhangi bir faydası var mı?
ArmenB

5

Geri aramalar, bulduğunuz gibi, serbest işlevler veya statik işlevler olmalıdır. Geri aramalar GLFWwindow*, otomatik thisişaretçi yerine ilk argüman olarak a alır .

GLFW ile kullanabilirsiniz glwSetWindowUserPointerve glfwGetWindowUserPointerdepolamak ve bir başvuru almak için WindowManagerya başına bir pencerede Windowörneği.

GLFW'nin saf C API'sı olduğu için sanal işlevleri herhangi bir doğrudan polimorfizm kullanmadığını unutmayın. Bu gibi API'ler her zaman serbest işlevleri varsayar (C'nin hiçbir sınıfı veya üye işlevi yoktur, sanal veya başka türlü) ve parametreler olarak (genellikle ilk parametre olarak; C hiçbiri yoktur this) açık "nesne örneklerini" iletir . İyi C API'leri aynı zamanda kullanıcı işaretçisi işlevselliğini de içerir (bazen diğer şeylerin yanı sıra "kullanıcı verileri" de denir), böylece globalleri kullanmanıza gerek kalmaz.

eski cevap:

WindowManager(Veya diğer sistemlerinizdeki) diğer verilere erişmeniz gerekiyorsa, geri aramalardan erişmek istiyorsanız bu bilgilere global olarak erişmeniz gerekebilir. Örneğin, std::unique_ptr<Engine>pencere yöneticinize erişmek için kullanabileceğiniz bir global oluşturun veya yalnızca bir global yapın std::unique_ptr<WindowManager>( std::unique_ptristerseniz "singletonlar için daha iyi" bir şeyle değiştirin ).

Birden fazla pencere desteği istiyorsanız, gereksinim duydukları verileri aramak için aldıkları Window std :: unordered_map GLFWwindow * `'u WindowManagereşleştirmek için bazı veri yapıları da içerir .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


Yardımın için teşekkürler. Böyle bir senaryoda, normal olarak bu şekilde mi işlenir (klavye girişlerini takip etmek için global bir unique_ptr kullanarak)? Bunun gibi küresel değişkenlerden kaçınmak istedim ve klavyeye ihtiyaç duyduğu her yerde sabit işaretçilerden geçmeyi tercih ettim ama bu mümkün değil gibi görünüyor, değil mi?
ArmenB

1
Genellikle unique_ptr değildir, ancak singleton kullanmak nadir değildir. GLFW ayrıca, global bir gereksinimi önleyebilecek pencereler için ayarlanmış bir kullanıcı veri işlevine sahiptir. Çoğu "iyi" C API'sinde benzer bir şey vardır. Gerçek bir bilgisayara geri döndüğümde bunu önermek için cevabı güncelleyebilir.
Sean Middleditch
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.