README.md
ef2b3669
 # [`gltraits`][]
 
27d69715
 A [C++11][](/[C++14][])/[OpenGL][]>=[1.0][](/[GLM][]) [trait][]s library.
ef2b3669
 
 This library seeks to unify some parts of the OpenGL [API][] to ease [generic
 programming][]. It also provides sensible default arguments, [optional][debug]
 [check][]s for [version][]/[extension][] support, and optional support for
 [OpenGL Mathematics (GLM)][GLM] (which provides [GLSL][]-like types).
 
 A more philosophical description: it aims to make the implicit symmetries of
 the OpenGL API explicit. In the process, some wrinkles in the symmetry are also
 brought to light. Therefore, it may be useful for learning and understanding
 the OpenGL API (although this use case may be limited due to the (ab)use of C++
 language features).
 
e38947dc
 This header-only library makes heavy use of macros. For easy inspection of the
 results, a script that runs the preprocessor is available in
 [`doc/preprocess`][] and its output in [`doc/preprocess.hpp`][].
 
 A typical use case is demonstrated by this quote from [OGLDEV on YouTube][]:
 
 > Notice, in order to make this an unsigned integer texture we use `GL_RGB32UI`
 > as the `internal_format`, `GL_RGB_INTEGER` as the `format`, and
 > `GL_UNSIGNED_INT` as the data `type`. Combining all these formats and types
 > correctly in OpenGL can often be a pain, and I literally pulled the last few
 > pieces of hair from my head trying to get this to work.
 
 These constants are provided in `GLTraits::Value<glm::uvec3>`. Of course, this
 becomes even more valuable when `glm::uvec3` is replaced by some unknown
 template parameter.
 
3378873e
 The function call seen in the video
 
 ```cpp
 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32UI, WindowWidth, WindowHeight, 0, GL_RGB_INTEGER, GL_UNSIGNED_INT, NULL);
 ```
 
 can be replaced with
 
 ```cpp
 GLTraits::Texture<2>::tex_image<glm::uvec3>(GL_TEXTURE_2D, {WindowWidth, WindowHeight});
 ```
 
 It is recommended to use a library based on `gltraits` that abstracts this
 further.
 
ef2b3669
 [`gltraits`]: https://git.rcrnstn.net/rcrnstn/gltraits
 [C++11]: https://en.wikipedia.org/wiki/C++11
27d69715
 [C++14]: https://en.wikipedia.org/wiki/C++14
ef2b3669
 [OpenGL]: https://en.wikipedia.org/wiki/OpenGL
 [1.0]: https://en.wikipedia.org/wiki/OpenGL#Version_history
 [GLM]: https://glm.g-truc.net
 [trait]: https://en.wikipedia.org/wiki/Trait_(computer_programming)
 [API]: https://en.wikipedia.org/wiki/API
 [generic programming]: https://en.wikipedia.org/wiki/Generic_programming
 [debug]: https://git.rcrnstn.net/rcrnstn/glbase#debug
 [check]: https://git.rcrnstn.net/rcrnstn/glbase#check
 [version]: https://en.wikipedia.org/wiki/OpenGL#Version_history
 [extension]: https://www.khronos.org/opengl/wiki/OpenGL_Extension
 [GLSL]: https://www.khronos.org/opengl/wiki/OpenGL_Shading_Language
e38947dc
 [`doc/preprocess`]: doc/preprocess
 [`doc/preprocess.hpp`]: doc/preprocess.hpp
 [OGLDEV on YouTube]: https://www.youtube.com/watch?v=71G-PVpaVk8&t=5m17s
 
 ## Usage
 
 ### Value
 
 The [empty][] `struct GLTraits::Value<typename Value>` is template specialized
 on the following types.
 
 -   `GLfloat`
 -   `bool`
 -   `GL{,u}byte`
 -   `GL{,u}short`
 -   `GL{,u}int`
 -   `GLdouble`
 
 [GLM][] support is enabled if `glm/glm.hpp` is included (specifically, if
 `GLM_VERSION` is defined) before inclusion of `gltraits.hpp`. In that case
 `GLTraits::Value` is additionally template specialized on the following types.
 
 -   `glm::{,i,u,d}vec{2,3,4}`
 -   `glm::{,d}mat{2{,x3,x4},3{x2,,x4},4{x2,x3,}}`
 
 `GLTraits::Value` contains the following `static constexpr` member variables.
 
 -   `char   const name[]`
 -   `GLint        columns`
 -   `GLint        rows`
 -   `GLenum       glsl`
 -   `GLenum       format`
 -   `GLenum       type`
 -   `GLenum       internal_format`
 -   `GLenum       internal_format_srgb`
 -   `GLenum       internal_format_compressed`
 -   `GLenum       internal_format_compressed_srgb`
 -   `bool         integer`
