// Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors. // All rights reserved. // Code licensed under the BSD License. // http://www.anki3d.org/LICENSE /// @file /// The file contains visitor concepts /// /// @par The main classes are: /// @li Visitable: For non related types /// @li VisitableCommonBase: For types with common base #pragma once #include #include #include namespace anki { /// @addtogroup util_patterns /// @{ /// Namespace for visitor internal classes namespace visitor_detail { /// A smart struct that given a type and a list of types finds a const integer indicating the type's position from the /// back of the list. /// /// Example of usage: /// @code /// GetVariadicTypeId::get() == 1 /// GetVariadicTypeId::get() == 0 /// GetVariadicTypeId::get() // Compiler error /// @endcode template struct GetVariadicTypeId { // Forward template struct Helper; // Declaration template struct Helper : Helper { }; // Specialized template struct Helper { static const I ID = sizeof...(Types_); }; /// Get the id template static constexpr I get() { return sizeof...(Types) - Helper::ID - 1; } }; /// A struct that from a variadic arguments list it returnes the type using an ID. /// Example of usage: /// @code /// GetTypeUsingId::DataType<0> x = "a string"; /// GetTypeUsingId::DataType<1> y = 123; /// GetTypeUsingId::DataType<2> y = 123.456f; /// @endcode template struct GetTypeUsingId { // Forward declaration template struct Helper; // Declaration template struct Helper : Helper { }; // Specialized template struct Helper<0, TFirst, Types_...> { using DataType = TFirst; }; template using DataType = typename Helper::DataType; }; } // end namespace visitor_detail /// Visitable class template class Visitable { public: Visitable() { } template Visitable(T* t) { setupVisitable(t); } I getVisitableTypeId() const { return m_what; } template static constexpr I getVariadicTypeId() { return visitor_detail::GetVariadicTypeId::template get(); } /// Apply visitor template void acceptVisitor(TVisitor& v) { ANKI_ASSERT(m_what != -1 && address != nullptr); acceptVisitorInternal(v); } /// Apply visitor (const version) template void acceptVisitor(TVisitor& v) const { ANKI_ASSERT(m_what != -1 && address != nullptr); acceptVisitorInternalConst(v); } /// Setup the data template void setupVisitable(T* t) { // Null arg. Setting for second time? Now allowed ANKI_ASSERT(t != nullptr); ANKI_ASSERT(address == nullptr && m_what == -1); address = t; m_what = visitor_detail::GetVariadicTypeId::template get(); } private: I m_what = -1; ///< The type ID void* address = nullptr; ///< The address to the data /// @name Accept visitor template methods /// @{ template void acceptVisitorInternal(TVisitor& v) { switch(m_what) { case 0: v.template visit(*reinterpret_cast(address)); break; default: ANKI_ASSERT(0 && "Wrong type ID"); break; } } template void acceptVisitorInternal(TVisitor& v) { constexpr I i = sizeof...(Types) - sizeof...(Types_) - 1; switch(m_what) { case i: v.template visit(*reinterpret_cast(address)); break; default: acceptVisitorInternal(v); break; } } template void acceptVisitorInternalConst(TVisitor& v) const { switch(m_what) { case 0: v.template visit(*reinterpret_cast(address)); break; default: ANKI_ASSERT(0 && "Wrong type ID"); break; } } template void acceptVisitorInternalConst(TVisitor& v) const { constexpr I i = sizeof...(Types) - sizeof...(Types_) - 1; switch(m_what) { case i: v.template visit(*reinterpret_cast(address)); break; default: acceptVisitorInternalConst(v); break; } } /// @} }; /// Visitable for types with common base /// @tparam TBase The base class /// @tparam Types The types that compose the visitable. They are derived from TBase /// /// @note In debug mode it uses dynamic cast as an extra protection reason template class VisitableCommonBase { public: #if ANKI_EXTRA_CHECKS // Allow dynamic cast in acceptVisitor virtual ~VisitableCommonBase() { } #endif I getVisitableTypeId() const { ANKI_ASSERT(m_what != -1); return m_what; } template static constexpr I getVariadicTypeId() { return visitor_detail::GetVariadicTypeId::template get(); } /// Check if this is of type template Bool isTypeOf() const { return getVariadicTypeId() == getVisitableTypeId(); } /// Apply mutable visitor template Error acceptVisitor(TVisitor& v) { ANKI_ASSERT(m_what != -1); return acceptVisitorInternal(v); } /// Apply const visitor template Error acceptVisitor(TVisitor& v) const { ANKI_ASSERT(m_what != -1); return acceptVisitorInternalConst(v); } /// Setup the type ID template void setupVisitable(const T*) { ANKI_ASSERT(m_what == -1); // Setting for second time not allowed m_what = visitor_detail::GetVariadicTypeId::template get(); } private: I m_what = -1; ///< The type ID /// @name Accept visitor template methods /// @{ template Error acceptVisitorInternal(TVisitor& v) { Error err = Error::kNone; switch(m_what) { case 0: { #if ANKI_EXTRA_CHECKS TFirst* base = dynamic_cast(this); ANKI_ASSERT(base != nullptr); #else TFirst* base = static_cast(this); #endif err = v.template visit(*base); } break; default: ANKI_ASSERT(0 && "Wrong type ID"); break; } return err; } template Error acceptVisitorInternal(TVisitor& v) { Error err = Error::kNone; constexpr I i = sizeof...(Types) - sizeof...(Types_) - 1; switch(m_what) { case i: { #if ANKI_EXTRA_CHECKS TSecond* base = dynamic_cast(this); ANKI_ASSERT(base != nullptr); #else TSecond* base = static_cast(this); #endif err = v.template visit(*base); } break; default: err = acceptVisitorInternal(v); break; } return err; } template Error acceptVisitorInternalConst(TVisitor& v) const { Error err = Error::kNone; switch(m_what) { case 0: { #if ANKI_EXTRA_CHECKS const TFirst* base = dynamic_cast(this); ANKI_ASSERT(base != nullptr); #else const TFirst* base = static_cast(this); #endif err = v.template visit(*base); break; } default: ANKI_ASSERT(0 && "Wrong type ID"); break; } return err; } template Error acceptVisitorInternalConst(TVisitor& v) const { Error err = Error::kNone; constexpr I i = sizeof...(Types) - sizeof...(Types_) - 1; switch(m_what) { case i: { #if ANKI_EXTRA_CHECKS const TSecond* base = dynamic_cast(this); ANKI_ASSERT(base != nullptr); #else const TSecond* base = static_cast(this); #endif err = v.template visit(*base); break; } default: err = acceptVisitorInternalConst(v); break; } return err; } /// @} }; /// @} } // end namespace anki