【Qt SDL相关问题】Qt 引入SDL导致main函数冲突的解决方案

简介: 【Qt SDL相关问题】Qt 引入SDL导致main函数冲突的解决方案

常见错误

报错详细

SDL/include/SDL_main.h:143: warning: "main" redefined
  143 | #define main    SDL_main

解决方案

这个问题是由于 SDL 库中的 SDL_main.h 文件中定义了一个名为 main 的宏,这与你的程序中的 main 函数产生了冲突。

你可以尝试以下的解决方案:

  1. 确保 #define SDL_MAIN_HANDLED 在包含任何 SDL 头文件之前。这是因为 SDL_MAIN_HANDLED 宏需要在包含 SDL.hSDL_main.h 之前定义,以防止 main 宏的定义。你的代码应该像这样:
#define SDL_MAIN_HANDLED
#include "SDL.h"
  1. 如果上述方法仍然无法解决问题,你可以尝试在包含 SDL 头文件之后立即取消 main 宏的定义:
#include "SDL.h"
#undef main

这样可以确保在你的代码中,main 不再被 SDL 的宏定义所覆盖。

解析原因

通常情况下,一个程序只能有一个 main 函数。然而,SDL 使用了一种技巧,通过宏定义将用户的 main 函数重命名为 SDL_main,然后提供自己的 main 函数。这样,当你在你的代码中写 main 函数时,预处理器实际上会将其重命名为 SDL_main

这是在 SDL 的 SDL_main.h 头文件中定义的宏:

#define main SDL_main
• 1

这意味着当你在你的代码中写如下的 main 函数:

int main(int argc, char* argv[]) {
    // 你的代码
}

预处理器实际上会将其转换为:

int SDL_main(int argc, char* argv[]) {
    // 你的代码
}

然后,SDL 提供了自己的 main 函数,这个函数会先进行一些初始化工作,然后调用 SDL_main(也就是你的 main 函数),最后进行清理工作。

如果你不希望 SDL 重定义你的 main 函数,你可以在包含 SDL 的头文件之前定义 SDL_MAIN_HANDLED 宏,然后在你的 main 函数中手动调用 SDL 的初始化和清理函数。

关于SDL的初始化

前面我们已经知道SDL会通过宏来调用自己的main函数,那么它做了什么呢?

具体来说,SDL 的 main 函数通常会做以下几件事情:

  1. 初始化 SDL 库:这包括初始化视频子系统、音频子系统、事件子系统等。
  2. 调用用户的 main 函数:在完成初始化之后,SDL 的 main 函数会调用 SDL_main(也就是用户的 main 函数)。
  3. 清理 SDL 库:在用户的 main 函数返回之后,SDL 的 main 函数会清理 SDL 库,包括关闭视频子系统、音频子系统、事件子系统等。

这种设计方式允许 SDL 在用户的 main 函数执行之前和之后插入自己的代码,而不需要用户在他们的 main 函数中显式地调用 SDL 的初始化和清理函数。

理论上没问题,为什么很多时候会报警告?

在 Qt 中,main 函数通常被用来创建一个 QApplication 对象,并启动 Qt 的事件循环。当 SDL 试图通过宏定义来重定义 main 函数时,这可能会导致 Qt 的初始化代码被错误地放在 SDL_main 函数中,而不是真正的 main 函数中,从而导致问题。

此外,Qt 的 moc (Meta-Object Compiler) 也可能会对 main 宏的重定义产生警告。moc 是 Qt 的一个工具,用于处理 Qt 的元对象系统。它会扫描源代码中的类定义,查找那些使用了 Qt 的特性(如信号和槽)的类,并为这些类生成额外的代码。如果 main 被定义为宏,moc 可能会在处理源代码时产生警告。

