如何安全地确保根据OpenGL规范正确实现char*类型(在任何平台上)? 在试图用C++和OpenGL3+来解决图形编程时,我遇到了一个稍微有点专门的理解问题:char类型、指针和潜在的隐式或显式转换到其他字符指针类型。我想我已经找到了一个解决办法,但我想再次核实一下,请你对此有何看法

如何安全地确保根据OpenGL规范正确实现char*类型(在任何平台上)? 在试图用C++和OpenGL3+来解决图形编程时,我遇到了一个稍微有点专门的理解问题:char类型、指针和潜在的隐式或显式转换到其他字符指针类型。我想我已经找到了一个解决办法,但我想再次核实一下,请你对此有何看法,c++,opengl,type-conversion,type-safety,char-pointer,C++,Opengl,Type Conversion,Type Safety,Char Pointer,当前(2014年10月)(第2.2章命令语法中的表2.2)列出了OpenGL数据类型并明确说明 总账类型不是C类型。因此,例如,GL类型int在本文档之外被称为GLint,不一定等同于C类型int。实现必须使用表中指示的准确位数来表示GL类型 此表中的GLchar类型指定为位宽8的类型,用于表示组成字符串的字符。 为了进一步缩小GLchar必须提供的范围,我们可以看看(OpenGL着色语言4.502014年7月,第3.1章字符集和编译阶段): OpenGL着色语言使用的源字符集是UTF-8编码方

当前(2014年10月)(第2.2章命令语法中的表2.2)列出了OpenGL数据类型并明确说明

总账类型不是C类型。因此,例如,GL类型int在本文档之外被称为GLint,不一定等同于C类型int。实现必须使用表中指示的准确位数来表示GL类型

此表中的GLchar类型指定为位宽8的类型,用于表示组成字符串的字符。
为了进一步缩小GLchar必须提供的范围,我们可以看看(OpenGL着色语言4.502014年7月,第3.1章字符集和编译阶段):

OpenGL着色语言使用的源字符集是UTF-8编码方案中的Unicode

现在,在我想要寻找的任何OpenGL库头中实现这一点的方法是一个简单的

typedef char GLchar;
这当然与我刚才引用的“GL类型不是C类型”的说法背道而驰

通常情况下,这不会是一个问题,因为typedef只适用于底层类型可能在将来发生更改的情况

问题从用户实现开始

在阅读一些OpenGL教程时,我遇到了各种方法,可以将GLSL源代码分配给处理它所需的GLchar数组。(请原谅我没有提供所有链接。目前,我没有这样做所需的声誉。)

网站open.gl喜欢这样做:

const GLchar* vertexSource =
"#version 150 core\n"
"in vec2 position;"
"void main() {"
"   gl_Position = vec4(position, 0.0, 1.0);"
"}";
或者这个:

// Shader macro
#define GLSL(src) "#version 150 core\n" #src

// Vertex shader
const GLchar* vertexShaderSrc = GLSL(
  in vec2 pos;

  void main() {
      gl_Position = vec4(pos, 0.0, 1.0);
  }
);
在lazyfoo.net(第30章加载文本文件着色器)上,源代码从文件(我的首选方法)读取到
std::string shaderString
变量中,然后该变量用于初始化GL字符串:

const GLchar* shaderSource = shaderString.c_str();
迄今为止,我所见过的最冒险的方法是我在谷歌加载着色器文件时得到的第一个方法——OpenGL SDK上托管的使用显式强制转换的时钟编码器加载教程——不是
GLchar*
,而是
GLubyte*
——如下所示:

GLchar** ShaderSource;
unsigned long len;
ifstream file;
// . . .
len = getFileLength(file);
// . . .
*ShaderSource = (GLubyte*) new char[len+1];

任何适当的C++编译器都会在这里给出一个无效的转换错误。仅当设置了-fppermissive标志时,g++编译器才会发出警告。通过这种方式编译,代码将起作用,因为

GLubyte
最终只是基本类型
unsigned char
的一个
typedef
别名,其长度与
char
相同。在这种情况下,隐式指针转换可能会生成警告,但仍然应该做正确的事情。这违背了C++标准,其中<代码> char *<代码>与<代码>签名< /COD>或<代码>未签名CHAR*<代码>不兼容,这样做是不好的做法。这就引出了我的问题:

