gdb: Have setter and getter callbacks for settings
authorLancelot SIX <lsix@lancelotsix.com>
Tue, 14 Sep 2021 22:36:53 +0000 (23:36 +0100)
committerLancelot SIX <lsix@lancelotsix.com>
Sun, 3 Oct 2021 16:53:16 +0000 (17:53 +0100)
The main motivation behind this improvement is to help the
implementation of a patch Simon Marchi is preparing to fix a bug when
MI or Python try to access parameters that are inferior dependent (see
PR/28085).

This commit extends the previous ones, which introduces the setting
object to represent a static variable whose value can be set or shown
with the appropriate commands.  This patch proposes that a setting can
either contain a pointer to a static variable holding a setting, or
pointers to a pair of setter and getter callback functions.

The callbacks functions can be used to retrieve or change the value with
custom logic.  This is useful when the source of truth for a given
setting is not contained in the variable pointed to by the setting
instance.

Given that the callback function call is hidden within the setting
abstraction introduced earlier, none of the sites accessing the setting
needs to be updated.  The registered getter or setter is used whatever
the way to access it is (through MI, Python, Guile, the "with" command
and the $_gdb_setting / $_gdb_setting_str convenience functions).

All the add_setshow_*_cmd are given a new overload that will accept the
pair of function pointers (set / get functions) instead of the pointer
to a global variable.

Tested on GNU/Linux x86_64 with no regression observed.

Change-Id: Ieb81fef57550632ff66e6aa85f637372a226be8c
Co-authored-by: Simon Marchi <simon.marchi@polymtl.ca>
gdb/cli/cli-decode.c
gdb/command.h

index b3bf62761574d177b06737e1df9c79b641df33e1..3b0830e4c17f9c005b339de2a8dea12a5b5d9955 100644 (file)
@@ -460,6 +460,10 @@ empty_func (const char *args, int from_tty, cmd_list_element *c)
    CLASS is as in add_cmd.
    VAR_TYPE is the kind of thing we are setting.
    VAR is address of the variable being controlled by this command.
+   SET_SETTING_FUNC is a pointer to an optional function callback used to set
+   the setting value.
+   GET_SETTING_FUNC is a pointer to an optional function callback used to get
+   the setting value.
    DOC is the documentation string.  */
 
 static struct cmd_list_element *
