Feature guide
High-level introduction to design of the Magnum library and basic building blocks.
Before you continue further, here are the essential bits of knowledge to help you around.
Library organization and naming scheme
The Magnum project consists of a library with core functionality and optional sub-libraries. Each library is in its own namespace, which corresponds to a sub-folder on the include path – so e.g. things from Magnum::
To reduce redundant verbosity, references throughout the documentation and code in example snippets have the root Magnum and Corrade namespaces omitted. In other words, as if the following was done:
using namespace Corrade; using namespace Magnum;
If Magnum is the primary library you're building your project on, it's recommended that you do the same — we're careful to not pollute the root namespace with overly generic names. However, except for Literals
namespaces, using
the subnamespaces is not recommended, as naming in those is deliberately picked short and may introduce conflicts (such as Containers::
If you frown upon the thought of using namespace Magnum
yet don't want to wear your fingers off by repeatedly writing Magnum::
everywhere, a common shorthand that projects go with is the following — similarly in spirit to import numpy as np
:
namespace Cr = Corrade; namespace Mn = Magnum;
Include files and forward declarations
Depending on where you come from, the #include
policy used by Magnum might either make you happy or freak you out. In short, there's no "include the
world" file, and instead you're supposed to include dedicated headers for APIs you want to use. The main reason is compile times, and the speed gain from doing it this way is too great to be ignored.
The general rule is that each top-level class has a corresponding include, so for example Math::#include
directive listed as well, ready to be copied.
In order to have the includes actually independent of each other, most Magnum types are forward-declared, and where possible, the header only relies on forward declarations. Which means that often the type already exists as a declaration, and in order to actually use it, you have to include the concrete definition of it. If you don't, the compiler will complain about use of an incomplete type. For example:
#include <Magnum/Magnum.h> /* only a Matrix4 forward declaration */ #include <Magnum/Math/Matrix4.h> /* the actual definition */ … Matrix4 a = Matrix4::translation({3.0f, 1.0f, 0.5f});
Of course not all headers can be written with just forward declarations, so there still are some transitive dependencies between headers (for example, the Magnum/
For happy compile times you're encouraged to rely on forward declarations in your code as well. See Forward declarations instead of includes for more information.
Debug output
One of the essential debugging workflows is inspection of variable contents by printing them out. Magnum defines a lot of new math types, enums and containers and it would be very painful if you had to loop over their contents or perform manual enum-to-string conversion every time you want to see what's inside.
Because writing to standard output and printing values for debugging purposes are distinct use cases with potentially conflicting requirements (should an enum value get written as a number? or as a name? a fully qualified name?), Magnum doesn't provide operator<<
overloads for std::
Instead, there's Utility::
Image2D image = …; Debug{} << "Image format is" << image.format() << "and size" << image.size(); Debug{} << "Color of the first pixel is" << image.pixels<Color4ub>()[0][0];
Image format is PixelFormat::RGBA8Srgb and size Vector(256, 192) Color of the bottom-left pixel is #33b27f
The main goal of this utility is convenience and readability — values are implicitly delimited by spaces and ended with a newline, container contents written with commas etc. Check out the class documentation for advanced features like colors, output redirection or printing file/line info.
Error handling in Magnum
Magnum differentiates between two kinds of errors — a programmer error (for example an out-of-bounds access) and a runtime error (for example when a file can't be opened). Programmer errors are assertions, directly leading to a program abort. Runtime errors expect you to do the error handling, and if you don't do that, then you're likely to hit an assertion — a programmer error — shortly after.
The assertions are deliberately not recoverable and are by default kept in release builds as well. A lot of effort is put into assertion messages, which clearly show you what went wrong. They get always printed to the standard output or its equivalent, see the troubleshooting guide for ways how to retrieve it on various platforms.
To avoid polluting user code with excessive error handling, runtime errors are restricted only where strictly necessary, and always documented as such. For performance and portability reasons, exceptions aren't used, Instead, the errors usually manifest as an invalid return value (for example an empty Containers::
Learn by example
Before you do a deep dive into the documentation, and if you haven't done already, it's recommended to go through the Getting Started Guide and check out at least the first example:
Learn through documentation
Each of the following pages provides a high-level description of a certain area of the library. It's recommended to read through these first to understand the overall principles and only then go to documentation of each concrete class and function.
- Platform support — Integration into windowing toolkits and creation of windowless contexts.
- Math type system — Type aliases, naming and compatibility with OpenGL, Vulkan and GLSL types.
- Operations with matrices and vectors — Introduction to essential classes of the graphics pipeline.
- 2D and 3D transformations — Introduction to essential operations on vectors and points.
- Keyframe-based animation — Layered framework for authoring, loading, optimizing and playing back animations.
- Loading and using plugins — Extending Magnum with additional functionality.
- File format support — Support tables for widely used image, scene, audio and font formats.
- OpenGL wrapping layer — Overview of the base OpenGL wrapper API.
- Vulkan wrapping layer — Overview of the base Vulkan wrapper API.
- Builtin shaders — Overview and basic usage of builtin shaders.
- Mesh processing tools — Overview of algorithms and utilities in the MeshTools namespace.
- Using the scene graph — Overview of scene management capabilities.
- Debugging helpers — Convenience classes to help you during development.
- UI library — Basic overview of the Magnum::
Ui library.