因此,如果你在 Qt 程序中使用 SDL,最好的做法是在包含 SDL 的头文件之前定义 SDL_MAIN_HANDLED 宏,然后在你的 main 函数中手动调用 SDL 的初始化和清理函数。这样可以避免 main 宏的重定义,同时确保 Qt 和 SDL 的初始化代码都在正确的位置。

SDL main函数相关源码

/*
    SDL_windows_main.c, placed in the public domain by Sam Lantinga  4/13/98
    The WinMain function -- calls your program's main() function
*/
#include "SDL_config.h"
#ifdef __WIN32__
/* Include this so we define UNICODE properly */
#include "../../core/windows/SDL_windows.h"
#include <shellapi.h> /* CommandLineToArgvW() */
/* Include the SDL main definition header */
#include "SDL.h"
#include "SDL_main.h"
#ifdef main
#undef main
#endif /* main */
/* Pop up an out of memory message, returns to Windows */
static BOOL OutOfMemory(void)
{
    SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal Error", "Out of memory - aborting", NULL);
    return FALSE;
}
#if defined(_MSC_VER)
/* The VC++ compiler needs main/wmain defined */
#define console_ansi_main main
#if UNICODE
#define console_wmain wmain
#endif
#endif
/* Gets the arguments with GetCommandLine, converts them to argc and argv
   and calls SDL_main */
static int main_getcmdline(void)
{
    LPWSTR *argvw;
    char **argv;
    int i, argc, result;
    argvw = CommandLineToArgvW(GetCommandLineW(), &argc);
    if (argvw == NULL) {
        return OutOfMemory();
    }
    /* Note that we need to be careful about how we allocate/free memory here.
     * If the application calls SDL_SetMemoryFunctions(), we can't rely on
     * SDL_free() to use the same allocator after SDL_main() returns.
     */
    /* Parse it into argv and argc */
    argv = (char **)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (argc + 1) * sizeof(*argv));
    if (argv == NULL) {
        return OutOfMemory();
    }
    for (i = 0; i < argc; ++i) {
        DWORD len;
        char *arg = WIN_StringToUTF8W(argvw[i]);
        if (arg == NULL) {
            return OutOfMemory();
        }
        len = (DWORD)SDL_strlen(arg);
        argv[i] = (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (size_t)len + 1);
        if (!argv[i]) {
            return OutOfMemory();
        }
        SDL_memcpy(argv[i], arg, len);
        SDL_free(arg);
    }
    argv[i] = NULL;
    LocalFree(argvw);
    SDL_SetMainReady();
    /* Run the application main() code */
    result = SDL_main(argc, argv);
    /* Free argv, to avoid memory leak */
    for (i = 0; i < argc; ++i) {
        HeapFree(GetProcessHeap(), 0, argv[i]);
    }
    HeapFree(GetProcessHeap(), 0, argv);
    return result;
}
/* This is where execution begins [console apps, ansi] */
int console_ansi_main(int argc, char *argv[])
{
    return main_getcmdline();
}
#if UNICODE
/* This is where execution begins [console apps, unicode] */
int console_wmain(int argc, wchar_t *wargv[], wchar_t *wenvp)
{
    return main_getcmdline();
}
#endif
/* This is where execution begins [windowed apps] */
int WINAPI MINGW32_FORCEALIGN
WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw) /* NOLINT(readability-inconsistent-declaration-parameter-name) */
{
    return main_getcmdline();
}
#endif /* __WIN32__ */
/* vi: set ts=4 sw=4 expandtab: */
/*
  Simple DirectMedia Layer
  Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
  This software is provided 'as-is', without any express or implied
  warranty.  In no event will the authors be held liable for any damages
  arising from the use of this software.
  Permission is granted to anyone to use this software for any purpose,
  including commercial applications, and to alter it and redistribute it
  freely, subject to the following restrictions:
  1. The origin of this software must not be misrepresented; you must not
     claim that you wrote the original software. If you use this software
     in a product, an acknowledgment in the product documentation would be
     appreciated but is not required.
  2. Altered source versions must be plainly marked as such, and must not be
     misrepresented as being the original software.
  3. This notice may not be removed or altered from any source distribution.
*/
#ifndef SDL_main_h_
#define SDL_main_h_
#include "SDL_stdinc.h"
/**
 *  \file SDL_main.h
 *
 *  Redefine main() on some platforms so that it is called by SDL.
 */
