From 8242a68e6a68396cce36fb289b70d88d4f2bf11f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 7 Dec 2011 21:31:37 +0100 Subject: [PATCH] SizeTraits, SizeBasedCall and relevant classes. SizeTraits class provides suitable types for given data size at compile time, SizeBasedCall can call suitable templated overload based on given data size at runtime. Also added meta classes Pow and Log for computing powers and logarithms at compile time, usable mainly in conjunction with SizeTraits. Their implementation is checked at compile-time using static_cast(). --- src/CMakeLists.txt | 1 + src/SizeTraits.cpp | 25 +++++++ src/SizeTraits.h | 174 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 200 insertions(+) create mode 100644 src/SizeTraits.cpp create mode 100644 src/SizeTraits.h 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