a6304722
 -   `GLenum       id`
 
 `id` is guaranteed to be unique to this `Value` and is equal to `glsl` for all
 `Value`s except `GL{,u}{byte,short}`, for which it is equal to `type`.
e38947dc
 
 `GLTraits::Value` contains the following `static` member functions.
 
 -   ```cpp
     void uniform(GLint location, Value const & value)
     ```
 
     Generalization of `glUniform*`.
 
     Uploads `value` to the [uniform][] indicated by `location` of the current
     [shader][] program.
 
 -   ```cpp
     void vertex_attrib(GLint location, Value const & value)
     ```
 
     Generalization of `glVertexAttrib*`.
 
     Uploads `value` to the [non-array attribute][] indicated by `location`.
     Note that [`glDisableVertexAttribArray`][] is not called (for performance).
 
 -   ```cpp
     void vertex_attrib_pointer(
         GLint       location,
         std::size_t offset = 0,
         std::size_t stride = sizeof(Value)
     )
     ```
 
     Generalization of `glVertexAttrib*Pointer`.
 
     Sets the [format][] as well as the [offset and stride][] of the [array
     attribute][] indicated by `location`. Note that
     [`glEnableVertexAttribArray`][] is not called (for performance).
 
 If `location` is `-1`, the above calls will do nothing. No error will be
 generated in this case.
 
 Note that matrix types (e.g. from [GLM][], if enabled), occupy several
 consecutive attribute locations (one per column), which are all handled
 automatically by `vertex_attrib{,_pointer}(...)` above.
 
 `GLTRAITS_VALUE*` macros are defined to ease the definition of new template
 specializations of `GLTraits::Value`. Consult the source for the definitions
 and usage examples.
 
a6304722
 The [empty][] `struct GLTraits::ValueID<GLenum id>` is template specialized on
 the different values of `GLTraits::Value<Value>::id`.
 
 `GLTraits::ValueID` contains the following type definitions.
 
 -   `Value`. An alias for the template type parameter for which
     `GLTraits::Value<Value>::id == id` holds.
 
 `GLTraits::ValueID` provides a compile time mapping from `GLenum id` back to
 the type `Value`. E.g. `GLTraits::ValueID<GL_FLOAT>::Value` is an alias for
 `GLfloat`. This works for all supported types, including those provided by
 [GLM][] if enabled, e.g. `GLTraits::ValueID<GL_FLOAT_VEC3>::Value` would be an
 alias for `glm::vec3`.
 
e38947dc
 [empty]: https://en.cppreference.com/w/cpp/types/is_empty
 [uniform]: https://www.khronos.org/opengl/wiki/Uniform_(GLSL)
 [shader]: https://www.khronos.org/opengl/wiki/Shader
 [non-array attribute]: https://www.khronos.org/opengl/wiki/Vertex_Specification#Non-array_attribute_values
 [format]: https://www.khronos.org/opengl/wiki/Vertex_Specification#Vertex_format
 [offset and stride]: https://www.khronos.org/opengl/wiki/Vertex_Specification#Vertex_buffer_offset_and_stride
 [array attribute]: https://www.khronos.org/opengl/wiki/Vertex_Specification#Vertex_Buffer_Object
 [`glDisableVertexAttribArray`]: https://registry.khronos.org/OpenGL-Refpages/gl4/html/glEnableVertexAttribArray.xhtml
 [`glEnableVertexAttribArray`]: https://registry.khronos.org/OpenGL-Refpages/gl4/html/glEnableVertexAttribArray.xhtml
c3bb4602
 
