严重包含在字符串中,除非我将它存储在 C++ 类中

Posted

技术标签:

【中文标题】严重包含在字符串中,除非我将它存储在 C++ 类中【英文标题】:Badly contained in a string except when I store it in C++ class 【发布时间】:2019-07-11 17:45:57 【问题描述】:

我正在接受 openGL 编程培训,我想创建自己的课程,让我的生活更轻松。但是,我在 Shader 类中遇到了一个问题。

我的 Shader 类由一个 C++ 字符串(着色器的名称)和另外两个 C 字符串(包含片段和顶点着色器的代码)组成。

然后,在初始化期间,我读取文件以将每个字符存储到我的两个 C 字符串中。此时,这两个变量已正确填充,但如果我尝试通过 getVertex() 方法读取它们,它根本不会显示其中必须包含的内容。

片段和顶点着色器代码必须存放在const GLchar * 中,因为glShaderSource() 用于加载着色器。下面是这个函数的原型:void glShaderSource(GLuint shader, GLsizei count, const GLchar **string, const GLint *length);

我已经尝试将基本的const char * 用作const GLchar *,但它是偶数。

我还尝试将着色器代码存储为 C++ 字符串,并在 main 中将它们转换为 C 字符串,但它不会改变任何内容。

这是我的调试打印代码:

-main.cpp

#include "head.h"

void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow* window);

int main()

    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    //glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);    //For MacOS


    /*Window initialization*/
    GLFWwindow* window = glfwCreateWindow(800, 600, "Hello Window!", NULL, NULL);
    if (window == NULL)
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    
    glfwMakeContextCurrent(window);
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);


    /*GLAD initialization*/
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    


    /*Initialize shaders*/
    Shader myShaders("Hello");

    std::cout << "Main Print\n" << myShaders.getVertex() << std::endl;

    /*Triangle Vertices*/
    /*float vertices[] = 
    -0.5f, -0.5f, 0.0f,
     0.5f, -0.5f, 0.0f,
     0.0f,  0.5f, 0.0f
    ;*/


    /*Rectangle Vertices*/
    float vertices[] = 
     0.5f,  0.5f, 0.0f,  // top right
     0.5f, -0.5f, 0.0f,  // bottom right
    -0.5f, -0.5f, 0.0f,  // bottom left
    -0.5f,  0.5f, 0.0f   // top left 
    ;
    unsigned int indices[] = 
        0, 1, 3,   // first triangle
        1, 2, 3    // second triangle
    ;

    unsigned int VBO;
    glGenBuffers(1, &VBO);

    /*Define the type of the VBO*/
    glBindBuffer(GL_ARRAY_BUFFER, VBO);

    /*Copy vertices into the GL_ARRAY_BUFFER object (VBO)*/
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);


    /*Creating a VS object*/
    unsigned int vertexShader;
    vertexShader = glCreateShader(GL_VERTEX_SHADER);

    /*Link the VS code to the VS object*/
    glShaderSource(vertexShader, 1, &myShaders.getVertex(), NULL);
    glCompileShader(vertexShader);

    /*Testing the VS compilation*/
    int  success;
    char infoLog[512];
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);

    if (!success)
        glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
    

    /*As the VS, same for FS*/
    unsigned int fragmentShader;
    fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &myShaders.getFragment(), NULL);
    glCompileShader(fragmentShader);
    if (!success) 
        glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
    


    /*Creating the program Shader*/
    unsigned int shaderProgram;
    shaderProgram = glCreateProgram();

    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);

    /*Testing PS compilation*/
    glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
    if (!success) 
        glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::PROGRAM::COMPILATION_FAILED\n" << infoLog << std::endl;
    


    /*Deleting shaders already used*/
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);

    /*Activating our shader*/
    glUseProgram(shaderProgram);


    /*How to interprets data*/
    /*(layout (location = 0),vec3,type of the vec,for [-1.0;1.0],stride worked with 0 too, offset to begin*/
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);


    /*Creating Vertex Array Object*/
    unsigned int VAO;
    glGenVertexArrays(1, &VAO);

    // 1. Lier le Vertex Array Object (VAO)
    glBindVertexArray(VAO);
    // 2. Copier les sommets dans un tampon pour qu’OpenGL les utilise
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    // 3. Initialiser les pointeurs d’attributs de sommets
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);


    /*Creating an EBO for tell the order of vertices to being draw*/
    unsigned int EBO;    
    glGenBuffers(1, &EBO);

    /*GL_ELEMENT_ARRAY_BUFFER for EBO*/
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);


    /*Setting the view*/
    glViewport(0, 0, 800, 600);


    /*To get a thread style*/
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);


    /*Render Loop*/
    while (!glfwWindowShouldClose(window))
        glClearColor(0.5f, 0.3f, 0.6f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        processInput(window);

        glUseProgram(shaderProgram);
        //glBindVertexArray(VAO);
        /*(Kind of primitive to use, begin of vertices tab, end of vertices tab)*/
        //glDrawArrays(GL_TRIANGLES, 0, 3);


        /*6 for length of EBO*/
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

        glfwPollEvents();
        glfwSwapBuffers(window);
    


    glfwTerminate();


    return 0;



/*Resize*/
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
    glViewport(0, 0, width, height);



/*Handle inputs*/
void processInput(GLFWwindow* window)
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);