我的观点是,所有这些教程都依赖于一个基本事实,即OpenGL规范的实现目前只是基本类型的typedefs形式的窗口修饰。本规范并未涵盖此假设。更糟糕的是,明确不鼓励将GL类型视为C类型

如果在将来的任何时候,OpenGL实现应该改变——无论出于何种原因,
GLchar
不再是
char
的简单的
typedef
别名,这样的代码将不再编译,因为指向不兼容类型的指针之间没有隐式转换。在某些情况下,告诉编译器忽略无效的指针转换当然是可能的,但这样打开错误编程的大门可能会导致代码中的所有其他问题

就我的理解而言,我只看到了一个地方:opengl.org官方的着色器编译wiki示例,即:

std::string vertexSource = //Get source code for vertex shader.
// . . .
const GLchar *source = (const GLchar *)vertexSource.c_str();
与其他教程的唯一区别是在作业之前显式转换为
const GLchar*
。丑陋,我知道,然而,就我所见,它使代码对OpenGL规范(总结)的任何有效未来实现都是安全的:一种表示UTF-8编码方案中字符的位大小为8的类型

为了说明我的推理,我编写了一个简单的类
GLchar2
,该类满足此规范,但不再允许与任何基本类型进行隐式指针转换:

// GLchar2.h - a char type of 1 byte length

#include <iostream>
#include <locale> // handle whitespaces

class GLchar2 {
  char element; // value of the GLchar2 variable
public:
  // default constructor
  GLchar2 () {}
  // user defined conversion from char to GLchar2
  GLchar2 (char element) : element(element) {}
  // copy constructor
  GLchar2 (const GLchar2& c) : element(c.element) {}
  // destructor
  ~GLchar2 () {}
  // assignment operator
  GLchar2& operator= (const GLchar2& c) {element = c; return *this;}
  // user defined conversion to integral c++ type char
  operator char () const {return element;}
};

// overloading the output operator to correctly handle GLchar2
// due to implicit conversion of GLchar2 to char, implementation is unnecessary
//std::ostream& operator<< (std::ostream& o, const GLchar2 character) {
//  char out = character;
//  return o << out;
//}

// overloading the output operator to correctly handle GLchar2*
std::ostream& operator<< (std::ostream& o, const GLchar2* output_string) {
  for (const GLchar2* string_it = output_string; *string_it != '\0'; ++string_it) {
    o << *string_it;
  }
  return o;
}

// overloading the input operator to correctly handle GLchar2
std::istream& operator>> (std::istream& i, GLchar2& input_char) {
  char in;
  if (i >> in) input_char = in; // this is where the magic happens
  return i;
}

// overloading the input operator to correctly handle GLchar2*
std::istream& operator>> (std::istream& i, GLchar2* input_string) {
  GLchar2* string_it;
  int width = i.width();
  std::locale loc;
  while (std::isspace((char)i.peek(),loc)) i.ignore(); // ignore leading whitespaces
  for (string_it = input_string; (((i.width() == 0 || --width > 0) && !std::isspace((char)i.peek(),loc)) && i >> *string_it); ++string_it);
  *string_it = '\0'; // terminate with null character
  i.width(0); // reset width of i
  return i;
}