799e1ae5
 ### Object
 
 The [empty][] `struct GLTraits::Object<GLenum object_type>` is template
 specialized on the following values.
 
 -   `GL_TEXTURE`
 -   `GL_BUFFER`
 -   `GL_QUERY`
 -   `GL_PROGRAM`
 -   `GL_SHADER`
 -   `GL_VERTEX_ARRAY`
 -   `GL_FRAMEBUFFER`
 -   `GL_RENDERBUFFER`
 -   `GL_SAMPLER`
 -   `GL_TRANSFORM_FEEDBACK`
 -   `GL_PROGRAM_PIPELINE`
 
 `GLTraits::Object` contains the following `static constexpr` member variables.
 
 -   `char const name[]`
 
 `GLTraits::Object` contains the following `static` member functions.
 
 -   ```cpp
     template<typename... Args>
     void gen_objects(GLsizei n, GLuint * objects, Args... args)
     ```
 
     Generalization of `glGen*s` and `glCreate`.
 
 -   ```cpp
     void delete_objects(GLsizei n, GLuint * objects)
     ```
 
     Generalization of `glDelete*s` and `glDelete*`.
 
 For `object_types` equal to  `GL_SHADER`, `GL_PROGRAM`, and
 `GL_PROGRAM_PIPELINE`, `GLTraits::Object` additionally contains the following
 `static` member functions.
 
 -   ```cpp
     std::string info_log(GLuint object)
     ```
 
     Generalization of `glGet*InfoLog`.
 
 `GLTRAITS_OBJECT*` macros are defined to ease the definition of new template
 specializations of `GLTraits::Object`. Consult the source for the definitions
 and usage examples.
 
