使用ifstream

时间:2016-01-19 17:12:14

标签: c++ opengl

我正在为2D roguelike写一些引擎元素。我正处于一个我希望能够打开.pngs的地方。 (我正在手动做事,因为我喜欢了解这些事情。)所以我创建了一个PngLoader类,并开始用它做基本的事情,比如......打开文件。出于某种原因,除了手动之外,这会破坏与GLEW类似的OpenGL GLFunctionFinder类。

当OpenGL版本太低时,GLFF基本上会崩溃程序;这是预期的行为。 (可能是一个未设置的函数指针的段错误。我可以通过使它更优雅地崩溃来解决这个问题,但是谁更关心?)GLFF工作得相当好,因为我的显卡运行OpenGL 4.3左右,但我确实拥有它几天前,当驱动程序切换到集成显卡驱动程序(只有OpenGL版本1.1)时。通过更改图形仪表板中的某些设置来解决这个问题。

所以当我写这样的东西时,我今天出现的问题出现了:

class ifcontainerclass {
    std::ifstream fs;
};

/* other code */

int WINAPI WinMain(/* ... */) {
    GLFunctionFinder ff;
    ff.interrogateWindows();

    ifcontainerclass ifcc;

    /* GL code and main loop */

    return 0;
}

...... OpenGL上下文卡在1.1版本上。如果我将ifstream更改为fstream,我会获得我期望的更高版本的上下文,问题就会消失。

我也在测试中发现,如果我注释掉GL code and main loop区域,问题就会消失。 “版本太低”检查在GLFunctionFinder::interrogateWindows()中完成,而不是在后面的GL代码中完成,因此仍在检查条件。 (经过一些测试,我发现评论MSG结构是导致问题消失的原因。)

我目前的信念是编译器正在做一些魔术,导致Windows / Intel / NVidia只发出OpenGL 1.1上下文/连接到错误的驱动程序...我真的不知道什么时候。这个问题看起来很随意。

我可能会考虑摆脱因懒惰而使用的全局HDC和全局HGLRC,因为我认为这个问题与事情的初始化方式有关编译器安排初始化这些东西,并将它们拉出全局范围将让我更有效地检查和控制该过程。我在GLFunctionFinder中使用static void * GlobalAddr = this文件范围的指针执行此操作,将其转换为虚拟窗口GLFunctionFinder中的WndProc,并HDCHGLRC {1}}是GLFunctionFinder的成员变量,可通过指针访问。我可能会在我的主窗口尝试类似的东西;无论如何,我一直需要清理全球范围内的东西。我可以做的另一件事是在调试器中运行每个版本并查看它的分歧,虽然我不愿意这样做,因为调试在我的IDE中没有真正正确设置,我不期待修复它。 / p>

我认为我可以在此期间使用fstream而不是ifstream来解决这个问题,但是我不习惯不理解这个奇怪的问题,因为它表明某种不稳定性我在我有10k行代码任意停止运行之前应该注意,并且只能通过改变其他地方似乎完全不相关的东西来修复。

问题:

  • 世界上到底发生了什么?这里的核心问题是什么?
  • 为什么将ifstream更改为fstream来解决问题?
  • 为什么注释MSG结构会解决问题?

PS:NvOptimusEnablement = 0x00000001没有解决问题。

PPS:Qt中的MinGW 4.9.2(作为IDE,没有Qt库)和CMake

编辑:在-ggdb传递给g ++后确定Qt的调试器工作后,我逐步完成了代码,发现PIXELFORMATDESCRIPTOR中的GLFunctionFinder不在分配;我将属性分配给一些随机临时变量而不是成员变量,而ChoosePixelFormat正在使用成员变量。由于您获得的上下文取决于您指定的像素类型,因此我实际上是从Windows请求不确定的设备上下文。编译的细节确定了PIXELFORMATDESCRIPTOR中放置了什么随机垃圾,而恰好声明ifstream而不是fstream放错了随机垃圾那个区域的垃圾。

在定义临时this->pfd_ = pfd;后,通过在GLFunctionFinderpfd的构造函数中添加一些内容来解决问题。

编辑2:为了满足我对“偏离主题”标志意味着什么的理解,我将提供核心问题的最小示例:

main.cpp中:

#include <windows.h>
#include <sstream>
#include <GL/gl.h>

HDC   h_dc;
HGLRC h_context;

