如何通过‘运行时导入’实现跨平台C++终端窗口大小获取函数
Got it, let's switch your compile-time conditional checks to a runtime-based approach for getting terminal window sizes across Linux and Windows. Here's a clean, maintainable way to do this:
Core Idea
Instead of wrapping platform-specific code in #ifdef blocks, we'll:
- Isolate Linux and Windows logic into separate helper functions.
- Use runtime platform detection to pick the right implementation.
- For Windows, dynamically load the required kernel32.dll functions (true runtime import) instead of linking at compile time. For Linux, we can either use standard libc functions directly or go full runtime with
dlopen/dlsym(I'll show both options).
Step-by-Step Implementation
1. Define Platform Detection & Function Types
First, add a simple way to detect the current platform at runtime (we use compile-time macros just to set a runtime flag, not to wrap logic):
// Adjust this to match your actual Vector class definition class Vector { public: int w; int h; }; enum class Platform { Linux, Windows, Unknown }; Platform getCurrentPlatform() { #ifdef __linux__ return Platform::Linux; #elif _WIN32 || _WIN64 return Platform::Windows; #else return Platform::Unknown; #endif } // Function pointer type for our platform-specific size getters using GetWindowSizeFunc = bool(*)(Vector&);
2. Linux Implementation
Option 1: Direct use of libc functions (simpler, still avoids compile-time logic blocks):
#include <sys/ioctl.h> #include <unistd.h> bool getLinuxWindowSize(Vector& windowsize) { struct winsize window; // Check if ioctl succeeds (system calls don't throw C++ exceptions, so try/catch was unnecessary) if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &window) == -1) { return false; } windowsize.w = window.ws_col; windowsize.h = window.ws_row; return true; }
Option 2: Full runtime import with dlopen (strict runtime loading of libc functions):
#include <dlfcn.h> bool getLinuxWindowSize(Vector& windowsize) { void* libcHandle = dlopen("libc.so.6", RTLD_LAZY); if (!libcHandle) { return false; } // Get pointer to ioctl function using ioctlFunc = int(*)(int, unsigned long, ...); ioctlFunc pIoctl = reinterpret_cast<ioctlFunc>(dlsym(libcHandle, "ioctl")); if (!pIoctl) { dlclose(libcHandle); return false; } struct winsize window; if (pIoctl(STDOUT_FILENO, TIOCGWINSZ, &window) == -1) { dlclose(libcHandle); return false; } windowsize.w = window.ws_col; windowsize.h = window.ws_row; dlclose(libcHandle); return true; }
3. Windows Implementation (True Runtime Import)
We'll dynamically load kernel32.dll and fetch the required functions at runtime instead of linking at compile time:
// Minimal type definitions to avoid global windows.h inclusion typedef void* HMODULE; typedef void* HANDLE; typedef unsigned long DWORD; typedef long BOOL; typedef struct _CONSOLE_SCREEN_BUFFER_INFO { struct { short Left, Top, Right, Bottom; } srWindow; } CONSOLE_SCREEN_BUFFER_INFO; // Helper to get GetProcAddress without linking kernel32 extern "C" void* GetProcAddress(void*, const char*); bool getWindowsWindowSize(Vector& windowsize) { // Load kernel32.dll at runtime HMODULE kernel32Handle = reinterpret_cast<HMODULE>( reinterpret_cast<void*(*)(const char*)>( GetProcAddress(reinterpret_cast<HMODULE>(-1), "LoadLibraryA") )("kernel32.dll") ); if (!kernel32Handle) { return false; } // Get pointers to required functions using GetStdHandleFunc = HANDLE(*)(DWORD); GetStdHandleFunc pGetStdHandle = reinterpret_cast<GetStdHandleFunc>( GetProcAddress(kernel32Handle, "GetStdHandle") ); using GetConsoleScreenBufferInfoFunc = BOOL(*)(HANDLE, CONSOLE_SCREEN_BUFFER_INFO*); GetConsoleScreenBufferInfoFunc pGetConsoleScreenBufferInfo = reinterpret_cast<GetConsoleScreenBufferInfoFunc>( GetProcAddress(kernel32Handle, "GetConsoleScreenBufferInfo") ); using FreeLibraryFunc = BOOL(*)(HMODULE); FreeLibraryFunc pFreeLibrary = reinterpret_cast<FreeLibraryFunc>( GetProcAddress(kernel32Handle, "FreeLibrary") ); if (!pGetStdHandle || !pGetConsoleScreenBufferInfo || !pFreeLibrary) { pFreeLibrary(kernel32Handle); return false; } // Retrieve window size const DWORD STD_OUTPUT_HANDLE = static_cast<DWORD>(-11); HANDLE stdoutHandle = pGetStdHandle(STD_OUTPUT_HANDLE); CONSOLE_SCREEN_BUFFER_INFO csbi; if (!pGetConsoleScreenBufferInfo(stdoutHandle, &csbi)) { pFreeLibrary(kernel32Handle); return false; } windowsize.w = csbi.srWindow.Right - csbi.srWindow.Left + 1; windowsize.h = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; pFreeLibrary(kernel32Handle); return true; }
4. Update Your DisplayManager Method
Now, wire everything together in your main function, with zero compile-time platform logic blocks:
class DisplayManager { private: Vector windowSize; public: bool getWindowSize(); }; bool DisplayManager::getWindowSize() { Vector windowsize; Platform currentPlatform = getCurrentPlatform(); GetWindowSizeFunc sizeGetter = nullptr; switch (currentPlatform) { case Platform::Linux: sizeGetter = getLinuxWindowSize; break; case Platform::Windows: sizeGetter = getWindowsWindowSize; break; default: return false; } if (sizeGetter && sizeGetter(windowsize)) { // Assign the retrieved size to your class member this->windowSize = windowsize; return true; } return false; }
Key Notes
- No Compile-Time Logic Blocks: The only
#ifdefusage is for setting a runtime platform flag, not wrapping core functionality. - True Runtime Import: Windows functions are loaded dynamically at runtime, no compile-time linking to kernel32.lib required.
- Error Handling: We check all function loads and system calls for failures, replacing the unnecessary
try/catchblocks (system calls don't throw C++ exceptions by default).
内容的提问来源于stack exchange,提问作者anon