-shader.hpp:

#pragma once

class Shader 

public:
    Shader(std::string const& name) : name_(name)
        std::string tmp;
        std::ifstream stream("./shaders/" + name_ + ".fs");
        if(stream) 
            while (stream.good()) 
                tmp += stream.get();
            
        
        fragment_ = tmp.c_str();

        stream.close();

        tmp = "";

        stream.open("./shaders/" + name_ + ".vs");
        if(stream) 
            while (stream.good()) 
                tmp += stream.get();
            
        
        vertex_ = tmp.c_str();

        stream.close();
        std::cout << "Shader Initialization Print\n" << vertex_ << "\n\n";
    

    void initialize()
        if (name_.size() > 0) 
            std::string tmp;
            std::ifstream stream("./shaders/" + name_ + ".fs");
            if (stream) 
                while (stream.good()) 
                    tmp += stream.get();
                
            
            fragment_ = tmp.c_str();
            stream.close();

            tmp = "";

            stream.open("./shaders/" + name_ + ".vs");
            if (stream) 
                while (stream.good()) 
                    tmp += stream.get();
                
            
            vertex_ = tmp.c_str();
            stream.close();
        
    

    void setName(std::string const& name) 
        name_ = name;
    

    std::string getName() 
        return name_;
    

    void setFragment(std::string const& fragment) 
        fragment_ = fragment.c_str();
    

    const GLchar* & getFragment() 
        return fragment_;
    

    void setVertex(std::string const& vertex) 
        vertex_ = vertex.c_str();
    

    const GLchar* & getVertex() 
        std::cout << "getVertex() Print\n" << vertex_ << "\n\n";
        return vertex_;
    

private:
    std::string name_;
    const GLchar * vertex_;
    const GLchar * fragment_;
;

-head.h:

#pragma once

#include <iostream>
#include <fstream>
#include <string>

#include <glad/glad.h>
#include <GLFW/glfw3.h>

#include "shader.hpp"

-执行跟踪

Shader Initialization Print
#version 330 core

layout (location = 0) in vec3 aPos;
void main()

    gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
 

getVertex() Print
¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦  

Main Print
¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦  
getVertex() Print
¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦  

ERROR::SHADER::VERTEX::COMPILATION_FAILED
0(1) : error C0000: syntax error, unexpected $undefined at token "<undefined>"

ERROR::SHADER::FRAGMENT::COMPILATION_FAILED
0(1) : error C0000: syntax error, unexpected $undefined at token "<undefined>"

ERROR::SHADER::PROGRAM::COMPILATION_FAILED
Vertex info
-----------
0(1) : error C0000: syntax error, unexpected $undefined at token "<undefined>"
(0) : error C2003: incompatible options for link

Fragment info
-------------
0(1) : error C0000: syntax error, unexpected $undefined at token "<undefined>"
(0) : error C2003: incompatible options for link

实际上,我希望在我调用getVertex()/getFragment() 方法的任何地方都能得到着色器初始化打印。

【问题讨论】:

setFrahment 函数尝试存储临时字符串的c_str ()。您在代码的各个部分都犯了同样的错误。临时死了会发生什么?字符串数据也消失了。 显而易见的解决方案是使vertex_fragment_ C++ 字符串类似于name_。有什么理由不可以吗? 回复@PaulMcKenzie:那么,我应该直接将数据存储在`` `vertex _``` 和fragment _ 变量中而不是使用临时变量吗? @3t13nn3 不,这无济于事。问题是来自c_str 的返回C 字符串的范围有限。将它存储在任何地方都是危险的,因为根据代码中其他地方发生的情况,它可能会变得无效。 回复@John:我必须使用const GLchar *glShaderSource() 函数中直接调用我的getter。但我会尝试将它投射到我的主目录中,但这对我来说并不干净。 【参考方案1】:

首先,当您读取文件时,您必须在读取字符后评估stream.good() 时出现问题,但在将字符添加到字符串之前。注意,.get 在读取字符失败时设置eofbit,但在读取 kast 字符时不设置:

std::string tmp;
std::ifstream stream("./shaders/" + name_ + ".fs");

while (true) 
    char c = stream.get();
    if (!stream)
        break;
    tmp += c;

无论如何我建议使用std::istreambuf_iterator:

std::ifstream stream("./shaders/" + name_ + ".fs");
std::string tmp = std::string(std::istreambuf_iterator<char>(stream), std::istreambuf_iterator<char>());

getVertexgetFragment。应返回对指向着色器代码字符 (const GLchar*&amp;) 的指针的引用。因此,您必须将着色器代码存储在属性中。我建议将代码放在std::string 中。此外,您还需要一个const GLchar* 类型的属性,它包含一个指向代码的指针,并且可以通过引用返回:

class Shader 
public:

   // ...

   void setVertex(std::string const& vertex) 
        vertex_ = vertex;
        vs_ptr_ = vertex_.c_str();
    

    const GLchar*& getVertex() 
        return vs_ptr_;
    

private:

    // ...

    std::string vertex_;
    const GLchar *vs_ptr_;
;

整个班级我的样子如下:

class Shader 

public:
    Shader(std::string const& name) 
        : name_(name)
        initialize();
    

    void initialize()
        if (name_.empty())
            return;

        std::ifstream stream("./shaders/" + name_ + ".fs");
        std::string tmp = std::string(std::istreambuf_iterator<char>(stream), std::istreambuf_iterator<char>()); 
        stream.close();
        setFragment(tmp);

        stream.open("./shaders/" + name_ + ".vs");
        tmp = std::string(std::istreambuf_iterator<char>(stream), std::istreambuf_iterator<char>());
        stream.close();
        setVertex(tmp);
    

    void setName(std::string const& name) 
        name_ = name;
    

    std::string getName() 
        return name_;
    

    void setFragment(std::string const& fragment) 
        fragment_ = fragment;
        fs_ptr_ = fragment_.c_str();
    

    const GLchar*& getFragment() 
        return fs_ptr_;
    

    void setVertex(std::string const& vertex) 
        vertex_ = vertex;
        vs_ptr_ = vertex_.c_str();
    

    const GLchar*& getVertex() 
        return vs_ptr_;
    

private:
    std::string name_;
    std::string vertex_;
    std::string fragment_;
    const GLchar *vs_ptr_;
    const GLchar *fs_ptr_;
;

【讨论】:

好的,可以了。如果我明白了,问题是关于用于获取 C 字符串指针的字符串的生命周期? @3t13nn3 是的。指针指向“易失性”存储器。它指向一个被销毁的对象的缓冲区。【参考方案2】:

这是处理此问题的一种方法。它使用string 并将c_str() 的结果存储在该字符串上。关键是,通过存储指针以及它所基于的字符串,您可以确保指针在字符串有效期间保持有效。

class Shader

public:
    void setVertex(std::string const& vertex) 
        vertex_ = vertex;
        vertexPtr_ = vertex_.c_str(); // this must be vertex_ not vertex, otherwise we have exactly the same problem as before
    

    const GLchar* & getVertex() 
        std::cout << "getVertex() Print\n" << vertex_ << "\n\n";
        return vertexPtr_;
    

private:
    string vertex_;
    const GLchar* vertexPtr_;
;

这是未经测试的代码。

C++ 不是一种只要可访问数据就保持有效的语言(例如,与 Java 不同)。如果不了解所创建对象的生命周期,就无法编程 C++。您的程序获得了正确的类型,但未能理解指针在您使用时无效。这个版本将字符串和指向它的指针保存在一起,这样两者的生命周期相同。

【讨论】:

以上是关于严重包含在字符串中,除非我将它存储在 C++ 类中的主要内容,如果未能解决你的问题,请参考以下文章

如何在 C++ 中获取、存储和打印非英语字符串

除非涉及 endl,否则返回字符串的 C++ 函数不起作用...?

C++ 字符串精确浮动

编辑存储在字节数组中的数据

我自己的堆栈类中的 C++ 奇怪行为

char 数组是不是包含字符串?