File indexing completed on 2024-06-16 04:23:17

0001 /*
0002     SPDX-FileCopyrightText: 2008 David Nolden <david.nolden.kdevelop@art-master.de>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-only
0005 */
0006 
0007 #ifndef KDEVPLATFORM_TYPEREGISTER_H
0008 #define KDEVPLATFORM_TYPEREGISTER_H
0009 
0010 #include <QHash>
0011 
0012 #include "abstracttype.h"
0013 #include "typesystemdata.h"
0014 
0015 namespace KDevelop {
0016 /**
0017  * \short A factory class for type data.
0018  *
0019  * This class provides an interface for creating private data
0020  * structures for classes.
0021  */
0022 class KDEVPLATFORMLANGUAGE_EXPORT AbstractTypeFactory
0023 {
0024 public:
0025     /**
0026      * Create a new type for the given \a data.
0027      *
0028      * \param data Data to assign to the new type. The data type must match the class type.
0029      */
0030     virtual AbstractType* create(AbstractTypeData* data) const = 0;
0031 
0032     /**
0033      * Call the destructor of the data-type.
0034      */
0035     virtual void callDestructor(AbstractTypeData* data) const = 0;
0036 
0037     /**
0038      * Copy contents of type-data \a from one location \a to another.
0039      *
0040      * \param from data to copy from
0041      * \param to data to copy to. This data must not be initialized yet
0042      *           (the constructor must not have been called yet)
0043      * \param constant set to true if \a to is to be a static unchangeable
0044      *                 data type (eg. in the type-repository), or false if
0045      *                 \a to is to be a dynamic changeable type data.
0046      */
0047     virtual void copy(const AbstractTypeData& from, AbstractTypeData& to, bool constant) const = 0;
0048 
0049     /**
0050      * Return the memory size of the given private \a data, including dynamic data.
0051      *
0052      * \param data data structure
0053      * \returns the size in memory of the data.
0054      */
0055     virtual uint dynamicSize(const AbstractTypeData& data) const = 0;
0056 
0057     /// Destructor.
0058     virtual ~AbstractTypeFactory()
0059     {
0060     }
0061 };
0062 
0063 /**
0064  * Template class to implement factories for each AbstractType subclass you want
0065  * to instantiate.
0066  */
0067 template <class T, class Data = typename T::Data>
0068 class TypeFactory
0069     : public AbstractTypeFactory
0070 {
0071 public:
0072     AbstractType* create(AbstractTypeData* data) const override
0073     {
0074 /*    if(!m_reUseTypes.isEmpty()) {
0075       return new (m_reUseTypes.pop()) T(*static_cast<typename T::Data*>(data));
0076     }else{*/
0077         return new T(*static_cast<typename T::Data*>(data));
0078 //     }
0079     }
0080 
0081     void copy(const AbstractTypeData& from, AbstractTypeData& to, bool constant) const override
0082     {
0083         Q_ASSERT(from.typeClassId == T::Identity);
0084 
0085         if (( bool )from.m_dynamic == ( bool )!constant) {
0086             //We have a problem, "from" and "to" should both be either dynamic or constant. We must copy once more.
0087             Data* temp = &AbstractType::copyDataDirectly<Data>(static_cast<const Data&>(from));
0088 
0089             new (&to) Data(*temp); //Call the copy constructor to initialize the target
0090 
0091             Q_ASSERT(( bool )to.m_dynamic == ( bool )!constant);
0092             destroyData(temp);
0093         } else {
0094             new (&to) Data(static_cast<const Data&>(from)); //Call the copy constructor to initialize the target
0095         }
0096     }
0097 
0098     void destroyData(AbstractTypeData* data) const
0099     {
0100         callDestructor(data);
0101         delete[] ( char* )data;
0102     }
0103 
0104     void callDestructor(AbstractTypeData* data) const override
0105     {
0106         Q_ASSERT(data->typeClassId == T::Identity);
0107         static_cast<Data*>(data)->~Data();
0108     }
0109 
0110     uint dynamicSize(const AbstractTypeData& data) const override
0111     {
0112         Q_ASSERT(data.typeClassId == T::Identity);
0113         return static_cast<const Data&>(data).dynamicSize();
0114     }
0115 };
0116 
0117 /**
0118  * \short A class which registers data types and creates factories for them.
0119  *
0120  * TypeSystem is a global static class which allows you to register new
0121  * AbstractType subclasses for creation.
0122  */
0123 class KDEVPLATFORMLANGUAGE_EXPORT TypeSystem
0124 {
0125     Q_DISABLE_COPY_MOVE(TypeSystem)
0126     TypeSystem() = default;
0127 public:
0128     /**
0129      * Register a new AbstractType subclass.
0130      */
0131     template <class T, class Data = typename T::Data>
0132     void registerTypeClass()
0133     {
0134         registerTypeClassInternal(new TypeFactory<T, Data>(), sizeof(Data), T::Identity);
0135     }
0136 
0137     /**
0138      * Unregister an AbstractType subclass.
0139      */
0140     template <class T, class Data = typename T::Data>
0141     void unregisterTypeClass()
0142     {
0143         unregisterTypeClassInternal(T::Identity);
0144     }
0145 
0146     /**
0147      * Create an AbstractType for the given data. The returned type must be put into a AbstractType::Ptr immediately.
0148      * Can return null if no type-factory is available for the given data (for example when a language-part is not loaded)
0149      */
0150     AbstractType* create(AbstractTypeData* data) const;
0151 
0152     /**
0153      * This just calls the correct constructor on the target. The target must be big enough to hold all the data.
0154      */
0155     void copy(const AbstractTypeData& from, AbstractTypeData& to, bool constant) const;
0156 
0157     ///Calls the dynamicSize(..) member on the given data, in the most special class. Since we cannot use virtual functions, this is the only way.
0158     uint dynamicSize(const AbstractTypeData& data) const;
0159 
0160     ///Returns the size of the derived class, not including dynamic data.
0161     ///Returns zero if the class is not known.
0162     uint dataClassSize(const AbstractTypeData& data) const;
0163 
0164     ///Calls the destructor, but does not delete anything. This is needed because the data classes must not contain virtual members.
0165     void callDestructor(AbstractTypeData* data) const;
0166 
0167     ///Returns true if the factory for this data type is loaded.
0168     ///If false is returned, then any of the other calls will fail.
0169     bool isFactoryLoaded(const AbstractTypeData& data) const;
0170 
0171     /// Access the static TypeSystem instance.
0172     static TypeSystem& self();
0173 
0174 private:
0175     void registerTypeClassInternal(AbstractTypeFactory* repo, uint dataClassSize, uint identity);
0176     void unregisterTypeClassInternal(uint identity);
0177 
0178     bool ensureFactoryLoaded(const AbstractTypeData& data) const;
0179 
0180     QHash<uint, AbstractTypeFactory*> m_factories;
0181     QHash<uint, uint> m_dataClassSizes;
0182 };
0183 
0184 /// Helper class to register an AbstractType subclass.
0185 ///
0186 /// Just use the REGISTER_TYPE(YourTypeClass) macro in your code, and you're done.
0187 template <class T, class Data = typename T::Data>
0188 struct TypeSystemRegistrator
0189 {
0190     TypeSystemRegistrator()
0191     {
0192         TypeSystem::self().registerTypeClass<T, Data>();
0193     }
0194     ~TypeSystemRegistrator()
0195     {
0196         TypeSystem::self().unregisterTypeClass<T, Data>();
0197     }
0198 private:
0199     Q_DISABLE_COPY(TypeSystemRegistrator)
0200 };
0201 
0202 ///You must add this into your source-files for every AbstractType based class
0203 ///For this to work, the class must have an "Identity" enumerator, that is unique among all types.
0204 ///It should be a unique value, but as small as possible, because a buffer at least as big as that number is created internally.
0205 #define REGISTER_TYPE(Class) TypeSystemRegistrator<Class, Class ## Data> register ## Class
0206 }
0207 
0208 #endif