【问题标题】:Problem trying to open a file with ifstream using CMake尝试使用 CMake 使用 ifstream 打开文件时出现问题
【发布时间】:2019-03-04 16:06:02
【问题描述】:

/* 定义类着色器。 */

我正在使用 OpenGL 和 CMake 用 C++ 开发应用程序。我的目录结构是:

  • 坦克
    • CMakeLists.txt
    • ext(包含 GLFW 和高兴)
      • TanX.cpp
      • TanX.h
      • 数学(包含几种数学方法;与我的问题不太相关)
      • 着色器
        • Shader.h
        • vertex.glsl
        • fragment.glsl
        • CMakeLists.txt

Shader.h 主要用于从文件中读取顶点和片段着色器源,编译它们并将它们链接到着色器程序。它看起来像这样:

#ifndef _SHADER_H
#define _SHADER_H

#include <string>
#include <exception>
#include <fstream>
#include <glad.h>


class shader_exception :std::exception {
private:
    const char* text;
public:
    shader_exception(const char* text)
        : text(text) {}
    virtual const char* what() const {
        return text;
    }
};

class Shader {
private:
    std::string* vertex_source = new std::string();
    std::string* fragment_source = new std::string();
    unsigned int vao, vbo, vertex_shader, fragment_shader, program;
public:
    Shader(const char* vertex_source_path, const char* fragment_source_path) {
        std::ifstream file_reader;
        file_reader.open(vertex_source_path);
        std::string line;

        if (file_reader.is_open()) {
            while (getline(file_reader, line)) {
                vertex_source->append(line + "\n");
            }
            file_reader.close();
        }
        else 
            throw shader_exception("Could not open vertex shader source file");

        file_reader.open(fragment_source_path);  // this is where I get an unhandled exception dialog box
        if (file_reader.is_open()) {
            while (getline(file_reader, line)) {
                fragment_source->append(line + "\n");
            }
            file_reader.close();
        }
        else
            throw shader_exception("Could not open fragment shader file");
        const char** vertex_source_c = (const char**)malloc(vertex_source->size());
        *vertex_source_c = vertex_source->c_str();
        const char** fragment_source_c = (const char**)malloc(fragment_source->size());

        *fragment_source_c = fragment_source->c_str();
        vertex_shader = glCreateShader(GL_VERTEX_SHADER);

        glShaderSource(vertex_shader, 1, vertex_source_c, NULL);
        glCompileShader(vertex_shader);

        int success;
        glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success);
        char infoLog[512];
        if (!success) {
            glGetShaderInfoLog(vertex_shader, 512, NULL, infoLog);
            throw shader_exception(infoLog);
        }

        glShaderSource(fragment_shader, 1, fragment_source_c, NULL);
        glCompileShader(fragment_shader);
        glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success);
        if (!success) {
            glGetShaderInfoLog(fragment_shader, 512, NULL, infoLog);
            throw shader_exception(infoLog);
        }

        program = glCreateProgram();
        glAttachShader(program, vertex_shader);
        glAttachShader(program, fragment_shader);
        glLinkProgram(program);
        glGetProgramiv(program, GL_LINK_STATUS, &success);
        if (!success) {
            glGetProgramInfoLog(program, 512, NULL, infoLog);
            throw shader_exception(infoLog);
        }

        glDeleteShader(vertex_shader);
        glDeleteShader(fragment_shader);
    }

    Shader() = delete;

    void use() 
    {
        glUseProgram(program);
    }

    ~Shader() {
        glDeleteProgram(program);
    }
};

#endif

在 TanX.cpp 中,我尝试像这样创建一个 Shader 对象:

Shader shader("vertex.glsl", "fragment.glsl");

如您所见,我想使用的着色器源文件是位于“Shader”文件夹中的 vertex.glsl 和 fragment.glsl。为了使它们可用于 ifstream,Shader 文件夹中的 CMakeLists.txt 文件如下所示(这是here 提到的使文件可用于程序的选项之一):

configure_file(${CMAKE_CURRENT_SOURCE_DIR}/vertex.glsl ${CMAKE_CURRENT_BINARY_DIR} COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fragment.glsl ${CMAKE_CURRENT_BINARY_DIR} COPYONLY)

