diff options
Diffstat (limited to 'src/util/cast.h')
-rw-r--r-- | src/util/cast.h | 101 |
1 files changed, 101 insertions, 0 deletions
diff --git a/src/util/cast.h b/src/util/cast.h new file mode 100644 index 0000000..06fb088 --- /dev/null +++ b/src/util/cast.h @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * Hand-rolled LLVM-style RTTI system for class hierarchies where dynamic_cast isn't fast enough. + */ +#ifndef INKSCAPE_UTIL_CAST_H +#define INKSCAPE_UTIL_CAST_H + +#include <type_traits> + +/* + * In order to use this system with a class hierarchy, specialize the following templates for + * every member of the hierarchy. The only requirement is that + * + * first_tag<T> <= first_tag<S> <= last_tag<T> + * + * exactly when S is a derived class of T. Then add to each class the line of boilerplate + * + * int tag() const override { return tag_of<decltype(*this)>; } + */ +template <typename T> inline constexpr int first_tag = std::enable_if<!sizeof(T), void>::value; +template <typename T> inline constexpr int last_tag = std::enable_if<!sizeof(T), void>::value; + +/** + * Convenience function to retrieve the tag (class id) of a given type. + */ +template <typename T> inline constexpr int tag_of = first_tag<std::remove_cv_t<std::remove_reference_t<T>>>; + +/** + * Equivalent to the boolean value of dynamic_cast<T const*>(...). + * + * If the supplied pointer is null, the check fails. + * + * To help catch redundant checks, checks that are known at compile time currently generate + * a compile error. Please feel free to remove these static_asserts if they become unhelpful. + */ +template<typename T, typename S> +bool is(S const *s) +{ + if (!s) return false; + if constexpr (std::is_base_of_v<T, S>) { + static_assert(!sizeof(T), "check is always true"); + return true; + } else if constexpr (std::is_base_of_v<S, T>) { + auto const s_tag = s->tag(); + return first_tag<T> <= s_tag && s_tag <= last_tag<T>; + } else { + static_assert(!sizeof(T), "check is always false"); + return false; + } +} + +/** + * Equivalent to static_cast<T [const]*>(...) where the const is deduced. + */ +template<typename T, typename S> +auto cast_unsafe(S *s) +{ + return static_cast<T*>(s); +} + +template<typename T, typename S> +auto cast_unsafe(S const *s) +{ + return static_cast<T const*>(s); +} + +/** + * Equivalent to dynamic_cast<T [const]*>(...) where the const is deduced. + * + * If the supplied pointer is null, the result is null. + * + * To help catch redundant casts, casts that are known at compile time currently generate + * a compile error. Please feel free to remove these static_asserts if they become unhelpful. + */ +template<typename T, typename S> +auto cast(S *s) +{ + if constexpr (std::is_base_of_v<T, S>) { + // Removed static assert; it complicates template "collect_items" + // static_assert(!sizeof(T), "cast is unnecessary"); + return cast_unsafe<T>(s); + } else if constexpr (std::is_base_of_v<S, T>) { + return is<T>(s) ? cast_unsafe<T>(s) : nullptr; + } else { + static_assert(!sizeof(T), "cast is impossible"); + return nullptr; + } +} + +#endif // INKSCAPE_UTIL_CAST_H + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : |