diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2fcf351e2..af1d19de7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -16,6 +16,7 @@ set(Magnum_SRCS Mesh.cpp Scene.cpp Shader.cpp + SizeTraits.cpp Texture.cpp ) diff --git a/src/SizeTraits.cpp b/src/SizeTraits.cpp new file mode 100644 index 000000000..5fe899c0a --- /dev/null +++ b/src/SizeTraits.cpp @@ -0,0 +1,25 @@ +/* + Copyright © 2010, 2011 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +#include "SizeTraits.h" + +namespace Magnum { + +#ifndef DOXYGEN_GENERATING_OUTPUT +static_assert(Pow<2, 3>::value == 8, "Implementation error in Pow meta class"); +static_assert(Log<2, 9>::value == 3, "Implementation error in Log meta class"); +#endif + +} diff --git a/src/SizeTraits.h b/src/SizeTraits.h new file mode 100644 index 000000000..b9b58955f --- /dev/null +++ b/src/SizeTraits.h @@ -0,0 +1,174 @@ +#ifndef Magnum_SizeTraits_h +#define Magnum_SizeTraits_h +/* + Copyright © 2010, 2011 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +/** @file + * @brief Class Magnum::SizeTraits, Magnum::SizeBasedCall, Magnum::Pow, Magnum::Log + */ + +#include "Magnum.h" + +namespace Magnum { + +/** +@brief Traits class providing suitable types for given data sizes +@tparam byte Highest byte needed (counting from zero) + +If you use indexed data, you would probably (for performance reasons) want to +use the smallest type which is able to store all indices in given range. This +class provides type suitable for given @b logarithmic size of data. For example, +if you want to store 289 elements, they occupy two bytes, so +%SizeTraits<1>::%SizeType is @c GLushort. For +convenience you can use Log class to compute logarithms at compile time, e.g. +%SizeTraits<Log<256, 289>::%value>::%SizeType. +*/ +template struct SizeTraits: public SizeTraits { + #ifdef DOXYGEN_GENERATING_OUTPUT + /** + * @brief (Unsigned) type able to index the data + * + * Not implemented for large sizes (> 232 elements), because + * OpenGL doesn't have any type which would be able to store the indices. + */ + typedef T SizeType; + #endif +}; + +#ifndef DOXYGEN_GENERATING_OUTPUT +template<> struct SizeTraits<0> { + typedef GLubyte SizeType; +}; +template<> struct SizeTraits<1> { + typedef GLushort SizeType; +}; +template<> struct SizeTraits<2> { + typedef GLuint SizeType; +}; +template<> struct SizeTraits<4> { + /* We don't have size type to store 2^32 values */ +}; +#endif + +/** +@brief Functor for calling templated function with type based on size +@tparam Base Base struct with templated function run(). See below + for example. + +If you have templated function which you want to call with type suitable for +indexing data of some size, you will probably use cascade of IFs, like this: +@code +size_t dataSize; +template Bar foo(Arg1 arg1, Arg2 arg2, ...); + +Bar bar; +if(dataSize < 256) + bar = foo(arg1, arg2, ...); +else if(dataSize < 65536) + bar = foo(arg1, arg2, ...); +// ... +@endcode +But this approach leads to repetitive and unmaintainable code, especially if +there are many arguments needed to pass to each function. The solution is to +use this class. The only thing you need is to rename your function to +run() and wrap it in a @c struct: +@code +struct Foo { + template Bar run(Arg1 arg1, Arg2 arg2, ...); +}; +@endcode +Then you can use this class to call the templated function with the right type +based on data size: +@code +bar = SizeBasedCall(dataSize)(arg1, arg2, ...); +@endcode +*/ +template struct SizeBasedCall: public Base { + /** + * @brief Constructor + * @param size Data size + */ + template SizeBasedCall(size_t size): size(size) {} + + /** + * @brief Functor + * @param arguments Arguments passed to @c Base::run + * @return Return value of @c Base::run + * + * Calls @c Base::run based on data size (given in constructor). If there + * is no suitable type for indexing given data size, prints message to + * error output and returns default-constructed value. + */ + template auto operator()(Args&&... arguments) -> decltype(Base::template run(std::forward(arguments)...)) { + switch(Math::log(256, size)) { + case 0: + return Base::template run(std::forward(arguments)...); + case 1: + return Base::template run(std::forward(arguments)...); + case 2: + case 3: + return Base::template run(std::forward(arguments)...); + } + + Corrade::Utility::Error() << "SizeBasedCall: no type able to index" << size << "elements."; + return decltype(Base::template run(std::forward(arguments)...))(); + } + + private: + size_t size; +}; + +/** +@brief Class for computing integral powers at compile time +@tparam base Base +@tparam exponent Exponent + +Useful mainly for computing template parameter value, e.g. in conjunction with +SizeTraits class. +*/ +template struct Pow { + /** @brief Value of the power */ + enum { value = base*Pow::value }; +}; + +#ifndef DOXYGEN_GENERATING_OUTPUT +template struct Pow { + enum { value = 1 }; +}; +#endif + +/** +@brief Class for computing integral logarithms at compile time +@tparam base Base +@tparam number Number + +Useful mainly for computing template parameter value, e.g. in conjunction with +SizeTraits class. +*/ +template struct Log { + /** @brief Value of the logarithm */ + enum { value = 1+Log::value }; +}; + +#ifndef DOXYGEN_GENERATING_OUTPUT +template struct Log: public Log {}; +template struct Log { + enum { value = 0 }; +}; +#endif + +} + +#endif