3378873e
 ### Texture
 
 The [empty][] `struct GLTraits::Texture<std::size_t N>` is template specialized
 on the following values.
 
 -   `1`. 1D textures.
 -   `2`. 2D textures.
 -   `3`. 3D textures.
 
 `GLTraits::Texture` contains the following type definitions.
 
 -   `Size`. An alias for `std::array<GLsizei, N>`.
 -   `Offset`. An alias for `std::array<GLsizei, N>`.
 -   `CopySize`. An alias for `std::array<GLsizei, min(N, 2)>`.
 -   `CopyOffset`. An alias for `std::array<GLsizei, 2>`.
 
 `GLTraits::Texture` contains the following `static constexpr` member variables.
 
 -   `default_target`. This is the default target, but many other targets can be
     used with the member functions described below. E.g. for `N = 2`
     `default_target` is `GL_TEXTURE_2D`, but valid `target` function arguments
     are `GL_{,PROXY}_TEXTURE_{2D,RECTANGLE,1D_ARRAY}`,
     `GL_TEXTURE_CUBE_MAP_{POSITIVE,NEGATIVE}_{X,Y,Z}`, and
     `GL_PROXY_TEXTURE_CUBE_MAP`.
 -   `default_binding`. Likewise, this is the default binding.
 
 `GLTraits::Texture` contains the following `static` member functions.
 
 -   ```cpp
     template<typename Value = GLubyte>
     void tex_image(
         GLenum        target,
         Size          size,
         GLenum        internal_format = 0,
         Value const * data            = nullptr,
         GLenum        format          = 0,
         GLenum        type            = 0,
         GLint         level           = 0,
         GLint         border          = 0
     )
     ```
 
     Generalization of `glTexImage{1,2,3}D`.
 
     OpenGL requires that `format` matches the kind of [image format][]
     specified by `internal_format` (even if `data` is `nullptr`!). Moreover, if
     `internal_format` specifies an unsized image format, `type` may be used by
     the implementation when choosing a size (perhaps even if that size is
     slow!). Therefore, if `format` and/or `type` is `0` (the default) sensible
     values will be chosen based on the given (or automatically chosen, see
     below) `internal_format` (note that there is no requirement that the number
     of color components of `internal_format` and `format` match):
 
     -   [Color formats][]: `GLTraits::Value<Value>` member variables.
     -   [Depth/stencil formats][]: `GL_DEPTH_STENCIL`, `GL_UNSIGNED_INT_24_8`.
     -   [Depth formats][]: `GL_DEPTH_COMPONENT`, `GL_UNSIGNED_INT`.
     -   [Stencil formats][]: `GL_STENCIL_INDEX`, `GL_UNSIGNED_BYTE`.
 
 -   ```cpp
     template<typename Value = GLubyte>
     void {tex,texture}_storage(
         {GLenum,GLuint} {target,texture},
         Size            size,
         GLenum          internal_format = 0,
         GLsizei         levels          = 1
     )
     ```
 
     Generalizations of `gl{Tex,Texture}Storage{1,2,3}D`, which both use
     [immutable storage][], where the latter uses [Direct State Access][].
 
 -   ```cpp
     template<typename Value = GLubyte>
     void {tex,texture}_sub_image(
         {GLenum,GLuint} {target,texture},
         Size            size,
         Value const *   data,
         GLenum          format = 0,
         GLenum          type   = 0,
         Offset          offset = {},
         GLint           level  = 0
     )
     ```
 
     Generalizations of `gl{Tex,Texture}SubImage{1,2,3}D`, where the latter uses
     [Direct State Access][].
 
 -   ```cpp
     void copy_{tex,texture}_sub_image(
         {GLenum,GLuint} {target,texture},
         CopySize        copy_size,
         CopyOffset      copy_offset = {},
         Offset          offset      = {},
         GLint           level       = 0
     )
     ```
 
     Generalizations of `glCopy{Tex,Texture}SubImage{1,2,3}D`, where the latter
     uses [Direct State Access][].
 
 -   ```cpp
     void compressed_tex_image(
         GLenum       target,
         Size         size,
         GLenum       internal_format,
         GLsizei      data_size = 0,
         void const * data      = nullptr,
         GLint        level     = 0,
         GLint        border    = 0
     )
     ```
 
     Generalization of `glCompressedTexImage{1,2,3}D`.
 
 -   ```cpp
     void compressed_{tex,texture}_sub_image(
         {GLenum,GLuint} {target,texture},
         Size            size,
         GLsizei         data_size,
         void const *    data,
         GLenum          internal_format,
         Offset          offset = {},
         GLint           level  = 0
     )
     ```
 
     Generalizations of `glCompressed{Tex,Texture}SubImage{1,2,3}D`, where the
     latter uses [Direct State Access][].
 
 For the member functions that are template parameterized on `Value`, when
 `internal_format`, `format` and/or `type` is `0` (the default), the value used
 will be taken from the corresponding `GLTraits::Value<Value>` member variable.
 
 Note that when the `Value` template type parameter is deduced from `data` the
 automatically chosen `format` may not be correct. E.g. it is reasonable for
 `data` to be a `GLubyte` pointer to RGBA values in which case `format` must be
 manually specified as `GL_RGBA` since the automatically chosen `format` would
 be `GL_RED`.
 
 Note that the [pixel transfer parameters][] are not modified, this must be
 handled manually if required.
 
 (There is no `copy_tex_image(...)` because OpenGL does not provide
 `glCopyTexImage3D`, only `glCopyTexImage{1,2}D`.)
 
 (There is no `{tex_image,tex_storage,texture_storage}_multisample(...)` because
 OpenGL does not provide `gl{TexImage,TexStorage,TextureStorage}1DMultisample`,
 only `gl{TexImage,TexStorage,TextureStorage}{2,3}DMultisample`.)
 
 (There is no `compressed_texture_image(...)` because OpenGL does not provide
 `glCompressedTextureImage{1,2,3}D`, only the `Sub` variants.)
 
 (There is no `framebuffer_texture(...)` because OpenGL provides incompatible
 signatures for `glFramebufferTexture1D` and `glFramebufferTexture{2,3}D`.)
 
 [image format]: https://www.khronos.org/opengl/wiki/Image_Format
 [color formats]: https://www.khronos.org/opengl/wiki/Image_Format#Color_formats
 [depth/stencil formats]: https://www.khronos.org/opengl/wiki/Image_Format#Depth_stencil_formats
 [depth formats]: https://www.khronos.org/opengl/wiki/Image_Format#Depth_formats
 [stencil formats]: https://www.khronos.org/opengl/wiki/Image_Format#Stencil_only
 [Direct State Access]: https://www.khronos.org/opengl/wiki/Direct_State_Access
 [immutable storage]: https://www.khronos.org/opengl/wiki/Texture_Storage#Immutable_storage
 [pixel transfer parameters]: https://www.khronos.org/opengl/wiki/Pixel_Transfer#Pixel_transfer_parameters
 
27d69715
 ### Member
 
 `GLTraits::members()` provides compile time reflection for types consisting
 (only) of types supported by `GLTraits::Value`. This feature is only available
 if [C++14][] or above is supported.
 
 If `T` is a non-[empty][] [trivially copyable][] [standard-layout][] type and
 there exists template specializations of `GLTraits::Value` for the types of
 either
 
 -   public member variables of `T`, if `T` is a [class][], or
 -   elements of `T`, if `T` is an [array][], or
 -   `T` itself, if `T` is a [scalar][]
 
 then `constexpr auto GLTraits::members<T>()` returns an instance of the