LRESULT CALLBACK MainWndProc(_In_ HWND   h_wnd,
                             _In_ UINT   u_msg,
                             _In_ WPARAM w_param,
                             _In_ LPARAM l_param) {
    switch(u_msg) {
    case WM_CREATE: {
        PIXELFORMATDESCRIPTOR pfd; // <-- This was the error source, (pfd not set to an
                                   //     accelerated format, but only sometimes) 
                                   //     except in my code it was harder to see than
                                   //     this.
        h_dc = GetDC(h_wnd);
        int pfint = ChoosePixelFormat(h_dc, &pfd);
        SetPixelFormat(h_dc, pfint, &pfd);
        h_context = wglCreateContext(h_dc);
        wglMakeCurrent(h_dc, h_context);
        const unsigned char * version_string =
                static_cast<const unsigned char *>(glGetString(GL_VERSION));
        if(version_string[0] == '1' || version_string[0] == '2') {
            std::stringstream ss;
            ss << "OpenGL version (" << version_string << ") is too low";
            MessageBox(NULL, ss.str().c_str(), "Error", MB_OK | MB_ICONERROR);
        }
        break;
    }

    case WM_DESTROY:
        PostQuitMessage(EXIT_SUCCESS);
        break;

    default:
        return DefWindowProc(h_wnd, u_msg, w_param, l_param);
    }

    return 1;
}

int WINAPI WinMain(  HINSTANCE h_inst,
                     HINSTANCE h_previnst,
                     LPSTR cmd_str_in,
                     int cmd_show_opt) {

    WNDCLASSEX wc;

    wc.cbSize         = sizeof(WNDCLASSEX);
    wc.style          = CS_OWNDC;
    wc.lpfnWndProc    = MainWndProc;
    wc.cbClsExtra     = 0;
    wc.cbWndExtra     = 0;
    wc.hInstance      = h_inst;
    wc.hIcon          = NULL;
    wc.hCursor        = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground  = (HBRUSH)(COLOR_BACKGROUND + 1);
    wc.lpszMenuName   = NULL;
    wc.lpszClassName  = "MAINWIN";
    wc.hIconSm        = NULL;

    RegisterClassEx(&wc);

    HWND h_wnd = CreateWindowEx(0,
                                "MAINWIN",
                                "MCVE Program",
                                WS_OVERLAPPEDWINDOW,
                                CW_USEDEFAULT,
                                CW_USEDEFAULT,
                                640,
                                480,
                                NULL,
                                NULL,
                                h_inst,
                                NULL);

    return EXIT_SUCCESS;
}

的CMakeLists.txt:

project(mcve_pfd_problem)
cmake_minimum_required(VERSION 2.8)
aux_source_directory(. SRC_LIST)
add_executable(${PROJECT_NAME} WIN32 ${SRC_LIST})

target_link_libraries(${PROJECT_NAME} opengl32)

如果有人跳到最后,问题已解决,但我不知道我应该如何表明。

1 个答案:

答案 0 :(得分:0)

所以,我实际看到的是未定义行为的影响,因为默认初始化一个struct,其中包含的值未初始化:

class GLFunctionFinder {
    PIXELFORMATDESCRIPTOR pfdarr_;
    /* other code */

    GLFunctionFinder();
    setupContext();
    /* other code */
}

GLFunctionFinder::GLFunctionFinder() {
    /* other code */
    PIXELFORMATDESCRIPTOR pfd = { /* things */ };
    // Missing: pfdarr_ = pfd;
    // pfdarr_ never gets set
}

GLFunctionFinder::setupContext() {
    // Undefined behavior:
    int px_format_default = ChoosePixelFormat(this->h_cd, &(this->pfdarr_));
    /* other code */
}

这给了ChoosePixelFormat pfdarr_中的任何垃圾。当我最初写这篇文章时,它表现得好像没有问题,因为显然垃圾数据看起来像#34;一个加速的像素格式类型,ChoosePixelFormat会给我一个int format,它产生了我之后的OpenGL上下文。它暂时保持这种状态,因为它只是继续工作。

fstream切换到ifstream更改了有关编译器布局/优化程序的方式的一些细节,pfdarr_中的垃圾数据更改为&#34;看起来像&# 34;一种不加速的格式。这导致了错误的上下文,导致OpenGL版本检查失败。注释MSG结构和事件循环的一部分的故事基本相同:它只是发生了编译器会发出一些产生我想要的OpenGL上下文的东西。

我正在编辑昨晚在Edit 2中给出的代码,它给了我一个1.1上下文。今天早上,完全相同的代码,没有错误;移动了MessageBox,发现我得到了4.3上下文。有趣的错误。