"This is madness!" Red Book OpenGL Example 1-3, done using templates

downs default_357-line at yahoo.de
Tue Jul 31 18:49:33 PDT 2007

This is a translation of the Red Book's OpenGL Example 1-3 into D, without using any standard 
library functions or pre-existing OpenGL bindings. It makes extensive use of templates and contains 
a minimum of repeated information. I realize the whole heap is probably close to unreadable, but it 
helps to start from the bottom - main is almost clean, and to ignore the templated code for the 
beginning. Use in whole or part as you like, under any license - none of this is copyrightable anyway.

template Tuple(T...) { alias T Tuple; }

/// Import the NAMES as constants of type TYPE (must be :uint) into the namespace,
/// starting with OFFSET and moving in NEXT jumps
template _ConstEnums(TYPE, alias NEXT, uint OFFSET, NAMES...) {
   static if (!NAMES.length) const char[] _ConstEnums="";
   else {
     static assert(is(typeof(NAMES[0]): char[]),
       "Invalid _ConstEnums parameter "~NAMES[0].stringof~" which is "~typeof(NAMES[0]).stringof~".");
     const char[] _ConstEnums="const "~TYPE.stringof~" "~NAMES[0]~"="~OFFSET.stringof~"; "
       ~_ConstEnums!(TYPE, NEXT, NEXT(OFFSET), NAMES[1..$]);

typedef uint Enum; /// additive increase
typedef uint Bitfield; /// multiplicative increase

uint Inc(uint v) { return v+1; } /// for Enum
uint Dbl(uint v) { return v*2; } /// for Bitfield
/// convenient wrapper
template ConstEnums(TYPE, OFFS_AND_NAMES...) {
   static assert(OFFS_AND_NAMES.length>1);
   static assert(is(TYPE: uint));
   static if(is(TYPE: Enum))
     const char[] ConstEnums=_ConstEnums!(TYPE, Inc, OFFS_AND_NAMES);
   else {
     static assert(is(TYPE: Bitfield), "Don't know how to generate const-enum from TYPE 
     const char[] ConstEnums=_ConstEnums!(TYPE, Dbl, OFFS_AND_NAMES);

/// Makes sure all U are of type T
template AssertTypes(T, U...) {
   static if (U.length) {
     const char[] AssertTypes=
       "static assert(is("~typeof(U[0]).stringof~": "~T.stringof~"), \"
         Type assertion failed - cannot convert "~typeof(U[0]).stringof~" to "~T.stringof~"\"); "
     ~AssertTypes!(T, U[1..$]);
   } else const char[] AssertTypes="";

/// all STRINGS[2..$] are prepended STRINGS[0] and appended STRINGS[1]
template PrePost(STRINGS...) {
   mixin(AssertTypes!(char[], STRINGS));
   static assert(STRINGS.length!<3, "Insufficient argument count for PrePost");
   static if(STRINGS.length>3) alias Tuple!(STRINGS[0]~STRINGS[2]~STRINGS[1], 
PrePost!(STRINGS[0..2], STRINGS[3..$])) PrePost;
   else alias Tuple!(STRINGS[0]~STRINGS[2]~STRINGS[1]) PrePost;

template Prepend(STRINGS...) { alias PrePost!(STRINGS[0], "", STRINGS[1..$]) Prepend; }
template Append(STRINGS...) { alias PrePost!("", STRINGS[0], STRINGS[1..$]) Append; }

extern(C) {
   typedef Enum StringName;
   mixin(ConstEnums!(StringName, 0x1F00, Prepend!("GL_", "VENDOR", "RENDERER", "VERSION", 
   typedef Enum ShadeModel;
   mixin(ConstEnums!(ShadeModel, 0x1D00, Prepend!("GL_", "FLAT", "SMOOTH")));
   typedef Bitfield AttribMask;
   mixin(ConstEnums!(AttribMask, 1, PrePost!("GL_", "_BIT",
     Append!("_BUFFER", "DEPTH", "ACCUM", "STENCIL"), "VIEWPORT",
     "EVAL", "LIST", "TEXTURE", "SCISSORS")));
   const AttribMask GL_ALL_ATTRIB_BITS=0xFFFF_FFFF;
   typedef Enum MatrixMode;
   mixin(ConstEnums!(MatrixMode, 0x1700, Prepend!("GL_", "MODELVIEW", "PROJECTION", "TEXTURE")));

   char *glGetString(StringName);
   Enum glGetError();

class glException : Exception { this(char[] s) { super(s); } }

T glCheck(T)(lazy T lv) {
   void checkError() {
     auto error=glGetError();
     if (error) throw new glException(format("GL error: ", error));
   static if(is(T==void)) {
     lv(); checkError;
   } else {
     auto res=lv();
     return res;

extern(C) {
   void glClearColor(float red=0f, float green=0f, float blue=0f, float alpha=0f);
   void glShadeModel(ShadeModel mode);
void init() {

extern(C) {
   void glPushMatrix();
   void glPopMatrix();
void glMatrix(void delegate() dg) {
   glPushMatrix(); dg(); glPopMatrix();

/// Look up the full type name for the OpenGL type suffix typeID
char[] glLookupType(char[] typeID) {
   foreach (type; ["byte", "double", "float", "int", "short", "ubyte", "uint", "ushort"]) {
     if(type[0..typeID.length]==typeID) return type;
   assert(false, "Invalid type: "~typeID);

/// "type, type, type" .. length is count
char[] paramList(int count, string type) {
   if (count>1) return type~", "~paramList(count-1, type); return type;

char[] glColorMixin(int params, char[] typeID, bool vector) {
   return "void glColor"~(params==3?"3":"4")~typeID~(vector?"v":"")~" ("~
     (vector?glLookupType(typeID)~" *); ":paramList(params, glLookupType(typeID))~"); ");

/// Generate the mixin string for all the glColor variations
char[] glColors() {
   char[] res;
   foreach (count; [3, 4]) foreach (name; ["b", "d", "f", "i", "s", "ub", "ui", "us"])
     res~=glColorMixin(count, name, false)~glColorMixin(count, name, true);
   return res;
extern(C) mixin(glColors); /// all the glColor functions in a single concise mixin.

/// It's unsigned if it starts with 'u'. Pure simplicity.
template unsigned(T) {
   static if(T.stringof[0]=='u') const bool unsigned=true;
   else const bool unsigned=false;

/// A very generic glColor. Supports static arrays.
import std.traits;
void glColor(T...)(T t) {
   static if (T.length==1) { alias T[0] Thingie; const bool vector=true; alias typeof(t[0][0]) 
ElemType; }
   else { alias T Thingie; const bool vector=false; alias typeof(t[0]) ElemType; }
   const char[] count=Thingie.length.stringof;
   static assert(count=="3"||count=="4");
   const char[] type=(unsigned!(ElemType)?"u":"")~ElemType.stringof[0];
   static if(vector) mixin("glColor"~count~type~"v(t[0].ptr); ");
   else mixin("glColor"~count~type~"(t); ");

float spin=0f;
alias Tuple!(1f) Xf;
alias Tuple!(0f, 1f) Yf;
alias Tuple!(0f, 0f, 1f) Zf;
template pair(T...) { alias Tuple!(T, T) pair; }
extern(C) {
   void glClear(AttribMask mask);
   void glRotatef(float angle, float x=1, float y=0, float z=0);
   void glRectf(pair!(pair!(float)));
   void glutSwapBuffers();
   void display() {
       glRotatef(spin, Zf);
       glColor(1f, 1f, 1f);
       glRectf(pair!(-25f), pair!(25f));

extern(C) {
   void glutPostRedisplay();
   void spinDisplay() {
     if (spin>360) spin-=360;

extern(C) {
   void glViewport(pair!(pair!(int)));
   void glMatrixMode(MatrixMode);
   void glLoadIdentity();
   void glOrtho(pair!(double) leftright, pair!(double) bottomtop, pair!(double) nearfar);
   template plusminus(T...) {
     static if(T.length) alias Tuple!(T[0], -T[0], plusminus!(T[1..$])) plusminus;
     else alias Tuple!() plusminus;
   void reshape(pair!(int) size) {
     glViewport(0, 0, size);
     glCheck({glMatrixMode=GL_PROJECTION; glLoadIdentity; });
     glCheck(glOrtho(plusminus!(50f, 50f, 1f)));
     glCheck({glMatrixMode=GL_MODELVIEW; glLoadIdentity; });

extern(C) {
   typedef Enum MouseButton;
   mixin(ConstEnums!(MouseButton, 0, PrePost!("GLUT_", "_BUTTON", "LEFT", "MIDDLE", "RIGHT")));
   typedef Enum MouseState;
   mixin(ConstEnums!(MouseState, 0, Prepend!("GLUT_", "DOWN", "UP")));
   void glutIdleFunc(void function());
   void mouse(MouseButton button, MouseState state, pair!(int) where) {
     switch(button) {
       case GLUT_LEFT_BUTTON: if(state==GLUT_DOWN) glutIdleFunc=&spinDisplay; break;
       case GLUT_MIDDLE_BUTTON: if(state==GLUT_DOWN) glutIdleFunc=null; break;
       default: break;

import std.string;
extern(C) {
   void glutInit(int *argc, char **argv);
   void glutInitDisplayMode(uint mode);
   void glutInitWindowSize(pair!(int) size);
   void glutInitWindowPosition(pair!(int) pos);
   void glutDisplayFunc(void function());
   void glutReshapeFunc(void function(pair!(int) size));
   void glutMouseFunc(void function(MouseButton button, MouseState state, pair!(int) pos));

   int glutCreateWindow(char *name);
   typedef Bitfield DisplayMode;
   mixin(ConstEnums!(DisplayMode, 1, Prepend!(
   const DisplayMode GLUT_RGB=0, GLUT_RGBA=0, GLUT_SINGLE=0;
   void glutMainLoop();

int main(string[] args) {
   /// C-ify the args
   int len=args.length;
   char *[] ptrs; foreach (entry; args) ptrs~=toStringz(entry);
   glutInit(&len, ptrs.ptr);
   glutInitDisplayMode=GLUT_DOUBLE | GLUT_RGB;
   return 0;

More information about the Digitalmars-d-learn mailing list