23a16d29
 [empty][] `struct GLTraits::Members<typename T, typename... Members>` where
 each of `Members...` is `GLTraits::Member<typename Value, std::size_t offset>`
 that describe the ("member") types above, in the order they occur in `T`.
 `Value` is the type of the member and `offset` is the offset of the member in
 `T`. Note that `offset` is only correct if the padding in `T` is minimal while
 still satisfying the [`alignof`][] of the member types. The author knows of no
 compiler that breaks this assumption unless explicitly instructed (e.g. with
 [`alignas`][], which are not taken into account), however the standard allows
 it.
27d69715
 
 An example use case would be along the lines of:
 
 ```cpp
 template<
5ee00b59
     typename    T,
27d69715
     typename    Value,
23a16d29
     std::size_t offset,
     typename... Members
27d69715
 >
 void vertex_setup(
23a16d29
     GLTraits::Members<T, GLTraits::Member<Value, offset>, Members...>,
     GLint location = 0
27d69715
 )
 {
5ee00b59
     GLTraits::Value<Value>::vertex_attrib_pointer(location, offset, sizeof(T));
27d69715
     vertex_setup(
23a16d29
         GLTraits::Members<T, Members...>{},
27d69715
         location + GLTraits::Value<Value>::columns
     );
 }
5ee00b59
 template<typename T>
23a16d29
 void vertex_setup(GLTraits::Members<T>, GLint)
27d69715
 {}
 
 struct Vertex
 {
     glm::vec3  position;
     glm::vec2  tex_coord;
     glm::mat3  tbn;
     glm::ivec4 bone_indices;
     glm::vec4  bone_weights;
     GLubyte    flags;
     GLdouble   unaligned;
 };
5ee00b59
 vertex_setup(GLTraits::members<Vertex>());
27d69715
 ```
 
 This use case is tested in [`tests/vertex_setup.cpp`][]. To verify that the
 compiler sees through all the templates, a script that builds it and
 disassembles it available in [`doc/vertex_setup`][] and its output in
 [`doc/vertex_setup.i`][].
 
 It is recommended to use a library based on `gltraits` that abstracts this
 further.
 
 Many thanks to Antony Polukhin (the author of [Boost.PFR][] and its standalone
 version [`magic_get`][]) on whose talks this implementation is based:
 
 -   [CppCon 2016: C++14 Reflections Without Macros, Markup nor External Tooling][]
 -   [Meeting C++ 2018: Better C++14 reflections][]
 
 [trivially copyable]: https://en.cppreference.com/w/cpp/types/is_trivially_copyable
 [standard-layout]: https://en.cppreference.com/w/cpp/types/is_standard_layout
 [class]: https://en.cppreference.com/w/cpp/types/is_class
 [array]: https://en.cppreference.com/w/cpp/types/is_array
 [scalar]: https://en.cppreference.com/w/cpp/types/is_scalar
23a16d29
 [`alignof`]: https://en.cppreference.com/w/cpp/language/alignof
 [`alignas`]: https://en.cppreference.com/w/cpp/language/alignas
27d69715
 [`tests/vertex_setup.cpp`]: tests/vertex_setup.cpp
 [`doc/vertex_setup`]: doc/vertex_setup
 [`doc/vertex_setup.i`]: doc/vertex_setup.i
 [Boost.PFR]: https://www.boost.org/libs/pfr
 [`magic_get`]: https://github.com/apolukhin/magic_get
 [CppCon 2016: C++14 Reflections Without Macros, Markup nor External Tooling]: https://www.youtube.com/watch?v=abdeAew3gmQ
 [Meeting C++ 2018: Better C++14 reflections]: https://www.youtube.com/watch?v=UlNUNxLtBI0
 
5a9edb8a
 ## Building
 
 See [`BUILDING.md`][].
 
 [`BUILDING.md`]: BUILDING.md
 
c3bb4602
 ## License
 
 Licensed under the [ISC License][] unless otherwise noted, see the
 [`LICENSE`][] file.
 
 [ISC License]: https://choosealicense.com/licenses/isc
 [`LICENSE`]: LICENSE