README.md
66f5e343
 # [`globject`][]
 
 A [C++11][]/[OpenGL][] [\>=1.0][] [object][] library.
 
4f14c1c2
 The provided `GLObject` class is intended to be the base class of several other
 classes that encapsulate different types of OpenGL objects.
 
66f5e343
 [`globject`]: https://git.rcrnstn.net/rcrnstn/globject
 [C++11]: https://en.wikipedia.org/wiki/C++11
 [OpenGL]: https://en.wikipedia.org/wiki/OpenGL
 [\>=1.0]: https://en.wikipedia.org/wiki/OpenGL#Version_history
 [object]: https://www.khronos.org/opengl/wiki/OpenGL_Object
8eea41c4
 
4f14c1c2
 ## Usage
 
 ### OpenGL loading library
 
 This library can be used with an arbitrary [OpenGL loading library][] by making
 sure `GLOBJECT_LOADER` is `#define`d to the file to `#include`, e.g.
 `<glad/glad.h>` (either in the source or through a compiler switch, probably
 defined in the build system). The default if none is defined is `<GL/glew.h>`.
 
 [OpenGL loading library]: https://www.khronos.org/opengl/wiki/OpenGL_Loading_Library
 
 ### [OpenGL Mathematics (GLM)][]
 
 If `glm/glm.hpp` is included (specifically, if `GLM_VERSION` is defined) before
 inclusion of `globject.hpp` [OpenGL Mathematics (GLM)][] support will be
 enabled. See [Data][].
 
 [OpenGL Mathematics (GLM)]: https://glm.g-truc.net
 [Data]: #data
 
 ### Thread safety
 
 "Global" state (usually used to mirrors some OpenGL [context][] internal state
 for convenience or performance) is declared `thread_local`. This means that
 multithreading is supported under the assumption that a given OpenGL
 [context][] and the `GLObject`s created while it is current are only used from
 a single thread.
 
 This is not a huge limitation since driver vendors recommend to not share
 OpenGL [context][]s between threads, for performance.
 
 [context]: https://www.khronos.org/opengl/wiki/OpenGL_Context
 
 ### Function documentation
 
 For brevity, argument and return types may omit `const &` and functions may
 omit `const` qualifiers in the remainder of this documentation when it has no
 implication for the user, other than performance. Consult the source for the
 exact function signatures.
 
 ### `public` interface
 
 #### [Special member functions][]
 
 There is no `public` constructor. It is impossible to directly instantiate
 `GLObject`s. Instead, use a derived class that encapsulates a specific type of
 OpenGL object.
 
 There is no defined default constructor. The copy constructor and copy/move
 assignment operators are `delete`d. This enforces the invariant that a
 successfully constructed instance (that has not been moved from) always (is the
 only instance that) corresponds to a valid OpenGL object.
 
 The destructor is `virtual`, so references to instances of (different) derived
 classes can be held in (the same) containers.
 
 The move constructor's `noexcept` specifier is only honored if `debug` is
 `false`, see [Debug][].
 
 [special member functions]: https://en.wikipedia.org/wiki/Special_member_functions
 [Debug]: #debug
 
 #### Getters and setters
 
 Getters and setters share the same name and use function overloading to
 disambiguate. Getters (which can be run on `const` objects) take no argument
 and return the value (by constant reference). Setters take a [forwarding
 reference][] argument and return the old value (by value, after move). Getters
 and setters use a "gets"/"sets" shorthand below to indicate they conform to
 this behaviour.
 
 [forwarding reference]: https://en.cppreference.com/w/cpp/language/reference#Forwarding_references
 
 #### Core
 
 `bool static supported(Version version_min, std::string extension = {})`
 returns `true` if the current OpenGL context implements the given, or a later,
 version or the given extension. If no version check is desired, provide `{0,
 0}`. If no extension check is desired, provide an empty extension string (the
 default). If no check at all is performed, `false` is returned. This is
 convenient because extensions are often absorbed into newer versions of the
 OpenGL specification itself, and some platforms stop reporting support for the
 extension for those versions, so only checking if the extension is available
 fails. [History of OpenGL][] has a list of which versions of OpenGL absorbed
 which extensions. If a non-empty `extension` is provided, it must be on the
 form returned by [`glGetStringi`][]`(GL_EXTENSIONS, index)`, i.e. it must start
 with `"GL_"`. Since this library is agnostic to the used [OpenGL loading
 library][] (which is usually used to perform these kind of checks), the OpenGL
 API itself is queried. The old method of detecting the OpenGL version and
 extensions was deprecated in OpenGL 3.0 and removed in OpenGL 3.1. Both methods
 are dealt with correctly. `Version` is an alias for `std::array<GLint, 2>`.
 
 `GLint static get_integer(GLenum name)` calls [`glGetIntegerv`][] with the
 given argument and returns the result.
 
 `GLuint object()` gets the underlying OpenGL object. This is guaranteed to be
 valid, unless the `GLObject` has been moved from, in which case it is `0`.
 
 `operator GLuint()` (implicit conversion operator) returns `object()`.
 
 [History of OpenGL]: https://www.khronos.org/opengl/wiki/History_of_OpenGL
 [`glGetStringi`]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glGetString.xhtml
 [`glGetIntegerv`]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glGet.xhtml
 
 #### Data
 
 `template<typename Data> struct DataTraits` is template specialized on `GL*`
 (and, if enabled, [OpenGL Mathematics (GLM)][]) types and provides the
 following `static constexpr` values.
 
 -   `char const[] name`
 -   `GLint        columns`
 -   `GLint        rows`
 -   `GLenum       format`
 -   `GLenum       type`
 -   `GLenum       internal_format`
 
 It also provides the following functions.
 
 -   `void static attrib(GLuint index, Data value)` which uploads `value` to the
     [non-array attribute][] indicated by `index`. Note that [OpenGL Mathematics
     (GLM)][] matrix types, if enabled, occupy several consecutive indices (one
     per column), which are all uploaded automatically by this call.
 
 -   `void static uniform(GLint location, Data value)` which uploads `value` to
     the [uniform][] indicated by `location` of the current [shader][] program.
 
 `GLOBJECT_DATA*` macros to ease the definition of new template specializations
 of `DataTraits` are defined. Consult the source for the definitions and usage
 examples.
 
 [non-array attribute]: https://www.khronos.org/opengl/wiki/Vertex_Specification#Non-array_attribute_values
 [uniform]: https://www.khronos.org/opengl/wiki/Uniform_(GLSL)
 [shader]: https://www.khronos.org/opengl/wiki/Shader
 
 #### Path
 
 `Path` is an alias for `std::string`.
 
 `Paths` is an alias for `std::vector<Path>`.
 
 #### Debug
 
 `bool static debug()` gets/sets the global `debug` flag. When `true`,
 potentially costly debug operations are performed as part of other operations.
 Defaults to `true`.
 
 `DebugCallback static debug_callback()` gets/sets a callback that may be called
 by `GLObject` and its derived classes when `debug` is `true`. The debug
 callback can also be called by client code if desired. `DebugCallback` is an
 alias for `std::function<void (std::string const & message)>`. Defaults to a
 function which outputs to `std::cerr`, appends a newline and flushes.
 
 `DebugObjects static debug_objects()` gets a list of all `GLObjects`
 constructed, and not subsequently destructed, during the time `debug` was
 `true`. `DebugObjects` is an alias for `std::vector<GLObject *>`.
 
 `std::string debug_name()` returns a short representation of the object.
 
 `std::string debug_info()` returns a potentially longer representation of the
 object.
 
 `std::string static debug_objects_name()` returns a short representation of all
 objects in `debug_objects`.
 
 `std::string static debug_objects_info()` returns a potentially longer
 representation of all objects in `debug_objects`.
 
 Code in `GLObject` (and properly behaved derived classes) uses the macro
 `GLOBJECT_DEBUG_IF(D)` which expands to `if (debug() >= 1)` by default. It can
 be made to expand to `if (false)` by defining `GLOBJECT_DEBUG 0`, with the
 result that an optimizing compiler will completely compile out the
 corresponding debug code.
 
 #### Exceptions
 
 All exceptions thrown are of the type `GLObject::Exception`, which inherits
 from `std::runtime_error`.
 
 ### `protected` interface
 
 #### [Special member functions][]
 
 Derived classes should define a (usually `explicit` non-default) constructor. A
 `noexcept` move constructor and `virtual` destructor should only be defined if
 resources (other than `object`, which is handled by `GLObject`) need to be
 moved/released. Derived classes should not define or `delete` any of the
 non-defined/`delete`d special member functions detailed in the `public`
 interface documentation above.
 
 Two helper functions that act as adapters from the interface of the
 `glCreate*`/`glDelete*` family of functions (used by shader and program
 objects, termed "unconventional objects" by the OpenGL Wiki) to the interface
 of the `glGen*s`/`glDelete*s` family of functions are provided.
 
 -   `template<GLCreateObject gl_create_object> void static
     gl_create_object_(GLsizei n, GLuint * objects)` takes a `glCreate*`
     function as template argument, and conforms to the `glGen*s` interface.
     `GLCreateObject` is an alias for `GLuint (*)()`.
 
 -   `template<GLDeleteObject gl_delete_object> void static
     gl_delete_object_(GLsizei n, GLuint * objects)` takes a `glDelete*`
     function as template argument, and conforms to the `glDelete*s` interface.
     `GLDeleteObject` is an alias for `void (*)(GLuint)`.
 
 `GLObject` has a concept of pseudo-objects. They are classes that wish to make
 use of the `protected` interface of `GLObject` but does not encapsulate an
 OpenGL [object][].
 
 The `GLObject` constructor takes:
 
 -   `GLGenObjects gl_gen_objects` which is a function pointer that conforms to
     the `glGen*s` interface. Specify `nullptr` for pseudo-objects.
 
 -   `GLDeleteObjects gl_delete_objects` which is a function pointer that
     conforms to the `glDelete*s` interface. Specify `nullptr` for
     pseudo-objects.
 
 -   `GLenum object_type` which is one of the following. Specify `0` for
     pseudo-objects.
 
     -   `GL_BUFFER`
     -   `GL_SHADER`
     -   `GL_PROGRAM`
     -   `GL_VERTEX_ARRAY`
     -   `GL_QUERY`
     -   `GL_PROGRAM_PIPELINE`
     -   `GL_TRANSFORM_FEEDBACK`
     -   `GL_SAMPLER`
     -   `GL_TEXTURE`
     -   `GL_RENDERBUFFER`
     -   `GL_FRAMEBUFFER`
 
 -   `std::string object_label` which will be used when throwing exceptions and,
     if OpenGL [\>=4.3][] or the [`KHR_debug`][] extension is available, passed
     to [`glObjectLabel`][].
 
 [\>=4.3]: https://en.wikipedia.org/wiki/OpenGL#Version_history
 [`KHR_debug`]: https://www.khronos.org/registry/OpenGL/extensions/KHR/KHR_debug.txt
 [`glObjectLabel`]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glObjectLabel.xhtml
 
 #### Getters and setters
 
 Macros that aid in the implementation of (potentially static thread local)
 getters and setters are defined. These should be used in the class definition.
 They define functions named `NAME`, which operate on a variable named
 `NAME##_`. The underlying variable is not declared automatically, this must be
 done manually (probably `private`). The `ACCESS` variant defines both `GET` and
 `SET`.
 
 `GLOBJECT_{GET,SET,ACCESS}{,_THREAD}(TYPE, NAME)`
 
 Additionally, static thread local member variables need to be defined in
 exactly one translation unit. A macro to help with this is defined as well.
 Note that, since the definition is outside the class definition, the `NAME`
 needs to be qualified with the class name.
 
 `GLOBJECT_THREAD(NAME, INIT)`
 
 #### Path
 
 `Path static path_prefix_(Path path, Path prefix)` returns `prefix` prepended
 to `path`, separated with a `/`, unless
 
 1.  `prefix` is empty
 2.  `path` starts with a `/`
 
 in which case `path` is returned as is.
 
 #### TGA
 
 A `TGA` class is provided to handle [Truevision TGA][] images in uncompressed
 [BGRA][] (`GL_BGRA`) format. TGA was selected because it is widely supported
 and has trivial header and data layout.
 
 `Size` is an alias for `std::array<GLsizei, 2>`.
 
 `Data` is an alias for `std::vector<GLubyte>`.
 
 `explicit TGA(Size size, Data data)` instantiates a `TGA` object with the given
 `size` and `data`.
 
 `TGA static read(std::string path)` reads the file `path` into a `TGA` object.
 
 `void write(std::string const & path)` writes the `TGA` object to the file
 `path`.
 
 `Data data()` gets the data of the TGA image.
 
 `Size size()` gets the size of the TGA image.
 
 [Truevision TGA]: https://en.wikipedia.org/wiki/Truevision_TGA
 [BGRA]: https://en.wikipedia.org/wiki/RGBA_color_model
 
 #### Check
 
 These functions throw an exception if some condition does not hold. It is
 recommended that derived classes use similar helper functions, defined in an
 implementation file, to hide the string processing necessary to form a helpful
 exception message.
 
 Derived classes should treat error checking as free during:
 
 -   Construction and destruction, except move construction.
 -   Exceptions.
 -   Debug function calls.
 
 At all other times, non-critical error checking should only be performed if
 `debug` is `true`.
 
 `void static check_path_(std::string path)` checks that `path` is non-empty.
 
 `void static check_error_(GLenum error)` checks an error returned by
 [`glGetError`][].
 
 `void static check_supported_(Version version_min, std::string extension = {})`
 checks that `supported` returns `true` for the given arguments.
 
 `void static check_format_(GLenum format, GLenum format_expected)` checks that
 `format` matches `format_expected`.
 
 `void static check_type_(GLenum type, GLenum type_expected)` checks that `type`
 matches `type_expected`.
 
 `void static check_internal_format_(GLenum internal_format)` checks that
 `internal_format` is supported.
 
 [`glGetError`]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glGetError.xhtml
 
 #### Fail
 
 These functions throw an exception and should be marked `[[noreturn]]`. It is
 recommended that derived classes use similar helper functions, defined in an
 implementation file, to hide the string processing necessary to form a helpful
 exception message.
 
 `void fail_action_(std::string action)` throws an exception with a message that
 includes `action` and `debug_name()`.
 
 #### String
 
 `std::string static str_path_(Path path)` returns `path` surrounded by quotes.
 
 `std::string static str_paths_(Paths paths)` returns `paths` surrounded by
 quotes and joined with commas.
 
 `std::string static str_enum_(GLenum name)` returns the hexadecimal string
 representation of `name` (often used as fallback).
 
 `std::string static str_error_(GLenum error)` returns the string representation
 of values returned by [`glGetError`][].
 
 `std::string static str_format_(GLenum format)` returns the string
 representation of `format`.
 
 `std::string static str_type_(GLenum type)` returns the string representation
 of `type`.
 
 `std::string static str_internal_format_(GLenum internal_format)` returns the
 string representation of `internal_format`.
 