@@ -486,9 +490,11 @@ add_set_or_show_cmd (const char *name,
 /* Add element named NAME to both the command SET_LIST and SHOW_LIST.
    CLASS is as in add_cmd.  VAR_TYPE is the kind of thing we are
    setting.  VAR is address of the variable being controlled by this
-   command.  SET_FUNC and SHOW_FUNC are the callback functions (if
-   non-NULL).  SET_DOC, SHOW_DOC and HELP_DOC are the documentation
-   strings.
+   command.  If nullptr is given as VAR, then both SET_SETTING_FUNC and
+   GET_SETTING_FUNC must be provided. SET_SETTING_FUNC and GET_SETTING_FUNC are
+   callbacks used to access and modify the underlying property, whatever its
+   storage is.  SET_FUNC and SHOW_FUNC are the callback functions (if non-NULL).
+   SET_DOC, SHOW_DOC and HELP_DOC are the documentation strings.
 
    Return the newly created set and show commands.  */
 
@@ -544,13 +550,16 @@ add_setshow_cmd_full (const char *name,
                      var_types var_type, T *var,
                      const char *set_doc, const char *show_doc,
                      const char *help_doc,
+                     setting_setter_ftype<T> set_setting_func,
+                     setting_getter_ftype<T> get_setting_func,
                      cmd_func_ftype *set_func,
                      show_value_ftype *show_func,
                      struct cmd_list_element **set_list,
                      struct cmd_list_element **show_list)
 {
   auto erased_args
-    = setting::erase_args (var_type, var);
+    = setting::erase_args (var_type, var,
+                          set_setting_func, get_setting_func);
 
   return add_setshow_cmd_full_erased (name,
                                      theclass,
@@ -584,12 +593,36 @@ add_setshow_enum_cmd (const char *name,
   set_show_commands commands
     =  add_setshow_cmd_full<const char *> (name, theclass, var_enum, var,
                                           set_doc, show_doc, help_doc,
-                                          set_func, show_func,
-                                          set_list, show_list);
+                                          nullptr, nullptr, set_func,
+                                          show_func, set_list, show_list);
   commands.set->enums = enumlist;
   return commands;
 }
 
+/* Same as above but using a getter and a setter function instead of a pointer
+   to a global storage buffer.  */
+
+set_show_commands
+add_setshow_enum_cmd (const char *name, command_class theclass,
+                     const char *const *enumlist, const char *set_doc,
+                     const char *show_doc, const char *help_doc,
+                     setting_setter_ftype<const char *> set_func,
+                     setting_getter_ftype<const char *> get_func,
+                     show_value_ftype *show_func,
+                     cmd_list_element **set_list,
+                     cmd_list_element **show_list)
+{
+  auto cmds = add_setshow_cmd_full<const char *> (name, theclass, var_enum,
+                                                 nullptr, set_doc, show_doc,
+                                                 help_doc, set_func, get_func,
+                                                 nullptr, show_func, set_list,
+                                                 show_list);
+
+  cmds.set->enums = enumlist;
+
+  return cmds;
+}
+
 /* See cli-decode.h.  */
 const char * const auto_boolean_enums[] = { "on", "off", "auto", NULL };
 
@@ -610,17 +643,42 @@ add_setshow_auto_boolean_cmd (const char *name,
                              struct cmd_list_element **show_list)
 {
   set_show_commands commands
-    = add_setshow_cmd_full<enum auto_boolean> (name, theclass,
-                                              var_auto_boolean,var,
-                                              set_doc, show_doc, help_doc,
-                                              set_func, show_func,
-                                              set_list, show_list);
+    = add_setshow_cmd_full<enum auto_boolean> (name, theclass, var_auto_boolean,
+                                              var, set_doc, show_doc, help_doc,
+                                              nullptr, nullptr, set_func,
+                                              show_func, set_list, show_list);
 
   commands.set->enums = auto_boolean_enums;
 
   return commands;
 }
 
+/* Same as above but using a getter and a setter function instead of a pointer
+   to a global storage buffer.  */
+
+set_show_commands
+add_setshow_auto_boolean_cmd (const char *name, command_class theclass,
+                             const char *set_doc, const char *show_doc,
+                             const char *help_doc,
+                             setting_setter_ftype<enum auto_boolean> set_func,
+                             setting_getter_ftype<enum auto_boolean> get_func,
+                             show_value_ftype *show_func,
+                             cmd_list_element **set_list,
+                             cmd_list_element **show_list)
+{
+  auto cmds = add_setshow_cmd_full<enum auto_boolean> (name, theclass,
+                                                      var_auto_boolean,
+                                                      nullptr, set_doc,
+                                                      show_doc, help_doc,
+                                                      set_func, get_func,
+                                                      nullptr, show_func,
+                                                      set_list, show_list);
+
+  cmds.set->enums = auto_boolean_enums;
+
+  return cmds;
+}
+
 /* See cli-decode.h.  */
 const char * const boolean_enums[] = { "on", "off", NULL };
 
@@ -642,7 +700,7 @@ add_setshow_boolean_cmd (const char *name, enum command_class theclass, bool *va
   set_show_commands commands
     = add_setshow_cmd_full<bool> (name, theclass, var_boolean, var,
                                  set_doc, show_doc, help_doc,
-                                 set_func, show_func,
+                                 nullptr, nullptr, set_func, show_func,
                                  set_list, show_list);
 
   commands.set->enums = boolean_enums;
@@ -650,6 +708,29 @@ add_setshow_boolean_cmd (const char *name, enum command_class theclass, bool *va
   return commands;
 }
 
+/* Same as above but using a getter and a setter function instead of a pointer
+   to a global storage buffer.  */
+
+set_show_commands
+add_setshow_boolean_cmd (const char *name, command_class theclass,
+                        const char *set_doc, const char *show_doc,
+                        const char *help_doc,
+                        setting_setter_ftype<bool> set_func,
+                        setting_getter_ftype<bool> get_func,
+                        show_value_ftype *show_func,
+                        cmd_list_element **set_list,
+                        cmd_list_element **show_list)
+{
+  auto cmds = add_setshow_cmd_full<bool> (name, theclass, var_boolean, nullptr,
+                                         set_doc, show_doc, help_doc,
+                                         set_func, get_func, nullptr,
+                                         show_func, set_list, show_list);
+
+  cmds.set->enums = boolean_enums;
+
+  return cmds;
+}
+
 /* Add element named NAME to both the set and show command LISTs (the
    list for set/show or some sublist thereof).  */
 
@@ -666,14 +747,38 @@ add_setshow_filename_cmd (const char *name, enum command_class theclass,
   set_show_commands commands
     = add_setshow_cmd_full<std::string> (name, theclass, var_filename, var,
                                         set_doc, show_doc, help_doc,
-                                        set_func, show_func,
-                                        set_list, show_list);
+                                        nullptr, nullptr, set_func,
+                                        show_func, set_list, show_list);
 
   set_cmd_completer (commands.set, filename_completer);
 
   return commands;
 }
 
+/* Same as above but using a getter and a setter function instead of a pointer
+   to a global storage buffer.  */
+
+set_show_commands
+add_setshow_filename_cmd (const char *name, command_class theclass,
+                         const char *set_doc, const char *show_doc,
+                         const char *help_doc,
+                         setting_setter_ftype<std::string> set_func,
+                         setting_getter_ftype<std::string> get_func,
+                         show_value_ftype *show_func,
+                         cmd_list_element **set_list,
+                         cmd_list_element **show_list)
+{
+  auto cmds = add_setshow_cmd_full<std::string> (name, theclass, var_filename,
+                                                nullptr, set_doc, show_doc,
+                                                help_doc, set_func, get_func,
+                                                nullptr, show_func, set_list,
+                                                show_list);
+
+  set_cmd_completer (cmds.set, filename_completer);
+
+  return cmds;
+}
+
 /* Add element named NAME to both the set and show command LISTs (the
    list for set/show or some sublist thereof).  */
 
@@ -689,9 +794,9 @@ add_setshow_string_cmd (const char *name, enum command_class theclass,
 {
   set_show_commands commands
     = add_setshow_cmd_full<std::string> (name, theclass, var_string, var,
-                                        set_doc, show_doc, help_doc,
-                                        set_func, show_func,
-                                        set_list, show_list);
+                                       set_doc, show_doc, help_doc,
+                                       nullptr, nullptr, set_func,
+                                       show_func, set_list, show_list);
 
   /* Disable the default symbol completer.  */
   set_cmd_completer (commands.set, nullptr);
@@ -699,6 +804,31 @@ add_setshow_string_cmd (const char *name, enum command_class theclass,
   return commands;
 }
 
+/* Same as above but using a getter and a setter function instead of a pointer
+   to a global storage buffer.  */
+
+set_show_commands
+add_setshow_string_cmd (const char *name, command_class theclass,
+                       const char *set_doc, const char *show_doc,
+                       const char *help_doc,
+                       setting_setter_ftype<std::string> set_func,
+                       setting_getter_ftype<std::string> get_func,
+                       show_value_ftype *show_func,
+                       cmd_list_element **set_list,
+                       cmd_list_element **show_list)
+{
+  auto cmds = add_setshow_cmd_full<std::string> (name, theclass, var_string,
+                                                nullptr, set_doc, show_doc,
+                                                help_doc, set_func, get_func,
+                                                nullptr, show_func, set_list,
+                                                show_list);
+
+  /* Disable the default symbol completer.  */
+  set_cmd_completer (cmds.set, nullptr);
+
+  return cmds;
+}
+
 /* Add element named NAME to both the set and show command LISTs (the
    list for set/show or some sublist thereof).  */
 
@@ -715,8 +845,8 @@ add_setshow_string_noescape_cmd (const char *name, enum command_class theclass,
   set_show_commands commands
     = add_setshow_cmd_full<std::string> (name, theclass, var_string_noescape,
                                         var, set_doc, show_doc, help_doc,
-                                        set_func, show_func, set_list,
-                                        show_list);
+                                        nullptr, nullptr, set_func, show_func,
+                                        set_list, show_list);
 
   /* Disable the default symbol completer.  */
   set_cmd_completer (commands.set, nullptr);
@@ -724,6 +854,32 @@ add_setshow_string_noescape_cmd (const char *name, enum command_class theclass,
   return commands;
 }
 
+/* Same as above but using a getter and a setter function instead of a pointer
+   to a global storage buffer.  */
+
+set_show_commands
+add_setshow_string_noescape_cmd (const char *name, command_class theclass,
+                                const char *set_doc, const char *show_doc,
+                                const char *help_doc,
+                                setting_setter_ftype<std::string> set_func,
+                                setting_getter_ftype<std::string> get_func,
+                                show_value_ftype *show_func,
+                                cmd_list_element **set_list,
+                                cmd_list_element **show_list)
+{
+  auto cmds = add_setshow_cmd_full<std::string> (name, theclass,
+                                                var_string_noescape, nullptr,
+                                                set_doc, show_doc, help_doc,
+                                                set_func, get_func,
+                                                nullptr, show_func, set_list,
+                                                show_list);
+
+  /* Disable the default symbol completer.  */
+  set_cmd_completer (cmds.set, nullptr);
+
+  return cmds;
+}
+
 /* Add element named NAME to both the set and show command LISTs (the
    list for set/show or some sublist thereof).  */
 
@@ -740,14 +896,38 @@ add_setshow_optional_filename_cmd (const char *name, enum command_class theclass
   set_show_commands commands
     = add_setshow_cmd_full<std::string> (name, theclass, var_optional_filename,
                                         var, set_doc, show_doc, help_doc,
-                                        set_func, show_func, set_list,
-                                        show_list);
+                                        nullptr, nullptr, set_func, show_func,
+                                        set_list, show_list);
 
   set_cmd_completer (commands.set, filename_completer);
 
   return commands;
 }
 
+/* Same as above but using a getter and a setter function instead of a pointer
+   to a global storage buffer.  */
+
+set_show_commands
+add_setshow_optional_filename_cmd (const char *name, command_class theclass,
+                                  const char *set_doc, const char *show_doc,
+                                  const char *help_doc,
+                                  setting_setter_ftype<std::string> set_func,
+                                  setting_getter_ftype<std::string> get_func,
+                                  show_value_ftype *show_func,
+                                  cmd_list_element **set_list,
+                                  cmd_list_element **show_list)
+{
+  auto cmds =
+    add_setshow_cmd_full<std::string> (name, theclass, var_optional_filename,
+                                      nullptr, set_doc, show_doc, help_doc,
+                                      set_func, get_func, nullptr, show_func,
+                                      set_list,show_list);
+
+  set_cmd_completer (cmds.set, filename_completer);
+
+  return cmds;
+}
+
 /* Completes on literal "unlimited".  Used by integer commands that
    support a special "unlimited" value.  */
 
@@ -782,15 +962,39 @@ add_setshow_integer_cmd (const char *name, enum command_class theclass,
                         struct cmd_list_element **show_list)
 {
   set_show_commands commands
-    = add_setshow_cmd_full<int> (name, theclass, var_integer, var, set_doc,
-                                show_doc, help_doc, set_func, show_func,
-                                set_list, show_list);
+    = add_setshow_cmd_full<int> (name, theclass, var_integer, var,
+                                set_doc, show_doc, help_doc,
+                                nullptr, nullptr, set_func,
+                                show_func, set_list, show_list);
 
   set_cmd_completer (commands.set, integer_unlimited_completer);
 
   return commands;
 }
 
+/* Same as above but using a getter and a setter function instead of a pointer
+   to a global storage buffer.  */
+
+set_show_commands
+add_setshow_integer_cmd (const char *name, command_class theclass,
+                        const char *set_doc, const char *show_doc,
+                        const char *help_doc,
+                        setting_setter_ftype<int> set_func,
+                        setting_getter_ftype<int> get_func,
+                        show_value_ftype *show_func,
+                        cmd_list_element **set_list,
+                        cmd_list_element **show_list)
+{
+  auto cmds = add_setshow_cmd_full<int> (name, theclass, var_integer, nullptr,
+                                        set_doc, show_doc, help_doc, set_func,
+                                        get_func, nullptr, show_func, set_list,
+                                        show_list);
+
+  set_cmd_completer (cmds.set, integer_unlimited_completer);
+
+  return cmds;
+}
+
 /* Add element named NAME to both the set and show command LISTs (the
    list for set/show or some sublist thereof).  CLASS is as in
    add_cmd.  VAR is address of the variable which will contain the
@@ -809,14 +1013,38 @@ add_setshow_uinteger_cmd (const char *name, enum command_class theclass,
   set_show_commands commands
     = add_setshow_cmd_full<unsigned int> (name, theclass, var_uinteger, var,
                                          set_doc, show_doc, help_doc,
-                                         set_func, show_func,
-                                         set_list, show_list);
+                                         nullptr, nullptr, set_func,
+                                         show_func, set_list, show_list);
 
   set_cmd_completer (commands.set, integer_unlimited_completer);
 
   return commands;
 }
 
+/* Same as above but using a getter and a setter function instead of a pointer
+   to a global storage buffer.  */
+
+set_show_commands
+add_setshow_uinteger_cmd (const char *name, command_class theclass,
+                         const char *set_doc, const char *show_doc,
+                         const char *help_doc,
+                         setting_setter_ftype<unsigned int> set_func,
+                         setting_getter_ftype<unsigned int> get_func,
+                         show_value_ftype *show_func,
+                         cmd_list_element **set_list,
+                         cmd_list_element **show_list)
+{
+  auto cmds = add_setshow_cmd_full<unsigned int> (name, theclass, var_uinteger,
+                                                 nullptr, set_doc, show_doc,
+                                                 help_doc, set_func, get_func,
+                                                 nullptr, show_func, set_list,
+                                                 show_list);
+
+  set_cmd_completer (cmds.set, integer_unlimited_completer);
+
+  return cmds;
+}
+
 /* Add element named NAME to both the set and show command LISTs (the
    list for set/show or some sublist thereof).  CLASS is as in
    add_cmd.  VAR is address of the variable which will contain the
@@ -834,8 +1062,27 @@ add_setshow_zinteger_cmd (const char *name, enum command_class theclass,
 {
   return add_setshow_cmd_full<int> (name, theclass, var_zinteger, var,
                                    set_doc, show_doc, help_doc,
-                                   set_func, show_func,
-                                   set_list, show_list);
+                                   nullptr, nullptr, set_func,
+                                   show_func, set_list, show_list);
+}
+
+/* Same as above but using a getter and a setter function instead of a pointer
+   to a global storage buffer.  */
+
+set_show_commands
+add_setshow_zinteger_cmd (const char *name, command_class theclass,
+                         const char *set_doc, const char *show_doc,
+                         const char *help_doc,
+                         setting_setter_ftype<int> set_func,
+                         setting_getter_ftype<int> get_func,
+                         show_value_ftype *show_func,
+                         cmd_list_element **set_list,
+                         cmd_list_element **show_list)
+{
+  return add_setshow_cmd_full<int> (name, theclass, var_zinteger, nullptr,
+                                   set_doc, show_doc, help_doc, set_func,
+                                   get_func, nullptr, show_func, set_list,
+                                   show_list);
 }
 
 set_show_commands
@@ -852,14 +1099,39 @@ add_setshow_zuinteger_unlimited_cmd (const char *name,
 {
   set_show_commands commands
     = add_setshow_cmd_full<int> (name, theclass, var_zuinteger_unlimited, var,
-                                set_doc, show_doc, help_doc, set_func,
-                                show_func, set_list, show_list);
+                                set_doc, show_doc, help_doc, nullptr,
+                                nullptr, set_func, show_func, set_list,
+                                show_list);
 
   set_cmd_completer (commands.set, integer_unlimited_completer);
 
   return commands;
 }
 
+/* Same as above but using a getter and a setter function instead of a pointer
+   to a global storage buffer.  */
+
+set_show_commands
+add_setshow_zuinteger_unlimited_cmd (const char *name, command_class theclass,
+                                    const char *set_doc, const char *show_doc,
+                                    const char *help_doc,
+                                    setting_setter_ftype<int> set_func,
+                                    setting_getter_ftype<int> get_func,
+                                    show_value_ftype *show_func,
+                                    cmd_list_element **set_list,
+                                    cmd_list_element **show_list)
+{
+  auto cmds
+    = add_setshow_cmd_full<int> (name, theclass, var_zuinteger_unlimited,
+                                nullptr, set_doc, show_doc, help_doc, set_func,
+                                get_func, nullptr, show_func, set_list,
+                                show_list);
+
+  set_cmd_completer (cmds.set, integer_unlimited_completer);
+
+  return cmds;
+}
+
 /* Add element named NAME to both the set and show command LISTs (the
    list for set/show or some sublist thereof).  CLASS is as in
    add_cmd.  VAR is address of the variable which will contain the
@@ -877,7 +1149,27 @@ add_setshow_zuinteger_cmd (const char *name, enum command_class theclass,
 {
   return add_setshow_cmd_full<unsigned int> (name, theclass, var_zuinteger,
                                             var, set_doc, show_doc, help_doc,
-                                            set_func, show_func, set_list,
+                                            nullptr, nullptr, set_func,
+                                            show_func, set_list, show_list);
+}
+
+/* Same as above but using a getter and a setter function instead of a pointer
+   to a global storage buffer.  */
+
+set_show_commands
+add_setshow_zuinteger_cmd (const char *name, command_class theclass,
+                          const char *set_doc, const char *show_doc,
+                          const char *help_doc,
+                          setting_setter_ftype<unsigned int> set_func,
+                          setting_getter_ftype<unsigned int> get_func,
+                          show_value_ftype *show_func,
+                          cmd_list_element **set_list,
+                          cmd_list_element **show_list)
+{
+  return add_setshow_cmd_full<unsigned int> (name, theclass, var_zuinteger,
+                                            nullptr, set_doc, show_doc,
+                                            help_doc, set_func, get_func,
+                                            nullptr, show_func, set_list,
                                             show_list);
 }
 
index e6e6ec8a5d86991bfa85d561ac2a56c32759b7f9..2abfbc497b9885093dd10d634b49b8ae121242ed 100644 (file)
@@ -181,6 +181,20 @@ inline bool var_type_uses<const char *> (var_types t)
   return t == var_enum;
 }
 
+/* Function signature for a callback used to get a value from a setting.  */
+
+template<typename T>
+using setting_getter_ftype = const T &(*) ();
+
+/* Function signature for a callback used to set a value to a setting.  */
+
+template<typename T>
+using setting_setter_ftype = void (*) (const T &);
+
+/* Generic/type-erased function pointer.  */
+
+using erased_func = void (*) ();
+
 /* Interface for getting and setting a setting's value.
 
    The underlying data can be of any VAR_TYPES type.  */
@@ -204,14 +218,30 @@ struct setting
   struct erased_args
   {
     void *var;
+    erased_func setter;
+    erased_func getter;
   };
 
   template<typename T>
-  static erased_args erase_args (var_types var_type, T *var)
+  static erased_args erase_args (var_types var_type,
+                                T *var,
+                                setting_setter_ftype<T> set_setting_func,
+                                setting_getter_ftype<T> get_setting_func)
   {
     gdb_assert (var_type_uses<T> (var_type));
-
-    return {var};
+  /* The getter and the setter must be both provided or both omitted.  */
+    gdb_assert
+      ((set_setting_func == nullptr) == (get_setting_func == nullptr));
+
+  /* The caller must provide a pointer to a variable or get/set functions, but
+     not both.  */
+    gdb_assert ((set_setting_func == nullptr) != (var == nullptr));
+
+    return {
+       var,
+       reinterpret_cast<erased_func> (set_setting_func),
+       reinterpret_cast<erased_func> (get_setting_func)
+    };
   }
 
   /* Create a setting backed by pre-validated type-erased args.
@@ -219,16 +249,37 @@ struct setting
      (see VAR_TYPE_USES).  */
   setting (var_types var_type, const erased_args &args)
     : m_var_type (var_type),
-      m_var (args.var)
+      m_var (args.var),
+      m_getter (args.getter),
+      m_setter (args.setter)
   {
   }
 
-  /* Access the type of the current setting.  */
-  var_types type () const
+  /* Create a setting backed by setter and getter functions.
+
+     Type T must match the var type VAR_TYPE (see VAR_TYPE_USES).  */
+  template<typename T>
+  setting (var_types var_type,
+          setting_setter_ftype<T> setter,
+          setting_getter_ftype<T> getter)
+    : m_var_type (var_type)
   {
-    return m_var_type;
+    gdb_assert (var_type_uses<T> (var_type));
+
+    /* Getters and setters are cast to and from the arbitrary `void (*) ()`
+       function pointer type.  Make sure that the two types are really of the
+       same size.  */
+    gdb_static_assert (sizeof (m_getter) == sizeof (getter));
+    gdb_static_assert (sizeof (m_setter) == sizeof (setter));
+
+    m_getter = reinterpret_cast<erased_func> (getter);
+    m_setter = reinterpret_cast<erased_func> (setter);
   }
 
+  /* Access the type of the current setting.  */
+  var_types type () const
+  { return m_var_type; }
+
   /* Return the current value.
 
      The template parameter T is the type of the variable used to store the
@@ -239,13 +290,23 @@ struct setting
     gdb_assert (var_type_uses<T> (m_var_type));
     gdb_assert (m_var != nullptr);
 
-    return *static_cast<const T *> (m_var);
+    if (m_var == nullptr)
+      {
+       gdb_assert (m_getter != nullptr);
+       auto getter = reinterpret_cast<setting_getter_ftype<T>> (m_getter);
+       return getter ();
+      }
+    else
+      return *static_cast<const T *> (m_var);
   }
 
   /* Sets the value of the setting to V.
 
-     The template parameter T indicates the type of the variable used to
-     store the setting.
+     If we have a user-provided setter, use it to set the setting.  Otherwise
+     copy the value V to the internally referenced buffer.
+
+     The template parameter T indicates the type of the variable used to store
+     the setting.
 
      The var_type of the setting must match T.  */
   template<typename T>
@@ -255,16 +316,32 @@ struct setting
        this instantiation.  */
     gdb_assert (var_type_uses<T> (m_var_type));
 
-    *static_cast<T *> (m_var) = v;
+    if (m_var == nullptr)
+      {
+       gdb_assert (m_setter != nullptr);
+       auto setter = reinterpret_cast<setting_setter_ftype<T>> (m_setter);
+       setter (v);
+      }
+    else
+      *static_cast<T *> (m_var) = v;
   }
 
 private:
-  /* The type of the variable M_VAR is pointing to.  */
+  /* The type of the variable M_VAR is pointing to, or that M_GETTER / M_SETTER
+     get or set.  */
   var_types m_var_type;
 
-  /* Pointer to the enclosed variable.  The type of the variable is encoded
-     in M_VAR_TYPE.  */
-  void *m_var;
+  /* Pointer to the enclosed variable
+
+     Either M_VAR is non-nullptr, or both M_GETTER and M_SETTER are
+     non-nullptr.  */
+  void *m_var = nullptr;
+
+  /* Pointer to a user provided getter.  */
+  erased_func m_getter = nullptr;
+
+  /* Pointer to a user provided setter.  */
+  erased_func m_setter = nullptr;
 };
 
 /* This structure records one command'd definition.  */
@@ -548,72 +625,159 @@ extern set_show_commands add_setshow_enum_cmd
    show_value_ftype *show_func, cmd_list_element **set_list,
    cmd_list_element **show_list);
 
+extern set_show_commands add_setshow_enum_cmd
+  (const char *name, command_class theclass, const char *const *enumlist,
+   const char *set_doc, const char *show_doc,
+   const char *help_doc, setting_setter_ftype<const char *> set_func,
+   setting_getter_ftype<const char *> get_func, show_value_ftype *show_func,
+   cmd_list_element **set_list, cmd_list_element **show_list);
+
 extern set_show_commands add_setshow_auto_boolean_cmd
   (const char *name, command_class theclass, auto_boolean *var,
    const char *set_doc, const char *show_doc, const char *help_doc,
    cmd_func_ftype *set_func, show_value_ftype *show_func,
    cmd_list_element **set_list, cmd_list_element **show_list);
 
