|  | /* Macros for general registry objects. | 
|  |  | 
|  | Copyright (C) 2011-2022 Free Software Foundation, Inc. | 
|  |  | 
|  | This file is part of GDB. | 
|  |  | 
|  | This program is free software; you can redistribute it and/or modify | 
|  | it under the terms of the GNU General Public License as published by | 
|  | the Free Software Foundation; either version 3 of the License, or | 
|  | (at your option) any later version. | 
|  |  | 
|  | This program is distributed in the hope that it will be useful, | 
|  | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | GNU General Public License for more details. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License | 
|  | along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ | 
|  |  | 
|  | #ifndef REGISTRY_H | 
|  | #define REGISTRY_H | 
|  |  | 
|  | #include <type_traits> | 
|  |  | 
|  | template<typename T> class registry; | 
|  |  | 
|  | /* An accessor class that is used by registry_key. | 
|  |  | 
|  | Normally, a container class has a registry<> field named | 
|  | "registry_fields".  In this case, the default accessor is used, as | 
|  | it simply returns the object. | 
|  |  | 
|  | However, a container may sometimes need to store the registry | 
|  | elsewhere.  In this case, registry_accessor can be specialized to | 
|  | perform the needed indirection.  */ | 
|  |  | 
|  | template<typename T> | 
|  | struct registry_accessor | 
|  | { | 
|  | /* Given a container of type T, return its registry.  */ | 
|  | static registry<T> *get (T *obj) | 
|  | { | 
|  | return &obj->registry_fields; | 
|  | } | 
|  | }; | 
|  |  | 
|  | /* In gdb, sometimes there is a need for one module (e.g., the Python | 
|  | Type code) to attach some data to another object (e.g., an | 
|  | objfile); but it's also desirable that this be done such that the | 
|  | base object (the objfile in this example) not need to know anything | 
|  | about the attaching module (the Python code). | 
|  |  | 
|  | This is handled using the registry system. | 
|  |  | 
|  | A class needing to allow this sort registration can add a registry | 
|  | field.  For example, you would write: | 
|  |  | 
|  | class some_container { registry<some_container> registry_fields; }; | 
|  |  | 
|  | The name of the field matters by default, see registry_accessor. | 
|  |  | 
|  | A module wanting to attach data to instances of some_container uses | 
|  | the "key" class to register a key.  This key can then be passed to | 
|  | the "get" and "set" methods to handle this module's data.  */ | 
|  |  | 
|  | template<typename T> | 
|  | class registry | 
|  | { | 
|  | public: | 
|  |  | 
|  | registry () | 
|  | : m_fields (get_registrations ().size ()) | 
|  | { | 
|  | } | 
|  |  | 
|  | ~registry () | 
|  | { | 
|  | clear_registry (); | 
|  | } | 
|  |  | 
|  | DISABLE_COPY_AND_ASSIGN (registry); | 
|  |  | 
|  | /* A type-safe registry key. | 
|  |  | 
|  | The registry itself holds just a "void *".  This is not always | 
|  | convenient to manage, so this template class can be used instead, | 
|  | to provide a type-safe interface, that also helps manage the | 
|  | lifetime of the stored objects. | 
|  |  | 
|  | When the container is destroyed, this key arranges to destroy the | 
|  | underlying data using Deleter.  This defaults to | 
|  | std::default_delete.  */ | 
|  |  | 
|  | template<typename DATA, typename Deleter = std::default_delete<DATA>> | 
|  | class key | 
|  | { | 
|  | public: | 
|  |  | 
|  | key () | 
|  | : m_key (registry<T>::new_key (cleanup)) | 
|  | { | 
|  | } | 
|  |  | 
|  | DISABLE_COPY_AND_ASSIGN (key); | 
|  |  | 
|  | /* Fetch the data attached to OBJ that is associated with this key. | 
|  | If no such data has been attached, nullptr is returned.  */ | 
|  | DATA *get (T *obj) const | 
|  | { | 
|  | registry<T> *reg_obj = registry_accessor<T>::get (obj); | 
|  | return (DATA *) reg_obj->get (m_key); | 
|  | } | 
|  |  | 
|  | /* Attach DATA to OBJ, associated with this key.  Note that any | 
|  | previous data is simply dropped -- if destruction is needed, | 
|  | 'clear' should be called.  */ | 
|  | void set (T *obj, DATA *data) const | 
|  | { | 
|  | registry<T> *reg_obj = registry_accessor<T>::get (obj); | 
|  | reg_obj->set (m_key, (typename std::remove_const<DATA> *) data); | 
|  | } | 
|  |  | 
|  | /* If this key uses the default deleter, then this method is | 
|  | available.  It emplaces a new instance of the associated data | 
|  | type and attaches it to OBJ using this key.  The arguments, if | 
|  | any, are forwarded to the constructor.  */ | 
|  | template<typename Dummy = DATA *, typename... Args> | 
|  | typename std::enable_if<std::is_same<Deleter, | 
|  | std::default_delete<DATA>>::value, | 
|  | Dummy>::type | 
|  | emplace (T *obj, Args &&...args) const | 
|  | { | 
|  | DATA *result = new DATA (std::forward<Args> (args)...); | 
|  | set (obj, result); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | /* Clear the data attached to OBJ that is associated with this KEY. | 
|  | Any existing data is destroyed using the deleter, and the data is | 
|  | reset to nullptr.  */ | 
|  | void clear (T *obj) const | 
|  | { | 
|  | DATA *datum = get (obj); | 
|  | if (datum != nullptr) | 
|  | { | 
|  | cleanup (datum); | 
|  | set (obj, nullptr); | 
|  | } | 
|  | } | 
|  |  | 
|  | private: | 
|  |  | 
|  | /* A helper function that is called by the registry to delete the | 
|  | contained object.  */ | 
|  | static void cleanup (void *arg) | 
|  | { | 
|  | DATA *datum = (DATA *) arg; | 
|  | Deleter d; | 
|  | d (datum); | 
|  | } | 
|  |  | 
|  | /* The underlying key.  */ | 
|  | const unsigned m_key; | 
|  | }; | 
|  |  | 
|  | /* Clear all the data associated with this container.  This is | 
|  | dangerous and should not normally be done.  */ | 
|  | void clear_registry () | 
|  | { | 
|  | /* Call all the free functions.  */ | 
|  | std::vector<registry_data_callback> ®istrations | 
|  | = get_registrations (); | 
|  | unsigned last = registrations.size (); | 
|  | for (unsigned i = 0; i < last; ++i) | 
|  | { | 
|  | void *elt = m_fields[i]; | 
|  | if (elt != nullptr) | 
|  | { | 
|  | registrations[i] (elt); | 
|  | m_fields[i] = nullptr; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | private: | 
|  |  | 
|  | /* Registry callbacks have this type.  */ | 
|  | typedef void (*registry_data_callback) (void *); | 
|  |  | 
|  | /* Get a new key for this particular registry.  FREE is a callback. | 
|  | When the container object is destroyed, all FREE functions are | 
|  | called.  The data associated with the container object is passed | 
|  | to the callback.  */ | 
|  | static unsigned new_key (registry_data_callback free) | 
|  | { | 
|  | std::vector<registry_data_callback> ®istrations | 
|  | = get_registrations (); | 
|  | unsigned result = registrations.size (); | 
|  | registrations.push_back (free); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | /* Set the datum associated with KEY in this container.  */ | 
|  | void set (unsigned key, void *datum) | 
|  | { | 
|  | m_fields[key] = datum; | 
|  | } | 
|  |  | 
|  | /* Fetch the datum associated with KEY in this container.  If 'set' | 
|  | has not been called for this key, nullptr is returned.  */ | 
|  | void *get (unsigned key) | 
|  | { | 
|  | return m_fields[key]; | 
|  | } | 
|  |  | 
|  | /* The data stored in this instance.  */ | 
|  | std::vector<void *> m_fields; | 
|  |  | 
|  | /* Return a reference to the vector of all the registrations that | 
|  | have been made.  */ | 
|  | static std::vector<registry_data_callback> &get_registrations () | 
|  | { | 
|  | static std::vector<registry_data_callback> registrations; | 
|  | return registrations; | 
|  | } | 
|  | }; | 
|  |  | 
|  | #endif /* REGISTRY_H */ |