7011ec66
 ## Build system
 
 This project supports [CMake][] and uses [`cmake-common`][]. There are several
 ways to use it in other CMake-based projects:
 
 -   With [`find_package`][]: ([Package][] and) [install][] it on the system.
 
 -   With [`add_subdirectory`][]: Bundle it.
 
 -   With [`FetchContent`][]: Download it as part of the CMake configure step.
 
 -   With [`cmake-common`][]: Use any of the above methods through a simplified
     interface.
 
 As usual, use [`add_dependencies`][] or [`target_link_libraries`][] (or
 `cmake-common`'s `DEPENDENCIES_*`) to declare the dependency.
 
 [CMake]: https://cmake.org
 [`cmake-common`]: https://git.rcrnstn.net/rcrnstn/cmake-common
 [`FetchContent`]: https://cmake.org/cmake/help/v3.14/module/FetchContent.html
 [`add_subdirectory`]: https://cmake.org/cmake/help/v3.14/command/add_subdirectory.html
 [`find_package`]: https://cmake.org/cmake/help/v3.14/command/find_package.html
 [Package]: #Package
 [Install]: #Install
 [`add_dependencies`]: https://cmake.org/cmake/help/v3.14/command/add_dependencies.html
 [`target_link_libraries`]: https://cmake.org/cmake/help/v3.14/command/target_link_libraries.html
 
 ### Configure and generate
 
 To configure and generate a build tree, use `cmake`:
 
 ```sh
 cmake -B _build
 ```
 
 To set the build type, pass e.g. `-D`[`CMAKE_BUILD_TYPE`][]`=Release`.
 
 [`cmake`]: https://cmake.org/cmake/help/v3.14/manual/cmake.1.html#generate-a-project-buildsystem
 [`CMAKE_BUILD_TYPE`]: https://cmake.org/cmake/help/v3.14/variable/CMAKE_BUILD_TYPE.html
 
 ### Build
 
 To build, use [`cmake --build`][]:
 
 ```sh
 cmake --build _build
 ```
 
 To disable building tests, pass `-D`[`BUILD_TESTING`][]`=OFF`.
 
 [`cmake --build`]: https://cmake.org/cmake/help/v3.14/manual/cmake.1.html#build-a-project
 [`BUILD_TESTING`]: https://cmake.org/cmake/help/v3.14/module/CTest.html
 
 ### Test
 
 To run tests, use [`ctest`][]:
 
 ```sh
 (cd _build && ctest)
 ```
 
 To show output from failing tests, pass `--output-on-failure`. To show output
 from all tests, pass `--verbose`.
 
 [`ctest`]: https://cmake.org/cmake/help/v3.14/manual/ctest.1.html
 
 ### Package
 
 To package, use [`cpack`][]:
 
 ```sh
 (cd _build && cpack)
 ```
 
 [`cpack`]: https://cmake.org/cmake/help/v3.14/manual/cpack.1.html
 
 ### Install
 
 To install onto the current system, use [`cmake --install`][]:
 
 ```sh
 cmake --install _build
 ```
 
 To set the prefix, pass e.g. `-D`[`CMAKE_INSTALL_PREFIX`][]`="$HOME/.local"`.
 
 [`cmake --install`]: https://cmake.org/cmake/help/v3.14/manual/cmake.1.html#install-a-project
 [`CMAKE_INSTALL_PREFIX`]: https://cmake.org/cmake/help/v3.14/variable/CMAKE_INSTALL_PREFIX.html
 
8eea41c4
 ## License
 
 Licensed under the [ISC License][] unless otherwise noted, see the
 [`LICENSE`][] file.
 
 [ISC License]: https://choosealicense.com/licenses/isc/
 [`LICENSE`]: LICENSE