最近一直在thinking inmodern C++,大部分都是使用基於對象(object based)的技術。使得對象相較於C++的面向對象風格的程序而言,對象要散列一些,結構更清晰。對象之間適配使用了大量的泛型編程(Generic programming)技術。Boost C++庫的發展和C++11語言新特性對模板的支持,優秀的泛型庫大量湧現。它們都繼承了STL極高的可復用性與很低的學習曲線。Jeremy Ong寫的Selene庫是筆者目前閱讀過的非常精彩的modern C++風格的程序庫,但也有一些show off的感覺。還有兩個數學庫glm與mtl。
對於圖形API的C++ wapper庫確實不多,OGLplus算是一個。代碼並算不上是非常精彩,諸如沒有使用traits/policy編程技法來降低復雜性,使用了decltype關鍵字這種較為初學的泛型技術,沒有考慮空基類優化等細節問題。但也有很多出彩的地方,比如對OpenGL具名對象的封裝(參見模板類Named)以及對OpenGL的Operation的封裝達到完全消除對OpenGL API函數的裸調用(Raw Call)等。
OGLplus是一個僅包含頭文件(header only)的modern C++風格的庫。它主要是對OpenGL的C++包裝(Wrapper)。官網上面,它自稱是面向對象(Object oriented)的OpenGL外觀,但是筆者認為它是基於對象的(Object based)。下面用一段代碼來展示OGLplus的Object based風格的使用。
#include <cassert>
#include <iostream>
#include <GL/glew.h>
#include <GL/glut.h>
#include <oglplus/all.hpp>
class Example
{
private:
oglplus::Context gl;
oglplus::VertexShader vs;
oglplus::FragmentShader fs;
oglplus::Program prog;
oglplus::Buffer verts;
public:
Example(void)
{
using namespace oglplus;
vs.Source(" \
#version 330\n \
in vec3 Position; \
void main(void) \
{ \
gl_Position = vec4(Position, 1.0); \
} \
");
vs.Compile();
fs.Source(" \
#version 330\n \
out vec4 fragcolor; \
void main(void) \
{ \
fragcolor = vec4(1.0, 0.0, 0.0, 1.0); \
} \
");
fs.Compile();
prog.AttachShader(vs);
prog.AttachShaders(fs);
prog.Link();
prog.Use();
GLfloat triangle_verts[9] = {
0.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f
};
verts.Bind(Buffer::Target::Array);
verts.Data(Buffer::Target::Array, 9, triangle_verts);
VertexAttribArray vert_attr(prog, "Position");
vert_attr.Setup<GLfloat>(3);
vert_attr.Enable();
gl.ClearColor(0.0f, 0.0f, 0.0f, 0.0f);
gl.ClearDepth(1.0f);
}
void Display(void)
{
using namespace oglplus;
gl.Clear().ColorBuffer().DepthBuffer();
gl.Viewport( 0,0, 800, 600 );
gl.DrawArrays(PrimitiveType::TriangleStrip, 0, 3);
}
static Example& GetInstance()
{
static Example example;
return example;
}
};
static void Display(void)
{
Example::GetInstance().Display();
glutSwapBuffers();
}
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
glutInitWindowSize(800, 600);
glutInitWindowPosition(100,100);
glutCreateWindow("OGLplus+GLUT+GLEW");
if(glewInit() == GLEW_OK) try
{
glGetError();
glutDisplayFunc(Display);
glutMainLoop();
return 0;
}
catch(oglplus::Error& err)
{
std::cerr <<
"Error (in " << err.GLSymbol() << ", " <<
err.ClassName() << ": '" <<
err.ObjectDescription() << "'): " <<
err.what() <<
" [" << err.File() << ":" << err.Line() << "] ";
std::cerr << std::endl;
err.Cleanup();
}
return 1;
}
Example類包裹的是OpenGL標准,這也是OGLplus所做的所有關於OpenGL的事情。main函數與一個glut的渲染回調::Display()都是與平台gl實現相關的代碼,這些OGLplus並沒有考慮,也算是合理的。雖然OGLplus在OpenGL API外層封裝了C++風格的Wrapper,但OGLplus保留了OpenGL的狀態機編程風格,使得擁有一定OpenGL編程經驗的程序員能夠非常快的上手。OGLplus的封裝到底做了哪些事情呢?
OGLplus是類型安全的OpenGL包裝。OpenGL管理創建在OpenGL環境(context, 在不同實現中由不同的系統內核對象管理)下的名字對象(如Buffer,Texture,Shader,FrameBuffer等),通常用一個GLuint類型的標識。這種無型別的標識使得程序在運行期出錯的幾率大大增加。資源在創建時便已知其型別,使用C++型別來管理這些OpenGL資源,應屬上策。OpenGL的名字對象由oglplus::Named類統一管理,這也是官網提及的資源自動管理的特性支持的基礎組件。OGLplus為我們封裝了所有的OpenGL函數調用,並同時封裝了調用這些函數的異常處理,並拋出C++異常。OGLplus的封裝,是不是讓代碼變得clean了許多呢?
最後再來談談我覺得這段代碼一個非常出彩的地方。就是Example::Display方法中的gl.Clear().ColorBuffer().DepthBuffer()一句。這一句fluent interface風格的代碼中的三次函數調用創建了三個oglplus::context::ClrBits的對象,Clear()創建一個空對象為了接下來的調用提供interface,後面依然依次創建ClrBIts型別的對象,並為後面的調用提供接口。創建的對象都是臨時對象,離開代碼段即進行對象的析構,而在ClrBits型別的析構函數中調用的則是glClear方法。這是使用RAII對象的良好實踐。
好了,Outline先寫到這裡,modern C++設計的旅程剛剛開始。
OGLplus 的詳細介紹:請點這裡
OGLplus 的下載地址:請點這裡