/**************************************************************************** ** ** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB). ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of Qt 3D Studio. ** ** $QT_BEGIN_LICENSE:GPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 or (at your option) any later version ** approved by the KDE Free Qt Foundation. The licenses are as published by ** the Free Software Foundation and appearing in the file LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QT3DRENDER_DRAGON_IMMUTABLE_P_H #define QT3DRENDER_DRAGON_IMMUTABLE_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists for the convenience // of other Qt classes. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include #include // Needed for MinGW #if defined(Q_OS_WIN) # if defined(Q_CC_GNU) # include # endif #endif #include QT_BEGIN_NAMESPACE namespace Qt3DRender { namespace Dragon { // TODO Remove when std::in_place_t is introduced in C++17 struct InPlaceT { explicit InPlaceT() = default; }; constexpr InPlaceT InPlace {}; namespace helper { template using decay_t = typename std::decay::type; template using enable_if_t = typename std::enable_if::type; } template struct Immutable { template struct is_derived : public std::integral_constant>::value> {}; Immutable() : m_container(std::make_shared()) , m_copyFunction(&Immutable::make_shared_helper) { } // Copy constructors template , typename = helper::enable_if_t::value>> Immutable(const Immutable &other) : m_container(other.m_container) , m_copyFunction(&Immutable::make_shared_helper) {} template , typename = helper::enable_if_t::value>> Immutable(const U& arg) : m_container(std::make_shared(arg)) , m_copyFunction(&Immutable::make_shared_helper) { } // Move constructors template , typename = helper::enable_if_t::value>> Immutable(Immutable &&other) : m_container(std::move(other.m_container)) , m_copyFunction(&Immutable::make_shared_helper) { } template , typename = helper::enable_if_t::value>> Immutable(U &&value) : m_container(std::make_shared(std::forward(value))) , m_copyFunction(&Immutable::make_shared_helper) { } // Copy assignment template , typename = helper::enable_if_t::value>> Immutable &operator=(const Immutable &other) { m_container = std::make_shared(*other.m_container); m_copyFunction = &Immutable::make_shared_helper; return *this; } template , typename = helper::enable_if_t::value>> Immutable &operator=(const U &value) { m_container = std::make_shared(value); m_copyFunction = &Immutable::make_shared_helper; return *this; } // Move assignment template , typename = helper::enable_if_t::value>> Immutable &operator=(Immutable &&other) { // TODO it seems this is never used, check to verify m_container = std::make_shared(std::move>(*other.m_container)); m_copyFunction = &Immutable::make_shared_helper; return *this; } template , typename = helper::enable_if_t::value>> Immutable &operator=(U &&value) { m_container = std::make_shared(std::forward(value)); m_copyFunction = &Immutable::make_shared_helper; return *this; } template Immutable(InPlaceT, Args&&... args) : m_container(std::make_shared(std::forward(args)...)) , m_copyFunction(&Immutable::make_shared_helper) { } template bool can_convert() const { auto typed_container = std::dynamic_pointer_cast(m_container); if (typed_container == nullptr) { return false; } return true; } const T& operator*() const { return *m_container; } const T &get() const { return *m_container; } template const Immutable as() const { auto typed_container = std::dynamic_pointer_cast(m_container); Immutable result; if (typed_container == nullptr) { qDebug() << "ERROR: Cannot cast from " << typeid(T).name() << " to " << typeid(U).name(); return result; } // TODO consider adding a private construtor that can set this at construction time result.m_container = typed_container; return result; } template Immutable as() { if (m_container.use_count() != 1) { m_container = m_copyFunction(m_container); } auto typed_container = std::dynamic_pointer_cast(m_container); Immutable result; if (typed_container == nullptr) { qDebug() << "ERROR: Cannot cast from " << typeid(T).name() << " to " << typeid(U).name(); return result; } // TODO consider adding a private construtor that can set this at construction time result.m_container = typed_container; return result; } const T* operator->() const { return m_container.get(); } const T* operator->() { return m_container.get(); } /*! * \brief modified lets you modify the contained object and returns a new Value with the * modified object. * * The container will always be copied. * * \param modifier * \return */ Immutable modified(const std::function &modifier) const& { if (m_container == nullptr) { return *this; } // Make a real copy of ourselves. // Note: We cannot use result = *this, because this would just increase the ref count. Immutable result; result.m_container = m_copyFunction(m_container); result.m_copyFunction = m_copyFunction; // Modify the copy before returning it. This is safe because we currently are the only // ones who know about this object. modifier(result.m_container.get()); return result; } /*! * \brief modified lets you modify the contained object and returns a new Value with the * modified object. * * If there are no other references to the container, the modification happens directly on * the current object. * * \param modifier * \return */ Immutable&& modified(const std::function &modifier) && { if (m_container == nullptr) { return std::move(*this); } // Even though we're an rvalue, we might not be the only with a reference to the container if (m_container.use_count() != 1) { m_container = m_copyFunction(m_container); } // As the only user modifier(m_container.get()); return std::move(*this); } template static std::shared_ptr make_shared_helper(std::shared_ptr c) { auto typed_container = std::static_pointer_cast(c); return std::make_shared(*typed_container); } bool operator==(const Immutable &other) const { return *m_container == *other.m_container; } bool operator!=(const Immutable &other) const { return *m_container != *other.m_container; } private: std::shared_ptr m_container; // TODO copying this can be expensive, consider adding an inner struct { T, function } // that is stored in a shared pointer for faster copying of the Value std::function(std::shared_ptr)> m_copyFunction; template friend struct Immutable; }; } } QT_END_NAMESPACE #endif // QT3DRENDER_DRAGON_IMMUTABLE_P_H