#ifndef SDL_MAIN_HANDLED
#if defined(__WIN32__)
/* On Windows SDL provides WinMain(), which parses the command line and passes
   the arguments to your main function.
   If you provide your own WinMain(), you may define SDL_MAIN_HANDLED
 */
#define SDL_MAIN_AVAILABLE
#elif defined(__WINRT__)
/* On WinRT, SDL provides a main function that initializes CoreApplication,
   creating an instance of IFrameworkView in the process.
   Please note that #include'ing SDL_main.h is not enough to get a main()
   function working.  In non-XAML apps, the file,
   src/main/winrt/SDL_WinRT_main_NonXAML.cpp, or a copy of it, must be compiled
   into the app itself.  In XAML apps, the function, SDL_WinRTRunApp must be
   called, with a pointer to the Direct3D-hosted XAML control passed in.
*/
#define SDL_MAIN_NEEDED
#elif defined(__GDK__)
/* On GDK, SDL provides a main function that initializes the game runtime.
   Please note that #include'ing SDL_main.h is not enough to get a main()
   function working. You must either link against SDL2main or, if not possible,
   call the SDL_GDKRunApp function from your entry point.
*/
#define SDL_MAIN_NEEDED
#elif defined(__IPHONEOS__)
/* On iOS SDL provides a main function that creates an application delegate
   and starts the iOS application run loop.
   If you link with SDL dynamically on iOS, the main function can't be in a
   shared library, so you need to link with libSDLmain.a, which includes a
   stub main function that calls into the shared library to start execution.
   See src/video/uikit/SDL_uikitappdelegate.m for more details.
 */
#define SDL_MAIN_NEEDED
#elif defined(__ANDROID__)
/* On Android SDL provides a Java class in SDLActivity.java that is the
   main activity entry point.
   See docs/README-android.md for more details on extending that class.
 */
#define SDL_MAIN_NEEDED
/* We need to export SDL_main so it can be launched from Java */
#define SDLMAIN_DECLSPEC    DECLSPEC
#elif defined(__NACL__)
/* On NACL we use ppapi_simple to set up the application helper code,
   then wait for the first PSE_INSTANCE_DIDCHANGEVIEW event before 
   starting the user main function.
   All user code is run in a separate thread by ppapi_simple, thus 
   allowing for blocking io to take place via nacl_io
*/
#define SDL_MAIN_NEEDED
#elif defined(__PSP__)
/* On PSP SDL provides a main function that sets the module info,
   activates the GPU and starts the thread required to be able to exit
   the software.
   If you provide this yourself, you may define SDL_MAIN_HANDLED
 */
#define SDL_MAIN_AVAILABLE
#elif defined(__PS2__)
#define SDL_MAIN_AVAILABLE
#define SDL_PS2_SKIP_IOP_RESET() \
   void reset_IOP(); \
   void reset_IOP() {}
#elif defined(__3DS__)
/*
  On N3DS, SDL provides a main function that sets up the screens
  and storage.
  If you provide this yourself, you may define SDL_MAIN_HANDLED
*/
#define SDL_MAIN_AVAILABLE
#endif
#endif /* SDL_MAIN_HANDLED */
#ifndef SDLMAIN_DECLSPEC
#define SDLMAIN_DECLSPEC
#endif
/**
 *  \file SDL_main.h
 *
 *  The application's main() function must be called with C linkage,
 *  and should be declared like this:
 *  \code
 *  #ifdef __cplusplus
 *  extern "C"
 *  #endif
 *  int main(int argc, char *argv[])
 *  {
 *  }
 *  \endcode
 */
