2011-05-18 27 views
25

Tengo el siguiente código:¿Cómo pasar una std :: string a glShaderSource?

glShaderSource(shader, 1, (const char **)data.c_str(), NULL); 

pero hace que mi caída del programa. ¿Cómo convierto std::string en const char **? También probé (const char **)& pero decía "requiere l-value" que no entiendo. Funciona bien cuando se utiliza este código:

const char *data = "some code"; 
glShaderSource(shader, 1, &data, NULL); 

Pero no puedo hacer que funcione directamente desde un std::string. Podría asignar una nueva matriz char pero no es un buen código.

También probé con const GLchar pero obviamente no hace la diferencia.

+4

Tener un vistazo a la documentación de la función. Debe haber una razón por la que toma un puntero a un puntero. Supuestamente, este es un parámetro de salida, y la función _escribe_ a 'char *' en el 'char ** desreferenciado. En ese caso, necesita pasar la dirección de un puntero real, y no de un temporal devuelto por 'std :: string :: c_str()'. – sbi

+1

@sbi: No es un parámetro de salida, está esperando una matriz de cadenas C. –

+1

@Benjamin: Ah, sí, me olvidé de esa posibilidad. – sbi

Respuesta

42

data.c_str() devuelve un const char*, así que esto:

const char *c_str = data.c_str(); 
glShaderSource(shader, 1, &c_str, NULL); 
+1

¿existe la posibilidad de eliminar la declaración de variable de la misma y llegar a la línea donde llamo a la función? – Rookie

+0

No, mira mi respuesta más arriba. – Jason

+1

@Rookie: declare, pero no inicialice, 'c_str' en alguna parte:' const char * c_str; '. Luego, en el punto de uso, intente esto: 'glShaderSource (sombreador, 1, & (c_str = data.c_str()), NULL);' –

14

El valor de retorno de std::string::c_str() es un valor de puntero (es decir, una dirección) a una matriz de cadenas estática celebrada el interior de las estructuras de datos del objeto std::string . Dado que el valor de retorno es solo un valor r temporal (es decir, es solo un número almacenado en un registro de CPU), no es un valor l y por lo tanto no tiene una dirección de memoria en la que pueda tomar la dirección de y lanzar a un puntero a puntero. Primero debe guardar el valor del puntero de retorno en una dirección de memoria. Las ubicaciones de memoria son l-values, y pueden tener el operador de dirección de aplicación aplicado a ellas. Es por eso que funciona su segundo método (o el método de Dark Falcon), aunque tenga en cuenta que el valor del puntero devuelto es temporal, lo que significa que si realiza operaciones en el objeto std::string, podría invalidar el puntero desde el objeto std::string internamente maneja la memoria de sus estructuras de datos. Tan solo porque haya guardado el valor del puntero de retorno en una ubicación de memoria no significa que el puntero no se invalidará en algún momento posterior, y en un punto que tal vez no sea capaz de elegir de manera determinista.

+0

c_str() return se puede asignar al valor l – CharlesB

+2

@ CharlesB: Sí, el valor de retorno se puede asignar a un valor l, pero el valor de retorno en sí no es un valor l, por lo tanto no se puede aplicar la dirección -de operador a la devolución de 'std :: string :: c_str()'. – Jason

+0

OK, pero sus explicaciones están un poco fuera de lugar, no necesita este conocimiento para resolver el problema de OP – CharlesB

0

Trate de utilizar el .c_str() que le dan un char * que se puede usar, ya que trabajó para usted b4

#include <string> 

void ConversionSample() 
{ 
std::string strTest ("This is a string"); 
const char* pszConstString = strTest.c_str(); 
} 
2

sólo quiero señalar que el puntero devuelto por c_str() sólo es válido siempre que no haga nada que requiera la reasignación del búfer interno de std :: string. Eso invalida el puntero que tienes.

Pero ya que realmente necesita una ** Me gustaría hacer esto:

const char* mychararr[1] = {data.c_str()}; 
glShaderSource(shader, 1, mychararr, NULL); 

que debería funcionar muy bien, siempre y cuando no se salga del alcance de mychararr.

+0

No creo que la inicialización se compile (en C++ 03). – sbi

+0

@sbi: GCC está contento con esas cosas, pero creo que tienes razón en que no es estándar. –

+0