我总结出,至少有两种可行的方法来编写代码,这些代码在不违反C++标准的情况下,将始终正确处理<代码> GLCHAR 字符串:

  • 使用从char数组到
    GLchar
    数组的显式转换(不整洁,但可行)

    const GLchar*sourceCode=(const GLchar*)“一些代码”

    std::string sourceString=std::string(“某些代码”);//可以从文件中删除
    GLchar*sourceCode=(GLchar*)sourceString.c_str()

  • 使用输入流操作符将字符串从文件直接读取到
    GLchar
    数组中

  • 第二种方法的优点是不需要显式转换,但要实现它,必须动态分配字符串的空间。另一个潜在的缺点是OpenGL不一定为输入和输出流操作符提供重载来处理它们的类型或指针类型。然而,正如我所展示的,只要至少实现了与char之间的类型转换,那么自己编写这些重载就不是什么巫术

    到目前为止,我还没有发现任何其他可行的重载,用于提供exactl的文件的输入
    // program: test_GLchar.cpp - testing implementation of GLchar
    
    #include <iostream>
    #include <fstream>
    #include <locale> // handle whitespaces
    #include "GLchar2.h"
    
    typedef char GLchar1;
    
    int main () {
      // byte size comparison
      std::cout << "GLchar1 has a size of " << sizeof(GLchar1) << " byte.\n"; // 1
      std::cout << "GLchar2 has a size of " << sizeof(GLchar2) << " byte.\n"; // 1
      // char constructor
      const GLchar1 test_char1 = 'o';
      const GLchar2 test_char2 = 't';
      // default constructor
      GLchar2 test_char3;
      // char conversion
      test_char3 = '3';
      // assignment operator
      GLchar2 test_char4;
      GLchar2 test_char5;
      test_char5 = test_char4 = 65; // ASCII value 'A'
      // copy constructor
      GLchar2 test_char6 = test_char5;
      // pointer conversion
      const GLchar1* test_string1 = "test string one"; // compiles
      //const GLchar1* test_string1 = (const GLchar1*)"test string one"; // compiles
      //const GLchar2* test_string2 = "test string two"; // does *not* compile!
      const GLchar2* test_string2 = (const GLchar2*)"test string two"; // compiles
    
      std::cout << "A test character of type GLchar1: " << test_char1 << ".\n"; // o
      std::cout << "A test character of type GLchar2: " << test_char2 << ".\n"; // t
      std::cout << "A test character of type GLchar2: " << test_char3 << ".\n"; // 3
      std::cout << "A test character of type GLchar2: " << test_char4 << ".\n"; // A
      std::cout << "A test character of type GLchar2: " << test_char5 << ".\n"; // A
      std::cout << "A test character of type GLchar2: " << test_char6 << ".\n"; // A
    
      std::cout << "A test string of type GLchar1: " << test_string1 << ".\n";
      // OUT: A test string of type GLchar1: test string one.\n
      std::cout << "A test string of type GLchar2: " << test_string2 << ".\n";
      // OUT: A test string of type GLchar2: test string two.\n
    
      // input operator comparison
      // test_input_file.vert has the content
      //  If you can read this,
      //  you can read this.
      // (one whitespace before each line to test implementation)
      GLchar1* test_string3;
      GLchar2* test_string4;
      GLchar1* test_string5;
      GLchar2* test_string6;
      // read character by character
      std::ifstream test_file("test_input_file.vert");
      if (test_file) {
        test_file.seekg(0, test_file.end);
        int length = test_file.tellg();
        test_file.seekg(0, test_file.beg);
    
        test_string3 = new GLchar1[length+1];
        GLchar1* test_it = test_string3;
        std::locale loc;
        while (test_file >> *test_it) {
          ++test_it;
          while (std::isspace((char)test_file.peek(),loc)) {
            *test_it = test_file.peek(); // add whitespaces
            test_file.ignore();
            ++test_it;
          }
        }
        *test_it = '\0';
        std::cout << test_string3 << "\n";
        // OUT: If you can read this,\n you can read this.\n
        std::cout << length << " " <<test_it - test_string3 << "\n";
        // OUT: 42 41\n
        delete[] test_string3;
        test_file.close();
      }
      std::ifstream test_file2("test_input_file.vert");
      if (test_file2) {
        test_file2.seekg(0, test_file2.end);
        int length = test_file2.tellg();
        test_file2.seekg(0, test_file2.beg);
    
        test_string4 = new GLchar2[length+1];
        GLchar2* test_it = test_string4;
        std::locale loc;
        while (test_file2 >> *test_it) {
          ++test_it;
          while (std::isspace((char)test_file2.peek(),loc)) {
            *test_it = test_file2.peek(); // add whitespaces
            test_file2.ignore();
            ++test_it;
          }
        }
        *test_it = '\0';
        std::cout << test_string4 << "\n";
        // OUT: If you can read this,\n you can read this.\n
        std::cout << length << " " << test_it - test_string4 << "\n";
        // OUT: 42 41\n
        delete[] test_string4;
        test_file2.close();
      }
      // read a word (until delimiter whitespace)
      test_file.open("test_input_file.vert");
      if (test_file) {
        test_file.seekg(0, test_file.end);
        int length = test_file.tellg();
        test_file.seekg(0, test_file.beg);
    
        test_string5 = new GLchar1[length+1];
        //test_file.width(2);
        test_file >> test_string5;
        std::cout << test_string5 << "\n";
        // OUT: If\n
        delete[] test_string5;
        test_file.close();
      }
      test_file2.open("test_input_file.vert");
      if (test_file2) {
        test_file2.seekg(0, test_file2.end);
        int length = test_file2.tellg();
        test_file2.seekg(0, test_file2.beg);
    
        test_string6 = new GLchar2[length+1];
        //test_file2.width(2);
        test_file2 >> test_string6;
        std::cout << test_string6 << "\n";
        // OUT: If\n
        delete[] test_string6;
        test_file2.close();
      }
      // read word by word
      test_file.open("test_input_file.vert");
      if (test_file) {
        test_file.seekg(0, test_file.end);
        int length = test_file.tellg();
        test_file.seekg(0, test_file.beg);
    
        test_string5 = new GLchar1[length+1];
        GLchar1* test_it = test_string5;
        std::locale loc;
        while (test_file >> test_it) {
          while (*test_it != '\0') ++test_it; // test_it points to null character
          while (std::isspace((char)test_file.peek(),loc)) {
            *test_it = test_file.peek(); // add whitespaces
            test_file.ignore();
            ++test_it;
          }
        }
        std::cout << test_string5 << "\n";
        // OUT: If you can read this,\n you can read this.\n
        delete[] test_string5;
        test_file.close();
      }
      test_file2.open("test_input_file.vert");
      if (test_file2) {
        test_file2.seekg(0, test_file2.end);
        int length = test_file2.tellg();
        test_file2.seekg(0, test_file2.beg);
    
        test_string6 = new GLchar2[length+1];
        GLchar2* test_it = test_string6;
        std::locale loc;
        while (test_file2 >> test_it) {
          while (*test_it != '\0') ++test_it; // test_it points to null character
          while (std::isspace((char)test_file2.peek(), loc)) {
            *test_it = test_file2.peek(); // add whitespaces
            test_file2.ignore();
            ++test_it;
          }
        }
        std::cout << test_string6 << "\n";
        // OUT: If you can read this,\n you can read this.\n
        delete[] test_string6;
        test_file2.close();
      }
      // read whole file with std::istream::getline
      test_file.open("test_input_file.vert");
      if (test_file) {
        test_file.seekg(0, test_file.end);
        int length = test_file.tellg();
        test_file.seekg(0, test_file.beg);
    
        test_string5 = new GLchar1[length+1];
        std::locale loc;
        while (std::isspace((char)test_file.peek(),loc)) test_file.ignore(); // ignore leading whitespaces
        test_file.getline(test_string5, length, '\0');
        std::cout << test_string5  << "\n";
        // OUT: If you can read this,\n you can read this.\n
        delete[] test_string5;
        test_file.close();
      }
      // no way to do this for a string of GLchar2 as far as I can see
      // the getline function that returns c-strings rather than std::string is
      // a member of istream and expects to return *this, so overloading is a no go
      // however, this works as above:
    
      // read whole file with std::getline
      test_file.open("test_input_file.vert");
      if (test_file) {
        std::locale loc;
        while (std::isspace((char)test_file.peek(),loc)) test_file.ignore(); // ignore leading whitespaces
        std::string test_stdstring1;
        std::getline(test_file, test_stdstring1, '\0');
        test_string5 = (GLchar1*) test_stdstring1.c_str();
        std::cout << test_string5 << "\n";
        // OUT: If you can read this,\n you can read this.\n
        test_file.close();
      }
    
      test_file2.open("test_input_file.vert");
      if (test_file2) {
        std::locale loc;
        while (std::isspace((char)test_file2.peek(),loc)) test_file2.ignore(); // ignore leading whitespaces
        std::string test_stdstring2;
        std::getline(test_file2, test_stdstring2, '\0');
        test_string6 = (GLchar2*) test_stdstring2.c_str();
        std::cout << test_string6 << "\n";
        // OUT: If you can read this,\n you can read this.\n
        test_file.close();
      }
    
      return 0;
    }
    
    #include <stdint.h>
    typedef int8_t GLchar;
    
    typedef signed char int8_t;
    
    int x;
    istream &is = ...;
    is.read((char*)&x, sizeof(x));