顶层 CMakeLists.txt 文件还包含

add_subdirectory("TanX/src/Shader")

以便执行Shader中的CMakeLists.txt文件。

问题是,当我尝试在 Visual Studio 中运行我的代码时,我在 Shader.h 文件的第 45 行(它试图打开 fragment.glsl)中得到一个未处理的异常。这是一个shader_exception,所以显然打开vertex.glsl 失败了。 “异常未处理”的详细信息 - 对话框:“TanX.exe 中 0x00007FFF43E4A388 处的未处理异常:Microsoft C++ 异常:内存位置 0x00000028896FF608 处的 shader_exception。发生”。现在我有两个问题

1) 我需要做什么才能让 ifstream 打开 vertex.glsl(然后是 fragment.glsl)?
2) 为什么我什至会得到一个未处理的异常错误,Shader 对象的构造不应该在抛出异常后立即失败,导致程序甚至没有到达第 45 行?

【问题讨论】:

    标签: c++ cmake


    【解决方案1】:

    马上,这...

    Shader* shader;
    try {
        *shader = Shader("vertex.glsl", "fragment.glsl");
    }
    

    ... 是未定义的行为,因为您正在取消引用未初始化的指针(即,您尚未为 Shader 对象分配任何存储空间)。这可能不是唯一的问题,但它是一个关键问题,必须先修复,然后才能进一步调试(因为之后程序的行为是不可预测的)。

    你可能想写:

    shader = new Shader(...);
    

    (实际上,您应该为此使用std::unique_ptr 而不是裸指针,但这超出了原始问题的范围。除了其他好处之外,它还使这种错误更容易被发现和避免。)


    至于其他问题,你有没有检查过程序是在.glsl文件所在的目录下运行的?传递给文件流的相对路径被解释为相对于程序的当前工作目录,而不是相对于原始源文件的位置。

    据我统计,第 45 行是...

    }
    

    ...所以有些东西似乎错位了。将实际的程序输出和异常信息(或对话框内容)连同行号或显示调试器指示的行的注释一起发布会很有帮助。

    【讨论】:

    • 其实我并不想写 shader = new Shader(...) 我试图找到一种方法将构造函数放在 try-catch-block 中,同时仍然能够在它之外使用着色器。不过,我现在确实放弃了这个想法并更新了问题。
    • 是的,我忘了复制文件开头的(不是很有帮助的)cmets,导致行号不同。我现在添加了 cmets,所以行号现在应该匹配。我还添加了对话框的内容。
    • 至于文件与程序的位置:这就是为什么我在该目录中有带有两个 configure_file(...) 命令的 CMakeLists 文件,我希望将文件复制到程序可以在哪里访问它们(我从here 得到的,可能也应该在问题中提到这一点)。
    • 好的,所以问题实际上是文件在错误的文件夹中。我将它们放入 Shader 文件夹的 bin 文件夹中,但由于所有内容都在标头中实现,因此放置它们的正确位置是顶级 CMakeLists.txt 文件的 bin 文件夹。感谢您帮助我解决这个问题!
    • @Fusion Gotcha。 std::unique_ptr 是我在这种情况下的首选(当我需要创建一个可以在 try 块的封闭范围内访问的对象时)。这归结为您尝试过的,但更安全。很高兴你把它整理出来。如果答案有帮助,请不要忘记投票和/或标记为已接受的答案。
    【解决方案2】:

    问题是我把文件放在哪里。我把它们放到Shader文件夹的bin文件夹里了,但是因为是在header中实现的,所以正确的地方是顶层文件夹的bin文件夹。

    【讨论】:

    • 很高兴您将其整理出来,但请注意 代码的编写位置(在哪个源代码文件/标头中)与相对路径的运行时解释无关。一旦编译,源代码的目录结构(如果有的话)已经完全丢失(忽略调试数据)。重要的是进程的当前工作目录 (CWD)。这取决于您启动程序的方式和地点。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-26
    • 2019-11-15
    相关资源
    最近更新 更多