+extern set_show_commands add_setshow_auto_boolean_cmd
+  (const char *name, command_class theclass, const char *set_doc,
+   const char *show_doc, const char *help_doc,
+   setting_setter_ftype<enum auto_boolean> set_func,
+   setting_getter_ftype<enum auto_boolean> get_func,
+   show_value_ftype *show_func, cmd_list_element **set_list,
+   cmd_list_element **show_list);
+
 extern set_show_commands add_setshow_boolean_cmd
   (const char *name, command_class theclass, bool *var, const char *set_doc,
    const char *show_doc, const char *help_doc, cmd_func_ftype *set_func,
    show_value_ftype *show_func, cmd_list_element **set_list,
    cmd_list_element **show_list);
 
+extern set_show_commands add_setshow_boolean_cmd
+  (const char *name, command_class theclass, const char *set_doc,
+   const char *show_doc, const char *help_doc,
+   setting_setter_ftype<bool> set_func,
+   setting_getter_ftype<bool> get_func, show_value_ftype *show_func,
+   cmd_list_element **set_list, cmd_list_element **show_list);
+
 extern set_show_commands add_setshow_filename_cmd
   (const char *name, command_class theclass, std::string *var, const char *set_doc,
    const char *show_doc, const char *help_doc, cmd_func_ftype *set_func,
    show_value_ftype *show_func, cmd_list_element **set_list,
    cmd_list_element **show_list);
 
+extern set_show_commands add_setshow_filename_cmd
+  (const char *name, command_class theclass, const char *set_doc,
+   const char *show_doc, const char *help_doc,
+   setting_setter_ftype<std::string> set_func,
+   setting_getter_ftype<std::string> get_func, show_value_ftype *show_func,
+   cmd_list_element **set_list, cmd_list_element **show_list);
+
 extern set_show_commands add_setshow_string_cmd
   (const char *name, command_class theclass, std::string *var, const char *set_doc,
    const char *show_doc, const char *help_doc, cmd_func_ftype *set_func,
    show_value_ftype *show_func, cmd_list_element **set_list,
    cmd_list_element **show_list);
 
+extern set_show_commands add_setshow_string_cmd
+  (const char *name, command_class theclass, const char *set_doc,
+   const char *show_doc, const char *help_doc,
+   setting_setter_ftype<std::string> set_func,
+   setting_getter_ftype<std::string> get_func,
+   show_value_ftype *show_func, cmd_list_element **set_list,
+   cmd_list_element **show_list);
+
 extern set_show_commands add_setshow_string_noescape_cmd
   (const char *name, command_class theclass, std::string *var, const char *set_doc,
    const char *show_doc, const char *help_doc, cmd_func_ftype *set_func,
    show_value_ftype *show_func, cmd_list_element **set_list,
    cmd_list_element **show_list);
 
+extern set_show_commands add_setshow_string_noescape_cmd
+  (const char *name, command_class theclass, const char *set_doc,
+   const char *show_doc, const char *help_doc,
+   setting_setter_ftype<std::string> set_func,
+   setting_getter_ftype<std::string> get_func, show_value_ftype *show_func,
+   cmd_list_element **set_list, cmd_list_element **show_list);
+
 extern set_show_commands add_setshow_optional_filename_cmd
   (const char *name, command_class theclass, std::string *var, const char *set_doc,
    const char *show_doc, const char *help_doc, cmd_func_ftype *set_func,
    show_value_ftype *show_func, cmd_list_element **set_list,
    cmd_list_element **show_list);
 
+extern set_show_commands add_setshow_optional_filename_cmd
+  (const char *name, command_class theclass, const char *set_doc,
+   const char *show_doc, const char *help_doc,
+   setting_setter_ftype<std::string> set_func,
+   setting_getter_ftype<std::string> get_func,
+   show_value_ftype *show_func, cmd_list_element **set_list,
+   cmd_list_element **show_list);
+
 extern set_show_commands add_setshow_integer_cmd
   (const char *name, command_class theclass, int *var, const char *set_doc,
    const char *show_doc, const char *help_doc, cmd_func_ftype *set_func,
    show_value_ftype *show_func, cmd_list_element **set_list,
    cmd_list_element **show_list);
 
+extern set_show_commands add_setshow_integer_cmd
+  (const char *name, command_class theclass, const char *set_doc,
+   const char *show_doc, const char *help_doc,
+   setting_setter_ftype<int> set_func,
+   setting_getter_ftype<int> get_func, show_value_ftype *show_func,
+   cmd_list_element **set_list, cmd_list_element **show_list);
+
 extern set_show_commands add_setshow_uinteger_cmd
   (const char *name, command_class theclass, unsigned int *var,
    const char *set_doc, const char *show_doc, const char *help_doc,
    cmd_func_ftype *set_func, show_value_ftype *show_func,
    cmd_list_element **set_list, cmd_list_element **show_list);
 
+extern set_show_commands add_setshow_uinteger_cmd
+  (const char *name, command_class theclass, const char *set_doc,
+   const char *show_doc, const char *help_doc,
+   setting_setter_ftype<unsigned int> set_func,
+   setting_getter_ftype<unsigned int> get_func, show_value_ftype *show_func,
+   cmd_list_element **set_list, cmd_list_element **show_list);
+
 extern set_show_commands add_setshow_zinteger_cmd
   (const char *name, command_class theclass, int *var, const char *set_doc,
    const char *show_doc, const char *help_doc, cmd_func_ftype *set_func,
    show_value_ftype *show_func, cmd_list_element **set_list,
    cmd_list_element **show_list);
 
+extern set_show_commands add_setshow_zinteger_cmd
+  (const char *name, command_class theclass, const char *set_doc,
+   const char *show_doc, const char *help_doc,
+   setting_setter_ftype<int> set_func,
+   setting_getter_ftype<int> get_func, show_value_ftype *show_func,
+   cmd_list_element **set_list, cmd_list_element **show_list);
+
 extern set_show_commands add_setshow_zuinteger_cmd
   (const char *name, command_class theclass, unsigned int *var,
    const char *set_doc, const char *show_doc, const char *help_doc,
    cmd_func_ftype *set_func, show_value_ftype *show_func,
    cmd_list_element **set_list, cmd_list_element **show_list);
 
+extern set_show_commands add_setshow_zuinteger_cmd
+  (const char *name, command_class theclass, const char *set_doc,
+   const char *show_doc, const char *help_doc,
+   setting_setter_ftype<unsigned int> set_func,
+   setting_getter_ftype<unsigned int> get_func, show_value_ftype *show_func,
+   cmd_list_element **set_list, cmd_list_element **show_list);
+
 extern set_show_commands add_setshow_zuinteger_unlimited_cmd
   (const char *name, command_class theclass, int *var, const char *set_doc,
    const char *show_doc, const char *help_doc, cmd_func_ftype *set_func,
    show_value_ftype *show_func, cmd_list_element **set_list,
    cmd_list_element **show_list);
 
+extern set_show_commands add_setshow_zuinteger_unlimited_cmd
+  (const char *name, command_class theclass, const char *set_doc,
+   const char *show_doc, const char *help_doc,
+   setting_setter_ftype<int> set_func, setting_getter_ftype<int> get_func,
+   show_value_ftype *show_func, cmd_list_element **set_list,
+   cmd_list_element **show_list);
+
 /* Do a "show" command for each thing on a command list.  */
 
 extern void cmd_show_list (struct cmd_list_element *, int);