【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_HANDLEDSDL ヘッダー ファイルをインクルードする前に確認してください。これは、SDL_MAIN_HANDLEDマクロを含む前にマクロを定義するSDL.hか、マクロの定義をSDL_main.h防ぐ必要があるためです。mainコードは次のようになります。

    #define SDL_MAIN_HANDLED
    #include "SDL.h"
    
  2. 上記の方法で問題が解決しない場合は、 SDL ヘッダー ファイルをインクルードした直後にmainマクロの定義を解除してみてください。

    #include "SDL.h"
    #undef main
    

これにより、コード内でmainSDL のマクロ定義の対象外になります。

理由を分析する

通常、プログラムにはmain関数を 1 つだけ含めることができます。mainただし、SDL は、マクロ定義を介してユーザーの関数の名前を変更しSDL_main、独自の関数を提供するというトリックを使用しますmainそうすることで、コード内に関数を記述するとmain、プリプロセッサによって実際にその関数の名前が に変更されますSDL_main

これは、 SDLヘッダー ファイルSDL_main.hで定義されたマクロです。

#define main SDL_main

これは、コード内に次のような関数を記述すると、次のようになりますmain

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

プリプロセッサは実際にこれを次のように変換します。

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

次に、SDL は独自のmain関数を提供します。この関数は、初期化作業を行ってからSDL_main(つまり、main関数を) 呼び出し、最後にクリーンアップします。

SDL で関数を再定義したくない場合はmain、SDL のヘッダー ファイルをインクルードする前にマクロを定義し、関数内で SDL の初期化関数とクリーンアップ関数を手動で呼び出すことができSDL_MAIN_HANDLEDますmain

SDLの初期化について

SDL がマクロを通じて独自の main 関数を呼び出すことはすでにわかっていますが、それは何をするのでしょうか?

具体的には、SDLmain関数は通常、次のことを行います。

  1. SDL ライブラリの初期化: これには、ビデオ サブシステム、オーディオ サブシステム、イベント サブシステムなどの初期化が含まれます。
  2. ユーザーのmain関数を呼び出す: 初期化後、SDL の関数(つまりユーザーの関数)mainが呼び出されますSDL_mainmain
  3. SDL ライブラリをクリーンアップする: ユーザーのmain関数が返された後、SDLmain関数は、ビデオ サブシステム、オーディオ サブシステム、およびイベント サブシステムを閉じるなど、SDL ライブラリをクリーンアップします。

mainこの設計アプローチにより、ユーザーが関数main内で SDL の初期化関数やクリーンアップ関数を明示的に呼び出す必要がなく、SDL はユーザー関数の実行の前後に独自のコードを挿入できます。

理論上は問題ありませんが、警告が頻繁に報告されるのはなぜですか?

Qt ではmain通常、QApplicationオブジェクトを作成し、Qt のイベント ループを開始するために関数が使用されます。SDL がmainマクロ定義を介して関数を再定義しようとすると、Qt の初期化コードがSDL_main実際の関数ではなく関数に誤って配置されmain、問題が発生する可能性があります。

さらに、Qt の moc (Meta-Object Compiler) もmainマクロの再定義に対して警告を生成する場合があります。moc は、Qt のメタオブジェクト システムを操作するための Qt ツールです。クラス定義のソース コードをスキャンし、シグナルやスロットなどの Qt 機能を使用するクラスを検索し、これらのクラスの追加コードを生成します。mainmoc がマクロとして定義されている場合、ソース コードの処理時に警告が生成される場合があります。

したがって、Qt プログラムで SDL を使用する場合、ベスト プラクティスは、SDL のヘッダー ファイルをインクルードする前にマクロを定義し、関数内で SDL の初期化関数とクリーンアップ関数を手動で呼び出すことSDL_MAIN_HANDLEDですmainこれにより、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 <[email protected]>

  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: */

エピローグ

理解することは、プログラミング学習の次のレベルへの重要なステップです。ただし、新しいスキルやアイデアを習得するには、常に時間と粘り強さが必要です。心理学の観点から見ると、学習には継続的な試行錯誤と調整が伴うことが多く、これは私たちの脳が問題を解決するための「アルゴリズム」を徐々に最適化していくのと似ています。

だからこそ、間違いに遭遇したときは、それを単なる強迫観念ではなく、学び改善する機会として捉える必要があります。これらの問題を理解して解決することで、現在のコードを修正できるだけでなく、プログラミング能力を向上させ、将来のプロジェクトで同じ間違いが起こるのを防ぐことができます。

皆さんも積極的に参加し、プログラミング スキルを継続的に向上させることをお勧めします。あなたが初心者であろうと経験豊富な開発者であろうと、私のブログがあなたの学習の旅に役立つことを願っています。この記事が役立つと思われる場合は、クリックしてブックマークするか、コメントを残して洞察や経験を共有してください。また、私のブログの内容について提案や質問をすることも歓迎します。「いいね!」、コメント、シェア、フォローのすべてが私にとって最大のサポートであり、共有し、創作を続けるモチベーションとなっています。


私の CSDN ホームページを読んで、よりエキサイティングなコンテンツのロックを解除してください: Bubble の CSDN ホームページ
ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/qq_21438461/article/details/131787303
おすすめ