#if defined(SDL_MAIN_NEEDED) || defined(SDL_MAIN_AVAILABLE)
#define main    SDL_main
#endif
#include "begin_code.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
 *  The prototype for the application's main() function
 */
typedef int (*SDL_main_func)(int argc, char *argv[]);
extern SDLMAIN_DECLSPEC int SDL_main(int argc, char *argv[]);
/**
 * Circumvent failure of SDL_Init() when not using SDL_main() as an entry
 * point.
 *
 * This function is defined in SDL_main.h, along with the preprocessor rule to
 * redefine main() as SDL_main(). Thus to ensure that your main() function
 * will not be changed it is necessary to define SDL_MAIN_HANDLED before
 * including SDL.h.
 *
 * \since This function is available since SDL 2.0.0.
 *
 * \sa SDL_Init
 */
extern DECLSPEC void SDLCALL SDL_SetMainReady(void);
#if defined(__WIN32__) || defined(__GDK__)
/**
 * Register a win32 window class for SDL's use.
 *
 * This can be called to set the application window class at startup. It is
 * safe to call this multiple times, as long as every call is eventually
 * paired with a call to SDL_UnregisterApp, but a second registration attempt
 * while a previous registration is still active will be ignored, other than
 * to increment a counter.
 *
 * Most applications do not need to, and should not, call this directly; SDL
 * will call it when initializing the video subsystem.
 *
 * \param name the window class name, in UTF-8 encoding. If NULL, SDL
 *             currently uses "SDL_app" but this isn't guaranteed.
 * \param style the value to use in WNDCLASSEX::style. If `name` is NULL, SDL
 *              currently uses `(CS_BYTEALIGNCLIENT | CS_OWNDC)` regardless of
 *              what is specified here.
 * \param hInst the HINSTANCE to use in WNDCLASSEX::hInstance. If zero, SDL
 *              will use `GetModuleHandle(NULL)` instead.
 * \returns 0 on success, -1 on error. SDL_GetError() may have details.
 *
 * \since This function is available since SDL 2.0.2.
 */
extern DECLSPEC int SDLCALL SDL_RegisterApp(const char *name, Uint32 style, void *hInst);
/**
 * Deregister the win32 window class from an SDL_RegisterApp call.
 *
 * This can be called to undo the effects of SDL_RegisterApp.
 *
 * Most applications do not need to, and should not, call this directly; SDL
 * will call it when deinitializing the video subsystem.
 *
 * It is safe to call this multiple times, as long as every call is eventually
 * paired with a prior call to SDL_RegisterApp. The window class will only be
 * deregistered when the registration counter in SDL_RegisterApp decrements to
 * zero through calls to this function.
 *
 * \since This function is available since SDL 2.0.2.
 */
extern DECLSPEC void SDLCALL SDL_UnregisterApp(void);
#endif /* defined(__WIN32__) || defined(__GDK__) */
#ifdef __WINRT__
/**
 * Initialize and launch an SDL/WinRT application.
 *
 * \param mainFunction the SDL app's C-style main(), an SDL_main_func
 * \param reserved reserved for future use; should be NULL
 * \returns 0 on success or -1 on failure; call SDL_GetError() to retrieve
 *          more information on the failure.
 *
 * \since This function is available since SDL 2.0.3.
 */
extern DECLSPEC int SDLCALL SDL_WinRTRunApp(SDL_main_func mainFunction, void * reserved);
#endif /* __WINRT__ */
#if defined(__IPHONEOS__)
/**
 * Initializes and launches an SDL application.
 *
 * \param argc The argc parameter from the application's main() function
 * \param argv The argv parameter from the application's main() function
 * \param mainFunction The SDL app's C-style main(), an SDL_main_func
 * \return the return value from mainFunction
 *
 * \since This function is available since SDL 2.0.10.
 */
