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_よいですか?key_callbackそのメンバー関数はWindowManagerWindowManagerクラスのメンバーになるため機能せず、クラスの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;
     }
}

とにかく、これはC ++クラスでGLFWを使用した場合の上位の結果の1つなので、glfwWindowをC ++クラスにカプセル化する方法も提供します。これは、グローバル、シングルトン、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 2016年

静的関数では、クラスの新しいインスタンス(つまりWindow *window )を作成しています。これはどのように問題を解決しますか?
クロコ

答えがいくつかの新しいC ++機能をサポートするように変更されていることに気付きました。関数の戻り値の型をautoに設定し、次に-> voidを使用してヒントを入力する利点はありますか?
ArmenB 2016

5

ご存知のように、コールバックはフリー関数または静的関数でなければなりません。コールバックGLFWwindow*は、自動thisポインターの代わりに最初の引数としてa を取ります。

GLFWを使用するglwSetWindowUserPointerと、およびglfwGetWindowUserPointerを使用して、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 2013

1
通常はunique_ptrではありませんが、シングルトンを使用することは珍しくありません。GLFWには、グローバルの必要性を回避できるウィンドウ用のユーザーデータ設定機能もあります。ほとんどの「良い」C APIは似たようなものを持っています。答えを更新して、実際のコンピューターに戻ったときにそれを示唆する可能性があります。
Sean Middleditch 2013
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.