在SDL3中,如何通过appstate传递一个指针数组?
这是一个简单的 SDL3helloworld.cpp 程序,能够成功渲染文本“Hello, World!”
#define SDL_MAIN_USE_CALLBACKS 1 // NOLINT
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
const int static WIDTH = 600;
const int static HEIGHT = 600;
const int static MAX = 255;
const float static SCALE = 4.0F;
const char static *MESSAGE = "Hello, World!"; // NOLINT
/* This function runs once at startup. */
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) { // NOLINT
SDL_Window *window = nullptr;
SDL_Renderer *renderer = nullptr;
/* Create the window */
if (!SDL_CreateWindowAndRenderer(MESSAGE, WIDTH, HEIGHT, SDL_WINDOW_RESIZABLE,
&window, &renderer)) {
SDL_Log("Couldn't create window and renderer: %s", // NOLINT
SDL_GetError()); // NOLINT
return SDL_APP_FAILURE;
}
*appstate = renderer;
return SDL_APP_CONTINUE;
}
/* This function runs once per frame, and is the heart of the program. */
SDL_AppResult SDL_AppIterate(void *appstate) { // NOLINT
auto *local_renderer = static_cast<SDL_Renderer *>(appstate);
int width = 0;
int height = 0;
/* Center the message and scale it up */
SDL_GetRenderOutputSize(local_renderer, &width, &height);
SDL_SetRenderScale(local_renderer, SCALE, SCALE);
const float xpos = ((static_cast<float>(width) / SCALE) -
(static_cast<float>(SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE) *
SDL_strlen(MESSAGE))) /
2;
const float ypos = ((static_cast<float>(height) / SCALE) -
SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE) /
2;
/* Draw the message */
SDL_SetRenderDrawColor(local_renderer, 0, 0, 0, MAX);
SDL_RenderClear(local_renderer);
SDL_SetRenderDrawColor(local_renderer, MAX, MAX, MAX, MAX);
SDL_RenderDebugText(local_renderer, xpos, ypos, MESSAGE);
SDL_RenderPresent(local_renderer);
return SDL_APP_CONTINUE;
}
/* This function runs when a new event (mouse input, keypresses, etc) occurs. */
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) { // NOLINT
if (event->type == SDL_EVENT_KEY_DOWN || event->type == SDL_EVENT_QUIT) {
return SDL_APP_SUCCESS; /* end the program, reporting success to the OS. */
}
return SDL_APP_CONTINUE;
}
/* This function runs once at shutdown. */
void SDL_AppQuit(void *appstate, SDL_AppResult result) {
} // NOLINT misc-use-anonymous-namespace
下面是我对参数 appstate 的理解,它是 SDL3 API 的一部分-
函数 SDL_AppInit 的第一个参数 appstate,预期是一个指向数组的指针。接近函数末尾时 -
*appstate = renderer;
指向 renderer 的指针被赋值给 appstate 数组的第一个元素。函数 SDL_AppIterate 通过第一个参数接收这个指针,然后 -
auto *local_renderer = static_cast<SDL_Renderer *>(appstate);
这个指针被强制转换为 local_renderer,它是指向同一渲染器的指针,该渲染器是在 SDL_AppInit 中创建的
接下来我尝试向 appstate 数组添加一个指向 window 的指针。我的做法是在 SDL_AppInit 中声明一个包含指向 renderer 和 window 的指针的数组,并通过 appstate 传递。在函数 SDL_AppInit 中,我声明了一个指向长度为2 的数组的指针 -
void *state[2];
然后用指向 renderer 和 window 的指针来填充该数组
state[0] = renderer;
state[1] = window;
现在我想把这个数组的指针赋给 appstate
appstate = (void **)&state;
问题在于,这个指针没有通过 appstate 参数在 SDL_AppIterate 中接收
下面是我修改后的Hello, World! 程序,但它不起作用 -
#define SDL_MAIN_USE_CALLBACKS 1 // NOLINT
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
const int static WIDTH = 600;
const int static HEIGHT = 600;
const int static MAX = 255;
const float static SCALE = 4.0F;
const char static *MESSAGE = "Hello, World!"; // NOLINT
/* This function runs once at startup. */
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) { // NOLINT
SDL_Window *window = nullptr;
SDL_Renderer *renderer = nullptr;
void *state[2];
/* Create the window */
if (!SDL_CreateWindowAndRenderer(MESSAGE, WIDTH, HEIGHT, SDL_WINDOW_RESIZABLE,
&window, &renderer)) {
SDL_Log("Couldn't create window and renderer: %s", // NOLINT
SDL_GetError()); // NOLINT
return SDL_APP_FAILURE;
}
state[0] = renderer;
state[1] = window;
appstate = (void **)&state;
return SDL_APP_CONTINUE;
}
/* This function runs once per frame, and is the heart of the program. */
SDL_AppResult SDL_AppIterate(void *appstate) { // NOLINT
// From the debugger I see appstate is empty, it should be pointer to the array passed in at the end of SDL_AppInit
auto **state = static_cast<void **>(appstate);
auto *local_renderer = static_cast<SDL_Renderer *>(state[0]);
int width = 0;
int height = 0;
/* Center the message and scale it up */
SDL_GetRenderOutputSize(local_renderer, &width, &height);
SDL_SetRenderScale(local_renderer, SCALE, SCALE);
const float xpos = ((static_cast<float>(width) / SCALE) -
(static_cast<float>(SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE) *
SDL_strlen(MESSAGE))) /
2;
const float ypos = ((static_cast<float>(height) / SCALE) -
SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE) /
2;
/* Draw the message */
SDL_SetRenderDrawColor(local_renderer, 0, 0, 0, MAX);
SDL_RenderClear(local_renderer);
SDL_SetRenderDrawColor(local_renderer, MAX, MAX, MAX, MAX);
SDL_RenderDebugText(local_renderer, xpos, ypos, MESSAGE);
SDL_RenderPresent(local_renderer);
return SDL_APP_CONTINUE;
}
/* This function runs when a new event (mouse input, keypresses, etc) occurs. */
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) { // NOLINT
if (event->type == SDL_EVENT_KEY_DOWN || event->type == SDL_EVENT_QUIT) {
return SDL_APP_SUCCESS; /* end the program, reporting success to the OS. */
}
return SDL_APP_CONTINUE;
}
/* This function runs once at shutdown. */
void SDL_AppQuit(void *appstate, SDL_AppResult result) {
} // NOLINT misc-use-anonymous-namespace
我到底哪里出错了?
解决方案
在 SDL_AppInit(void **appstate,... 中,你可以返回指向某些用户自定义信息的指针。当只返回一个指向 SDL_Renderer 的指针时,你不需要进行任何内存分配(因为SDL框架内部保存着 appstate 指向的指针)。
如果你需要返回两个指针,就需要为它们分配空间。示例:
struct RendererAndWindow {
SDL_Renderer* m_renderer;
SDL_Window* m_window;
};
SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]) {
SDL_Window* window = nullptr;
SDL_Renderer* renderer = nullptr;
if(!SDL_CreateWindowAndRenderer(MESSAGE, WIDTH, HEIGHT,
SDL_WINDOW_RESIZABLE, &window, &renderer)) {
SDL_Log("Couldn't create window and renderer: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
*appstate = new RendererAndWindow{renderer, window}; // allocate
return SDL_APP_CONTINUE;
}
然后在 SDL_AppIterate:
SDL_AppResult SDL_AppIterate(void* appstate) { // NOLINT
auto raw = static_cast<RendererAndWindow*>(appstate);
auto local_renderer = raw->m_renderer;
//...
注意:在关闭时需要 delete 已分配的 RendererAndWindow,以避免内存泄漏。