В настоящее время я реорганизую свой код в класс (VkAPP
) и наткнулся на эту ошибку:
аргумент типа "void (VkAPP::)(GLFWwindow window, int width, int height)" несовместим с параметром типа "GLFWwindowsizefun"
Вот рассматриваемая функция:
void onWindowResized(GLFWwindow* window, int width, int height) {
VkSurfaceCapabilitiesKHR surfaceCapabilities;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevices[0], surface, &surfaceCapabilities);
if (width > surfaceCapabilities.maxImageExtent.width) width = surfaceCapabilities.maxImageExtent.width;
if (height > surfaceCapabilities.maxImageExtent.height) height = surfaceCapabilities.maxImageExtent.height;
if (width == 0 || height == 0) return;
WIDTH = width;
HEIGHT = height;
recreateSwapchain();
}
Вот звонок:
glfwSetWindowSizeCallback(window, onWindowResized);
Функция и вызов находятся в одном классе.
Если я превращу это в:
static void onWindowResized
Я получил:
нестатическая ссылка на член должна быть относительно определенного объекта
для physicalDevices[0]
, surface
, WIDTH
, HEIGHT
и recreateSwapchain()
В качестве обходного пути, если вы можете установить какой-то «пользовательские данные» или «указатель пользовательских данных», вы можете использовать функцию-член static
. Установите указатель пользовательских данных на указатель на объект и используйте статическую функцию-член в качестве обратного вызова. Это работает, потому что статическим функциям-членам не нужен объект. Затем статическая функция-член извлекает указатель пользовательских данных, использует reinterpret_cast
, чтобы привести его к правильному типу объекта, и вызывает фактическую функцию-член.
@StarFox Делай то, что сказал Someprogrammerdude. Вы можете использовать glfwSetWindowUserPointer
и glfwGetWindowUserPointer
для передачи «пользовательских данных» в статический обратный вызов.
Прототип функции-члена
void onWindowResized(GLFWwindow* window, int width, int height);
на самом деле выглядит примерно так
void onWindowResized(
MyClass *this, //the famously infamous 'this' pointer
GLFWwindow* window,
int width,
int height
);
Чтобы использовать функцию в качестве обратного вызова, она должна быть либо глобальной, либо объявленной статической внутри класса.
Чтобы связать окно с конкретными данными, GLFW предоставляет механизм, с помощью которого это можно сделать:
void glfwSetWindowUserPointer(GLFWwindow *window, void *pointer);
и получить его через
void* glfwGetWindowUserPointer(GLFWwindow *window);
Чтобы все заработало, вам придется внести некоторые изменения:
//either global or inside class
static void onWindowResized(GLFWwindow* window, int width, int height)
{
//get your class instance
MyClass *obj = reinterpret_cast<MyClass*>(glfwGetWindowUserPointer(window));
//use the memebers as usual
//obj->physicalDevices[0];
//obj->surface;
//obj->WIDTH;
//obj->HEIGHT;
//obj->recreateSwapchain();
//note:
//if function is in global scope, the members have to be public
//if inside the class, private members can be accessed
}
не забудьте связать экземпляр вашего класса с окном:
//somewhere in your code (but before the callback gets invoked)
glfwSetWindowUserPointer(window, /* MyClass* */ obj);
Примечание. Внимательно следите за сроком жизни экземпляра класса obj
.
Начнем с первого сообщения об ошибке, которое вы получаете.
GLFWwindowsizefun
определяется следующим образом:
typedef void(* GLFWwindowsizefun) (GLFWwindow *window, int width, int height)
С другой стороны, (нестатический) метод onWindowResized
вашего класса C++ имеет неявный указатель на экземпляр класса.
Другими словами, нестатический VkAPP
метод вашего onWindowResized
класса C++ объявлен следующим образом:
// Inside VkAPP class
void onWindowResized(GLFWwindow* window, int width, int height)
имеет неявный параметр, который является указателем на экземпляр класса VkAPP
(т. е. классический указатель this
в C++):
void VkAPP::onWindowResized(VkAPP* this, /* implicit VkAPP* 'this' pointer */ ,
GLFWwindow* window,
int width,
int height)
Итак, компилятор C++ сообщил вам о несоответствии между ожидаемым GLFWwindowsizefun
(у которого нет неявного указателя this
) и вашим методом VkAPP::onWindowResized
(у которого есть неявный указатель this
).
Как можно устранить это несоответствие?
Что ж, вы можете определить статический метод в своем классе VkAPP
C++, например:
// Inside VkAPP class
//
// Note: the method is *static*
static void onWindowResizedStatic(GLFWwindow* window, int width, int height)
Обратите внимание, что методы static
не имеют неявного указателя this
, поэтому приведенный выше метод соответствует GLFWwindowsizefun
typedef
.
Таким образом, вы можете передать его вызову функции glfwSetWindowSizeCallback
:
// You had this line, which didn't work as onWindowResized was not static:
//
// glfwSetWindowSizeCallback(window, onWindowResized); <<-- error
//
// Pass a *static* method as callback, instead:
//
glfwSetWindowSizeCallback(window, onWindowResizedStatic);
К сожалению, как было показано в другом сообщении об ошибке, вы не можете получить доступ к элементам данных, специфичным для экземпляра, таким как physicalDevices[0]
, surface
, WIDTH
, HEIGHT
и т. д., из статического метода вашего класса.
Итак, идея здесь состоит в том, чтобы использовать метод static
как место для перенаправления вызова на метод вашего класса, специфичный для экземпляра, например:
// Inside VkAPP class
static void onWindowResizedStatic(GLFWwindow* window, int width, int height) {
// Somehow get the instance-specific "this" pointer
VkAPP* pThis = ... /* we'll see that later */;
// Forward the call to the non-static method callback:
pThis->onWindowResized(window, width, height);
}
Теперь, как получить указатель this
из метода static
выше?
Что ж, во время инициализации (например, в конструкторе вашего класса C++ или в другом методе инициализации), как только у вас есть действительный объект GLFWwindow
(например, созданный с помощью glfwCreateWindow
), вы можете вызвать функцию glfwSetWindowUserPointer
, чтобы установить специфичный для экземпляра this
указатель как общий «пользовательский указатель», связанный с объектом окна:
// Inside a method of your VkAPP C++ class, during initialization:
// Set the "this" pointer as window's user pointer for later retrieval
glfwSetWindowUserPointer(window, this);
Затем указатель this
можно позже получить, вызвав симметричный glfwGetWindowUserPointer
:
// Inside VkAPP class
static void onWindowResizedStatic(GLFWwindow* window, int width, int height) {
// Get the instance-specific "this" pointer
// that was set before during initialization
VkAPP* pThis = static_cast<VkAPP*>(glfwGetWindowUserPointer(window));
// Forward the call to the non-static method callback:
pThis->onWindowResized(window, width, height);
}
Обратите внимание: поскольку ваш метод onWindowResized
не является статическим, вы можете использовать все элементы данных, специфичные для экземпляра, такие как physicalDevices[0]
, surface
и т. д., или вызывать другие функции-члены, такие как recreateSwapchain()
, на которые жаловались во втором сообщении об ошибке.
Это отлично работает! но если я поставлю его перед созданием окна, оно не будет работать и вернет значение Null в pThis.
@StarFox: Конечно, у вас уже должен быть действительный объект GLFWwindow
, чтобы вызвать glfwSetWindowUserPointer
и установить пользовательский указатель пользовательских данных. Я добавил разъяснение по этому поводу в своем ответе.
Для тех, кто ищет быстрый awnser:
/* Resizes the Window when Size changes */
static void windowResizeStatic(GLFWwindow* window, int width, int height) {
Engine* pThis = static_cast<Engine*>(glfwGetWindowUserPointer(window));
pThis->onWindowResized(width, height);
}
void onWindowResized(int width, int height) {
/* rest of the function cut out */
}
void startGLFW() {
glfwInit();
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
window = glfwCreateWindow(WIDTH, HEIGHT, "Window Name", nullptr, nullptr);
//set the glfwSetWindowUserPointer here or it returns a nullptr
glfwSetWindowUserPointer(window, this);
glfwSetWindowSizeCallback(window, windowResizeStatic);
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
}
GLFW реализован на C. Он понятия не имеет о таких вещах, как классы и объекты. Указатели на функции, которые он использует, соответствуют функциям, не являющимся членами, в C++. Функция-член нуждается в объекте для вызова, и поэтому указатель на функцию-член — это не то же самое, что указатель на функцию, не являющуюся членом.