@Tony: Creo que es estándar, pero del estándar C++ 11 aún por lanzar. – sbi

7

glShaderSource firma es, de acuerdo con glShaderSource doc:

void glShaderSource(
    GLuint shader, 
    GLsizei count, 
    const GLchar** string, 
    const GLint* length); 

donde string "especifica una matriz de punteros a cadenas que contienen el código fuente para ser cargado en el shader". Lo que está intentando pasar es un puntero a una cadena terminada NULL (es decir, un puntero a const char*).

Por desgracia, no estoy familiarizado con glShaderSource, pero puedo adivinar que no se espera un puntero a "un código", pero algo como esto en su lugar:

const char** options = 
{ 
    "option1", 
    "option2" 
    // and so on 
}; 

De opengl-redbook, se puede leer un ejemplo (I lo ha acortado a propósito):

const GLchar* shaderSrc[] = { 
    "void main()", 
    "{", 
    " gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;", 
    "}" 
}; 
shader = glCreateShader(GL_VERTEX_SHADER); 
glShaderSource(shader, NumberOfLines(shaderSrc), shaderSrc, NULL); 
+1

'+ 1' para ver la documentación de funciones! – sbi

+0

@sbi y @mister, ¿cuál es el sentido de dividir la cadena en líneas como esa? funciona bien cuando todo está en el primer elemento de la matriz. – Rookie

+0

@Rookie: esto es lo que desea si quiere pasar una matriz de más de una cadena. Como solo quieres pasar una cadena, supongo que no se aplica a ti. – sbi

11

Puede obtener una llamada razonable utilizando una clase de ayuda. Definir esta clase:

struct StringHelper { 
    const char *p; 
    StringHelper(const std::string& s) : p(s.c_str()) {} 
    operator const char**() { return &p; } 
}; 

Entonces, cuando es necesario llamar a glShaderSource, hacerlo de esta manera:

glShaderSource(shader, 1, StringHelper(data), NULL); 
+0

Utilicé esto. Tenía varias cadenas para convertir, y podía esconder el código de implementación y hacer todo en una sola línea. Perfecto. – GCon

1

Shader.cpp

#include "Shader.hpp" 

Shader::Shader(GLenum type) 
{ 
    this->_type = type; 
} 
Shader::~Shader() {}  

GLuint Shader::get(char* filename) 
{ 
    GLuint shdr = glCreateShader(this->_type); 
    FILE* f = 0; 
    f = fopen(filename, "r+"); 
    char* str_tmp = 0; 
    char** shdr_text = 0; 
    shdr_text = (char**)malloc(sizeof(char**) * 255); 
    str_tmp = (char*)malloc(sizeof(char*) * 255); 
    int i = 0, ch = 0, n = 0; 

    for(i = 0; i < 255; ++i){ *(shdr_text + i) = (char*)malloc(sizeof(char*) * 255); } 

    i = 0; 
    while((ch = fgetc(f)) != EOF) 
    { 
     sprintf(str_tmp, "%s%c", str_tmp, ch); 
     if(ch == (int)'\n' || ch == (int)'\r') 
     { 
      sprintf(*(shdr_text + i), "%s", str_tmp); 
      sprintf(str_tmp, ""); 
      ++i; 
     } 
    } 

    free(str_tmp); 
    fclose(f); 

    glShaderSource(shdr, i, const_cast<const GLchar**>(shdr_text), 0); 
    glCompileShader(shdr); 

    free(shdr_text); 

    return(shdr); 
} 

Shader.hpp

#ifndef SHADER_HPP 
#define SHADER_HPP 

#include <stdlib.h> 
#include <stdio.h> 
#include <GL/glew.h> 
#include <GL/gl.h> 

class Shader 
{ 
    public: 
     Shader(GLenum type); 
     virtual ~Shader(); 

     GLuint get(char* filename); 

    private: 
     GLenum _type; 
}; 

#endif 
+1

Por favor comente su código –

+0

grande +1, de vuelta a cero, tornillos de comentarios, esto es bueno, gracias por publicar. –

-2

Uso de direcciones de cadena s y echándola también trabaja en una línea:

glShaderSource(vertexShader, 1, (const char* const*)&vertexSource, NULL);

+0

Pero conduce a un comportamiento indefinido. De hecho, casi nunca funcionará. – Ruslan