diff options
author | Tom Tromey <tom@tromey.com> | 2020-10-18 11:38:10 -0600 |
---|---|---|
committer | Tom Tromey <tom@tromey.com> | 2022-07-28 14:16:50 -0600 |
commit | 08b8a139c9e8adcb4ec12a8a17e5836b8b5acb63 (patch) | |
tree | e1050b58dcebee1150881f43f64c1c186955d240 /gdb/registry.h | |
parent | 8f83e7b9262e08fa43ca6e645337511c68ddc52a (diff) | |
download | binutils-gdb-08b8a139c9e8adcb4ec12a8a17e5836b8b5acb63.tar.gz |
Rewrite registry.h
This rewrites registry.h, removing all the macros and replacing it
with relatively ordinary template classes. The result is less code
than the previous setup. It replaces large macros with a relatively
straightforward C++ class, and now manages its own cleanup.
The existing type-safe "key" class is replaced with the equivalent
template class. This approach ended up requiring relatively few
changes to the users of the registry code in gdb -- code using the key
system just required a small change to the key's declaration.
All existing users of the old C-like API are now converted to use the
type-safe API. This mostly involved changing explicit deletion
functions to be an operator() in a deleter class.
The old "save/free" two-phase process is removed, and replaced with a
single "free" phase. No existing code used both phases.
The old "free" callbacks took a parameter for the enclosing container
object. However, this wasn't truly needed and is removed here as
well.
Diffstat (limited to 'gdb/registry.h')
-rw-r--r-- | gdb/registry.h | 470 |
1 files changed, 198 insertions, 272 deletions
diff --git a/gdb/registry.h b/gdb/registry.h index 1475fd2ec26..cab9f44e3ba 100644 --- a/gdb/registry.h +++ b/gdb/registry.h @@ -22,288 +22,214 @@ #include <type_traits> -/* The macros here implement a template type and functions for - associating some user data with a container object. - - A registry is associated with a struct tag name. To attach a - registry to a structure, use DEFINE_REGISTRY. This takes the - structure tag and an access method as arguments. In the usual - case, where the registry fields appear directly in the struct, you - can use the 'REGISTRY_FIELDS' macro to declare the fields in the - struct definition, and you can pass 'REGISTRY_ACCESS_FIELD' as the - access argument to DEFINE_REGISTRY. In other cases, use - REGISTRY_FIELDS to define the fields in the appropriate spot, and - then define your own accessor to find the registry field structure - given an instance of your type. - - The API user requests a key from a registry during gdb - initialization. Later this key can be used to associate some - module-specific data with a specific container object. - - The exported API is best used via the wrapper macros: - - - register_TAG_data(TAG) - Get a new key for the container type TAG. - - - register_TAG_data_with_cleanup(TAG, SAVE, FREE) - Get a new key for the container type TAG. - SAVE and FREE are defined as void (*) (struct TAG *object, void *data) - When the container object OBJECT is destroyed, first all registered SAVE - functions are called. - Then all FREE functions are called. - Either or both may be NULL. DATA is the data associated with the - container object OBJECT. - - - clear_TAG_data(TAG, OBJECT) - Clear all the data associated with OBJECT. Should be called by the - container implementation when a container object is destroyed. - - - set_TAG_data(TAG, OBJECT, KEY, DATA) - Set the data on an object. - - - TAG_data(TAG, OBJECT, KEY) - Fetch the data for an object; returns NULL if it has not been set. -*/ - -/* This structure is used in a container to hold the data that the - registry uses. */ - -struct registry_fields -{ - void **data; - unsigned num_data; -}; +template<typename T> class registry; -/* This macro is used in a container struct definition to define the - fields used by the registry code. */ +/* An accessor class that is used by registry_key. -#define REGISTRY_FIELDS \ - struct registry_fields registry_data + 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. -/* A convenience macro for the typical case where the registry data is - kept as fields of the object. This can be passed as the ACCESS - method to DEFINE_REGISTRY. */ + However, a container may sometimes need to store the registry + elsewhere. In this case, registry_accessor can be specialized to + perform the needed indirection. */ -#define REGISTRY_ACCESS_FIELD(CONTAINER) \ - (CONTAINER) +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; + } +}; -/* Opaque type representing a container type with a registry. This - type is never defined. This is used to factor out common - functionality of all struct tag names into common code. IOW, - "struct tag name" pointers are cast to and from "struct - registry_container" pointers when calling the common registry - "backend" functions. */ -struct registry_container; +/* 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). -/* Registry callbacks have this type. */ -typedef void (*registry_data_callback) (struct registry_container *, void *); + This is handled using the registry system. -struct registry_data -{ - unsigned index; - registry_data_callback save; - registry_data_callback free; -}; + A class needing to allow this sort registration can add a registry + field. For example, you would write: -struct registry_data_registration -{ - struct registry_data *data; - struct registry_data_registration *next; -}; + class some_container { registry<some_container> registry_fields; }; -struct registry_data_registry -{ - struct registry_data_registration *registrations; - unsigned num_registrations; -}; + 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. */ -/* Registry backend functions. Client code uses the frontend - functions defined by DEFINE_REGISTRY below instead. */ - -const struct registry_data *register_data_with_cleanup - (struct registry_data_registry *registry, - registry_data_callback save, - registry_data_callback free); - -void registry_alloc_data (struct registry_data_registry *registry, - struct registry_fields *registry_fields); - -/* Cast FUNC and CONTAINER to the real types, and call FUNC, also - passing DATA. */ -typedef void (*registry_callback_adaptor) (registry_data_callback func, - struct registry_container *container, - void *data); - -void registry_clear_data (struct registry_data_registry *data_registry, - registry_callback_adaptor adaptor, - struct registry_container *container, - struct registry_fields *fields); - -void registry_container_free_data (struct registry_data_registry *data_registry, - registry_callback_adaptor adaptor, - struct registry_container *container, - struct registry_fields *fields); - -void registry_set_data (struct registry_fields *fields, - const struct registry_data *data, - void *value); - -void *registry_data (struct registry_fields *fields, - const struct registry_data *data); - -/* Define a new registry implementation. */ - -#define DEFINE_REGISTRY(TAG, ACCESS) \ -static struct registry_data_registry TAG ## _data_registry = { NULL, 0 }; \ - \ -const struct TAG ## _data * \ -register_ ## TAG ## _data_with_cleanup (void (*save) (struct TAG *, void *), \ - void (*free) (struct TAG *, void *)) \ -{ \ - return (struct TAG ## _data *) \ - register_data_with_cleanup (&TAG ## _data_registry, \ - (registry_data_callback) save, \ - (registry_data_callback) free); \ -} \ - \ -const struct TAG ## _data * \ -register_ ## TAG ## _data (void) \ -{ \ - return register_ ## TAG ## _data_with_cleanup (NULL, NULL); \ -} \ - \ -static void \ -TAG ## _alloc_data (struct TAG *container) \ -{ \ - struct registry_fields *rdata = &ACCESS (container)->registry_data; \ - \ - registry_alloc_data (&TAG ## _data_registry, rdata); \ -} \ - \ -static void \ -TAG ## registry_callback_adaptor (registry_data_callback func, \ - struct registry_container *container, \ - void *data) \ -{ \ - struct TAG *tagged_container = (struct TAG *) container; \ - \ - registry_ ## TAG ## _callback tagged_func \ - = (registry_ ## TAG ## _callback) func; \ - \ - tagged_func (tagged_container, data); \ -} \ - \ -void \ -clear_ ## TAG ## _data (struct TAG *container) \ -{ \ - struct registry_fields *rdata = &ACCESS (container)->registry_data; \ - \ - registry_clear_data (&TAG ## _data_registry, \ - TAG ## registry_callback_adaptor, \ - (struct registry_container *) container, \ - rdata); \ -} \ - \ -static void \ -TAG ## _free_data (struct TAG *container) \ -{ \ - struct registry_fields *rdata = &ACCESS (container)->registry_data; \ - \ - registry_container_free_data (&TAG ## _data_registry, \ - TAG ## registry_callback_adaptor, \ - (struct registry_container *) container, \ - rdata); \ -} \ - \ -void \ -set_ ## TAG ## _data (struct TAG *container, \ - const struct TAG ## _data *data, \ - void *value) \ -{ \ - struct registry_fields *rdata = &ACCESS (container)->registry_data; \ - \ - registry_set_data (rdata, \ - (struct registry_data *) data, \ - value); \ -} \ - \ -void * \ -TAG ## _data (struct TAG *container, const struct TAG ## _data *data) \ -{ \ - struct registry_fields *rdata = &ACCESS (container)->registry_data; \ - \ - return registry_data (rdata, \ - (struct registry_data *) data); \ -} - - -/* External declarations for the registry functions. */ - -#define DECLARE_REGISTRY(TAG) \ -struct TAG ## _data; \ -typedef void (*registry_ ## TAG ## _callback) (struct TAG *, void *); \ -extern const struct TAG ## _data *register_ ## TAG ## _data (void); \ -extern const struct TAG ## _data *register_ ## TAG ## _data_with_cleanup \ - (registry_ ## TAG ## _callback save, registry_ ## TAG ## _callback free); \ -extern void clear_ ## TAG ## _data (struct TAG *); \ -extern void set_ ## TAG ## _data (struct TAG *, \ - const struct TAG ## _data *data, \ - void *value); \ -extern void *TAG ## _data (struct TAG *, \ - const struct TAG ## _data *data); \ - \ -template<typename DATA, typename Deleter = std::default_delete<DATA>> \ -class TAG ## _key \ -{ \ -public: \ - \ - TAG ## _key () \ - : m_key (register_ ## TAG ## _data_with_cleanup (nullptr, \ - cleanup)) \ - { \ - } \ - \ - DATA *get (struct TAG *obj) const \ - { \ - return (DATA *) TAG ## _data (obj, m_key); \ - } \ - \ - void set (struct TAG *obj, DATA *data) const \ - { \ - set_ ## TAG ## _data (obj, m_key, data); \ - } \ - \ - template<typename Dummy = DATA *, typename... Args> \ - typename std::enable_if<std::is_same<Deleter, \ - std::default_delete<DATA>>::value, \ - Dummy>::type \ - emplace (struct TAG *obj, Args &&...args) const \ - { \ - DATA *result = new DATA (std::forward<Args> (args)...); \ - set (obj, result); \ - return result; \ - } \ - \ - void clear (struct TAG *obj) const \ - { \ - DATA *datum = get (obj); \ - if (datum != nullptr) \ - { \ - cleanup (obj, datum); \ - set (obj, nullptr); \ - } \ - } \ - \ -private: \ - \ - static void cleanup (struct TAG *obj, void *arg) \ - { \ - DATA *datum = (DATA *) arg; \ - Deleter d; \ - d (datum); \ - } \ - \ - const struct TAG ## _data *m_key; \ +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, 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 typename registry<T>::registry_data *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. */ + for (const auto &datum : get_registrations ()) + { + void *elt = m_fields[datum->index]; + if (elt != nullptr) + { + datum->free (elt); + m_fields[datum->index] = nullptr; + } + } + } + +private: + + /* Registry callbacks have this type. */ + typedef void (*registry_data_callback) (void *); + + /* The type of a key. */ + struct registry_data + { + unsigned index; + registry_data_callback free; + }; + + /* 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 const registry_data *new_key (registry_data_callback free) + { + std::unique_ptr<registry_data> result (new registry_data); + std::vector<std::unique_ptr<registry_data>> ®istrations + = get_registrations (); + result->index = registrations.size (); + result->free = free; + registrations.emplace_back (std::move (result)); + return registrations.back ().get (); + } + + /* Set the datum associated with KEY in this container. */ + void set (const registry_data *key, void *datum) + { + m_fields[key->index] = datum; + } + + /* Fetch the datum associated with KEY in this container. If 'set' + has not been called for this key, nullptr is returned. */ + void *get (const registry_data *key) + { + return m_fields[key->index]; + } + + /* 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. We do separate allocations here so that the + addresses are stable and can be used as keys. */ + static std::vector<std::unique_ptr<registry_data>> &get_registrations () + { + static std::vector<std::unique_ptr<registry_data>> registrations; + return registrations; + } }; #endif /* REGISTRY_H */ |