extern DECLSPEC int SDLCALL SDL_UIKitRunApp(int argc, char *argv[], SDL_main_func mainFunction);
#endif /* __IPHONEOS__ */
#ifdef __GDK__
/**
 * Initialize and launch an SDL GDK application.
 *
 * \param mainFunction the SDL app's C-style main(), an SDL_main_func
 * \param reserved reserved for future use; should be NULL
 * \returns 0 on success or -1 on failure; call SDL_GetError() to retrieve
 *          more information on the failure.
 *
 * \since This function is available since SDL 2.24.0.
 */
extern DECLSPEC int SDLCALL SDL_GDKRunApp(SDL_main_func mainFunction, void *reserved);
/**
 * Callback from the application to let the suspend continue.
 *
 * \since This function is available since SDL 2.28.0.
 */
extern DECLSPEC void SDLCALL SDL_GDKSuspendComplete(void);
#endif /* __GDK__ */
#ifdef __cplusplus
}
#endif
#include "close_code.h"
#endif /* SDL_main_h_ */
/* vi: set ts=4 sw=4 expandtab: */

结语

在我们的编程学习之旅中,理解是我们迈向更高层次的重要一步。然而,掌握新技能、新理念,始终需要时间和坚持。从心理学的角度看,学习往往伴随着不断的试错和调整,这就像是我们的大脑在逐渐优化其解决问题的“算法”。

这就是为什么当我们遇到错误,我们应该将其视为学习和进步的机会,而不仅仅是困扰。通过理解和解决这些问题,我们不仅可以修复当前的代码,更可以提升我们的编程能力,防止在未来的项目中犯相同的错误。

我鼓励大家积极参与进来,不断提升自己的编程技术。无论你是初学者还是有经验的开发者,我希望我的博客能对你的学习之路有所帮助。如果你觉得这篇文章有用,不妨点击收藏,或者留下你的评论分享你的见解和经验,也欢迎你对我博客的内容提出建议和问题。每一次的点赞、评论、分享和关注都是对我的最大支持,也是对我持续分享和创作的动力。

目录
相关文章
|
21天前
|
编译器
(9)Qt中信号与槽重载的解决方案
本文介绍了在Qt中处理信号与槽重载问题的三种解决方案:使用函数指针、Qt提供的QOverload类和Qt4的宏方式。
63 3
|
6月前
QT中HASH函数方法
QT中HASH函数方法
191 0
|
6月前
|
编译器
Qt问题:Qt槽函数是否支持内联?
Qt问题:Qt槽函数是否支持内联?
49 0
|
6月前
|
编译器 C++
qt槽函数的四种写法
QT槽函数的四种写法
86 0
qt槽函数的四种写法
|
6月前
|
XML 自然语言处理 数据格式
Qt国际化翻译解决方案
Qt国际化翻译解决方案
133 0
|
6月前
|
IDE 编译器 开发工具
C/C++ IDE环境 (Qt Creator visual studio等) Cmake工程不显示头文件的解决方案
C/C++ IDE环境 (Qt Creator visual studio等) Cmake工程不显示头文件的解决方案
162 0
|
12月前
25 QT - event函数
25 QT - event函数
52 0
|
21天前
(7)Qt中的自定义槽(函数)
这篇文章介绍了在Qt中如何定义和使用自定义槽函数,包括类成员函数、静态类成员函数、全局函数和lambda表达式作为槽函数的示例,以及使用lambda表达式时的注意事项。
24 2
(7)Qt中的自定义槽(函数)
|
6月前
|
算法 Ubuntu Linux
Linux Qt cannot find -lGL错误完美解决方案(亲测有效)
Linux Qt cannot find -lGL错误完美解决方案(亲测有效)
404 1
Qt 窗口常用位置API函数 & 绘图原理 & 双缓冲机制 总结
Qt 窗口常用位置API函数 & 绘图原理 & 双缓冲机制 总结