// 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 /* * 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 <= first_tag <= last_tag * * 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; } */ template inline constexpr int first_tag = std::enable_if::value; template inline constexpr int last_tag = std::enable_if::value; /** * Convenience function to retrieve the tag (class id) of a given type. */ template inline constexpr int tag_of = first_tag>>; /** * Equivalent to the boolean value of dynamic_cast(...). * * 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 bool is(S const *s) { if (!s) return false; if constexpr (std::is_base_of_v) { static_assert(!sizeof(T), "check is always true"); return true; } else if constexpr (std::is_base_of_v) { auto const s_tag = s->tag(); return first_tag <= s_tag && s_tag <= last_tag; } else { static_assert(!sizeof(T), "check is always false"); return false; } } /** * Equivalent to static_cast(...) where the const is deduced. */ template auto cast_unsafe(S *s) { return static_cast(s); } template auto cast_unsafe(S const *s) { return static_cast(s); } /** * Equivalent to dynamic_cast(...) 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 auto cast(S *s) { if constexpr (std::is_base_of_v) { // Removed static assert; it complicates template "collect_items" // static_assert(!sizeof(T), "cast is unnecessary"); return cast_unsafe(s); } else if constexpr (std::is_base_of_v) { return is(s) ? cast_unsafe(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 :