Как использовать объект функции в качестве обратного вызова для вызова функций-членов (обработчиков) в объекте? - PullRequest
0 голосов
/ 10 июля 2019

Я хотел создать объект функции window_callback, который мог бы использовать в функциях glfwSet[...]Callback(). Он должен найти window в inline static std::unordered_map<GLFWwindow*, window&> windows_by_handles по его GLFWwindow* handle и затем вызвать соответствующий обработчик. Это мой текущий код:

template <typename... Args>
class window_callback
{
    typedef void(window::*member_func_t)(Args...);
    member_func_t func;
public:
    window_callback(member_func_t func_) :
        func(func_)
    {
    }

    void operator() (GLFWwindow* HWND, Args... args)
    {
        try
        {
            window& wnd = windows_by_handles.at(HWND);
            (wnd.*func)(args...); //error E0165
        }
        catch (const std::out_of_range&)
        {
            std::cerr << "Detected callback for unknown window\n";
        }
    }

    operator decltype(&(operator()))() { return &operator(); }
};

Объекты window_callback создаются следующим образом (внутри класса window):

inline static window_callback<int, int> resize_callback{ &on_resize };
inline static window_callback<double, double> mouse_movement_callback{ &on_mouse_moved };
inline static window_callback<double, double> scroll_callback{ &on_scrolled };

Затем они связаны с glfwSet[...]Callback() такими функциями (внутри конструктора класса window):

glfwSetFramebufferSizeCallback(handle, resize_callback);
glfwSetCursorPosCallback(handle, mouse_movement_callback);
glfwSetScrollCallback(handle, scroll_callback);

Данный код не компилируется. Это дает следующие ошибки при компиляции в Visual Studio 2019 (с использованием C ++ 17). Первые 2 ошибки происходят в объявлении пользовательского оператора преобразования:

  • C2833 «тип оператора» не является распознанным оператором или типом
  • Синтаксическая ошибка C2059: «новая строка»

  • C2664 GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow *,GLFWframebuffersizefun): невозможно преобразовать аргумент 2 из window::window_callback<int,int> в GLFWframebuffersizefun

  • C2664 GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow *,GLFWcursorposfun): невозможно преобразовать аргумент 2 из window::window_callback<double,double> в GLFWcursorposfun
  • C2664 GLFWscrollfun glfwSetScrollCallback(GLFWwindow *,GLFWscrollfun): невозможно преобразовать аргумент 2 из window::window_callback<double,double> в GLFWscrollfun

Проблемной частью является преобразование типа window_callback в соответствующие типы указателей на функции. Что нужно изменить, чтобы этот код работал или какое альтернативное решение будет работать таким же образом?

1 Ответ

2 голосов
/ 10 июля 2019

Как использовать объект функции в качестве обратного вызова для вызова функций-членов (обработчиков) в объекте?

, которые я мог бы использовать в функциях glfwSet [...] Callback ().

Glfw не принимает указатели на функции-члены в качестве функций обратного вызова и не принимает функциональные объекты.

Вы можете зарегистрировать только указатели на функции.Указатели на функции не могут указывать на нестатические функции-члены.Перегрузка оператора вызова функции для объекта функции также является функцией-членом.

Вам необходимо написать функцию, не являющуюся членом (или вы можете использовать статическую функцию-член, если хотите), которая оборачивает вызов для членафункция (или объект функции, если вы предпочитаете).

Пример:

struct C {
    void window_size(GLFWwindow*, int, int);
};

auto window_size_callback = [](GLFWwindow* window, int w, int h) {
    C c;
    c.window_size(window, w, h);
}; // a non-capturing lambda can be converted to a function pointer
   // you can use a named function if you prefer

glfwSetWindowSizeCallback(window, window_size_callback);

Но, вероятно, вам нужен доступ к какому-то конкретному экземпляру класса (или объекту функции), а непо умолчанию построен один.В конце концов, зачем использовать нестатическую функцию-член, если это не так.Чтобы достичь этого, вам нужен какой-то способ передачи ссылки на этот объект в обратный вызов.

Способ, задокументированный GLFW:

Каждое окно имеет указатель пользователя, который может бытьустанавливается с glfwSetWindowUserPointer и запрашивается с glfwGetWindowUserPointer.Это может быть использовано для любых целей, которые вам нужны, и не будет изменяться GLFW в течение всего срока службы окна.

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

C c;
glfwSetWindowUserPointer(window, &c);

auto window_size_callback = [](GLFWwindow* window, int w, int h) {
    C* cptr = static_cast<C*>(glfwSetWindowUserPointer(window));
    assert(c);
    cptr->window_size(window, w, h);
};

К сожалению, API GLFW не поддерживает специфичные для обратного вызова пользовательские данные.Если вам нужны разные объекты для разных обратных вызовов, вы можете использовать свою собственную карту от обратного вызова до объекта, но это не так просто, как в примере выше.

Кроме того, будьте очень осторожны, чтобы сохранить объект как минимум живымдо тех пор, пока окно живое, чтобы обратные вызовы не вызывались по висячему указателю.

...