public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v4 0/4] Refactor cmd_list_element.var
@ 2021-09-29 21:50 Lancelot SIX
  2021-09-29 21:50 ` [PATCH v4 1/4] gdb: Introduce setting construct within cmd_list_element Lancelot SIX
                   ` (4 more replies)
  0 siblings, 5 replies; 18+ messages in thread
From: Lancelot SIX @ 2021-09-29 21:50 UTC (permalink / raw)
  To: gdb-patches; +Cc: Lancelot SIX, Pedro Alves

Hi,

This is a V4 following
https://sourceware.org/pipermail/gdb-patches/2021-September/182006.html

This version addresses the comments from Pedro.  It has also been rebased
on top of current trunk.

The entire series have been tested on x86_64-linux-gnu without regression
observed.

All feedbacks welcome.

Lancelot.

---

Noticable changes since V3:

- Patch 1:
	- setting::get now returns by const reference instead of value.
  - setting::set has a parameter passed by const ref instead of value.
	- Since the setting value is returned by ref, remove the setting::get_p
    function which returned the value by address.  The (unique) caller of
    this function can get the address from the reference instead.
  - A setting can be costructed from a pre type-erased and pre validated
		setting::erased_arg value.  This is mainly used to avoid turning the
    best part of add_setshow_cmd_full into a templated function.
  - Use '= delete' to explicitly disable var_type_uses<T> for any T which
    is not used to store a setting's value.  This improves the compiler
    error message when such overload is used accidently.

- Patch 2:
	- Use the fact that setting::get returns by reference and avoids making
    few un necessary copies of strings.

- Patch 3:
	- Follow the changes introduced in patch 1 and allow construction of a
    setting::erased_arg usign callback functions.

- Patch 4: Unchanged.

Noticeable changes since V2:

- Patch 1:
  - cmd_list_element.var is now a gdb::optional<setting>, and the
    setting ctor ensures a setting cannot exist if it does not contain a
    value.
  - Instead of storing a setting with param_smob (scheme) or
    parmpy_object (python), keep the var_types and value as they are and
    create a setting wrapper around them only when required.

- Patch 2 is unchanged.

- Patch 3:
  - Store callback function pointers with a type erased 'void (*) ()'
    instead of using a union.  This requires significantly less code.
  - Add a STORAGE property that can tell the caller if the setting
    accesses its value via pointer or using the callback methods.
    This is used in show_remote_protocol_packet_cmd in order to ensure
    that accessing the setting by pointer is valid.

- Patch 4 kept unchanged except for conflict resolutions after rebasing
  on top of current trunk.

Noticeable changes since V1:

- Based on the feedbacks form Tom Tromey, the API have been simplified and
  the amount of templated code have been reduced.  Instead of using
  something like

    setting.get<var_integer, var_zinetger, var_zuinteger_unlimited> ()

  in order to access the value of a setting backed by an int (which must
  be either a var_integer, a var_zinetger or a var_zuinteger_unlimited),
  we now write

    setting.get<int> ()

  The 'get' function still includes an assert that setting.var_type is one
	of the var_types that is backed by an int.  In this version, the call
  site needs to know the mapping between var_types and underlying storage
  type.
- Based on a comment from Simon Marchi, the name of the type that ties
  together the var_type and the void * pointer has changed from 'param' to
	'setting'.
- Minor formatting issues have been addressed.

---

This patch series proposes to refactor the way cmd_list_element.var is
handled.  It is meant to be a cleanup series and is intended to offer a
basis on which to solve PR/28085.

Some commands contain a reference to a variable (setting) that can be set
or shown.  To do that, the command element contains pieces of information:
a void* pointer to global variable, and a var_types field to indicate the
type of the setting. The var_type dictates what the data type of  variable
pointed by the void* pointer must be.

When any piece of code within GDB tries to interact with such variable, it
first checks the type field and then casts the void* pointer into whatever
type pointer it needs to and dereferences it.  There are few limitations
with this approach I try to address in this patch series:

- Any code that interact with a cmd_list_element.var is responsible to
  do some cast to the appropriate type.  This is done in multiple places
  in the codebase and is quite easy to get wrong.  The first patch tries
  to cover that by introducing a safer abstraction around the void *
  pointer.  The accessors that hide the void * pointer adds runtime
  checks that the pointer is casted to the appropriate type that matches
  the var_type of the setting instance.
- The source of truth for any setting needs to be the variable pointed to.
  However, as pointed out by Simon in PR/28085 this is not always true, and
	there are bugs linked to this.  In order to solve this problem, patch 3
	allows getter and setter callbacks to be embedded in the abstraction
	introduced in patch 1.  When the setting is accessed, the callback is
  called to compute or update the value based on the current state of GDB.

Patch 2, which was originally proposed by Simon Marchi[1] proposes to
change the variable type from char* to std::string for string like
setting.  Finally Patch 4 does some more refactoring on how we detect
and notify observers when a given setting is updated.

None of those patches should introduce user visible changes.

The entire series have been tested on x86_64 and no regression have been
observed on the testsuite.

All feedbacks are welcome.


Lancelot SIX (3):
  gdb: Introduce setting construct within cmd_list_element
  gdb: Have setter and getter callbacks for settings
  gdb: Setting setter return a bool to tell if the value changed

Simon Marchi (1):
  gdb: make string-like set show commands use std::string variable

 gdb/auto-load.c              |  50 ++--
 gdb/breakpoint.c             |  22 +-
 gdb/build-id.c               |   4 +-
 gdb/cli/cli-cmds.c           | 148 ++++++-----
 gdb/cli/cli-decode.c         | 459 +++++++++++++++++++++++++++++------
 gdb/cli/cli-decode.h         |   9 +-
 gdb/cli/cli-logging.c        |  23 +-
 gdb/cli/cli-option.c         |   9 +-
 gdb/cli/cli-option.h         |   4 +-
 gdb/cli/cli-setshow.c        | 188 ++++++--------
 gdb/cli/cli-setshow.h        |   4 +-
 gdb/command.h                | 335 ++++++++++++++++++++++++-
 gdb/compile/compile.c        |  46 ++--
 gdb/corefile.c               |  17 +-
 gdb/defs.h                   |   4 +-
 gdb/disasm.c                 |  11 +-
 gdb/disasm.h                 |   2 +-
 gdb/dwarf2/dwz.c             |   2 +-
 gdb/dwarf2/index-cache.c     |  10 +-
 gdb/dwarf2/read.c            |  10 +-
 gdb/event-top.c              |  12 +-
 gdb/fork-child.c             |   7 +-
 gdb/guile/scm-param.c        | 175 +++++++------
 gdb/infcmd.c                 |  14 +-
 gdb/linux-thread-db.c        |  17 +-
 gdb/main.c                   |  17 +-
 gdb/maint-test-options.c     |  11 +-
 gdb/maint-test-settings.c    |   8 +-
 gdb/maint.c                  |   2 +-
 gdb/mi/mi-cmd-env.c          |  18 +-
 gdb/proc-api.c               |   5 +-
 gdb/python/py-param.c        |  65 +++--
 gdb/python/python-internal.h |   2 +-
 gdb/python/python.c          |  29 ++-
 gdb/remote-sim.c             |   7 +-
 gdb/remote.c                 |  13 +-
 gdb/serial.c                 |   8 +-
 gdb/solib.c                  |  20 +-
 gdb/source.c                 |  66 ++---
 gdb/source.h                 |   5 +-
 gdb/stack.c                  |  22 +-
 gdb/symfile.c                |  49 ++--
 gdb/symtab.c                 |  46 ++--
 gdb/target-descriptions.c    |   2 +-
 gdb/top.c                    | 112 ++++-----
 gdb/top.h                    |   2 +-
 gdb/tracepoint.c             |  29 +--
 gdb/tracepoint.h             |   2 +-
 48 files changed, 1395 insertions(+), 727 deletions(-)

-- 
2.33.0


^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH v4 1/4] gdb: Introduce setting construct within cmd_list_element
  2021-09-29 21:50 [PATCH v4 0/4] Refactor cmd_list_element.var Lancelot SIX
@ 2021-09-29 21:50 ` Lancelot SIX
  2021-09-30 12:12   ` Simon Marchi
  2021-09-30 12:39   ` Simon Marchi
  2021-09-29 21:50 ` [PATCH v4 2/4] gdb: make string-like set show commands use std::string variable Lancelot SIX
                   ` (3 subsequent siblings)
  4 siblings, 2 replies; 18+ messages in thread
From: Lancelot SIX @ 2021-09-29 21:50 UTC (permalink / raw)
  To: gdb-patches; +Cc: Lancelot SIX, Simon Marchi

cmd_list_element can contain a pointer to data that can be set and / or
shown.  This is achieved with the void* VAR member which points to the
data that can be accessed, while the VAR_TYPE member (of type enum
var_types) indicates how to interpret the data pointed to.

With this pattern, the user of the cmd_list_element needs to know what
is the storage type associated with a given VAR_TYPES in order to do
the proper casting.  No automatic safeguard is available to prevent
miss-use of the pointer.  Client code typically looks something like:

	switch (c->var_type)
	{
	  case var_zuinteger:
	    unsigned int v = *(unsigned int*) c->var;
	    ...
	    break;
	  case var_boolean:
	    bool v = *(bool *) c->var;
	    ...
	    break;
	  ...
	}

This patch proposes to add an abstraction around the var_types and void*
pointer pair.  The abstraction is meant to prevent the user from having
to handle the cast and verify that the data is read or written as a type
that is coherent with the setting's var_type.  This is achieved by
introducing the struct setting which exposes a set of templated get /
set member functions.  The template parameter is the type of the
variable that holds the referred variable.

Using those accessors allows runtime checks to be inserted in order to
ensure that the data pointed to has the expected type.  For example,
instantiating the member functions with bool will yield something
similar to:

	const bool &get<bool> () const
	{
	  gdb_assert (m_var_type == var_boolean);
	  gdb_assert (m_var != nullptr);
	  return *static_cast<bool *> (m_var);
	}
	void set<bool> (const bool &var)
	{
	  gdb_assert (m_var_type == var_boolean);
	  gdb_assert (m_var != nullptr);
	  *static_cast<bool *> (m_var) = var;
	}

Using the new abstraction, our initial example becomes:

	switch (c->var_type)
	{
	  case var_zuinteger:
	    unsigned int v = c->var->get<unsigned int> ();
	    ...
	    break;
	  case var_boolean:
	    bool v = c->var->get<bool> ();
	    ...
	    break;
	  ...
	}

While the call site is still similar, the introduction of runtime checks
help ensure correct usage of the data.

In order to avoid turning the bulk of add_setshow_cmd_full into a
templated function, and following a suggestion from Pedro Alves, a
setting can be constructed from a pre validated type erased reference to
a variable.  This is what setting::erased_args is used for.

Introducing an opaque abstraction to describe a setting will also make
it possible to use callbacks to retrieve or set the value of the setting
on the fly instead of pointing to a static chunk of memory.  This will
be done added in a later commit.

Given that a cmd_list_element may or may not reference a setting, the
VAR and VAR_TYPES members of the struct are replaced with a
gdb::optional<setting> named VAR.

Few internal function signatures have been modified to take into account
this new abstraction:

-The functions value_from_setting, str_value_from_setting and
 get_setshow_command_value_string used to have a 'cmd_list_element *'
 parameter but only used it for the VAR and VAR_TYPE member. They now
 take a 'setting const &' parameter instead.
- Similarly, the 'void *' and a 'enum var_types' parameters of
  pascm_param_value and gdbpy_parameter_value have been replaced with a
  'setting const &' parameter.

No user visible change is expected after this patch.

Tested on GNU/Linux x86_64, with no regression noticed.

Co-authored-by: Simon Marchi <simon.marchi@polymtl.ca>
Change-Id: Ie1d08c3ceb8b30b3d7bf1efe036eb8acffcd2f34
---
 gdb/auto-load.c              |   2 +-
 gdb/cli/cli-cmds.c           | 115 +++++++++++++++---------
 gdb/cli/cli-decode.c         | 150 ++++++++++++++++++-------------
 gdb/cli/cli-decode.h         |   9 +-
 gdb/cli/cli-setshow.c        | 168 ++++++++++++++++++++++-------------
 gdb/cli/cli-setshow.h        |   4 +-
 gdb/command.h                | 145 ++++++++++++++++++++++++++++++
 gdb/guile/scm-param.c        | 141 +++++++++++++++++------------
 gdb/maint.c                  |   2 +-
 gdb/python/py-param.c        |  26 +++++-
 gdb/python/python-internal.h |   2 +-
 gdb/python/python.c          |  29 +++---
 gdb/remote.c                 |   3 +-
 13 files changed, 545 insertions(+), 251 deletions(-)

diff --git a/gdb/auto-load.c b/gdb/auto-load.c
index 9cd70f174c3..7f0bb74c32a 100644
--- a/gdb/auto-load.c
+++ b/gdb/auto-load.c
@@ -1408,7 +1408,7 @@ set_auto_load_cmd (const char *args, int from_tty)
 	     "otherwise check the auto-load sub-commands."));
 
   for (list = *auto_load_set_cmdlist_get (); list != NULL; list = list->next)
-    if (list->var_type == var_boolean)
+    if (list->var->type () == var_boolean)
       {
 	gdb_assert (list->type == set_cmd);
 	do_set_command (args, from_tty, list);
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index e5c7152ba7a..ecbe5a4b43c 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -236,7 +236,7 @@ with_command_1 (const char *set_cmd_prefix,
 					  /*ignore_help_classes=*/ 1);
   gdb_assert (set_cmd != nullptr);
 
-  if (set_cmd->var == nullptr)
+  if (!set_cmd->var.has_value ())
     error (_("Cannot use this setting with the \"with\" command"));
 
   std::string temp_value
@@ -245,7 +245,8 @@ with_command_1 (const char *set_cmd_prefix,
   if (nested_cmd == nullptr)
     nested_cmd = skip_spaces (delim + 2);
 
-  std::string org_value = get_setshow_command_value_string (set_cmd);
+  gdb_assert (set_cmd->var.has_value ());
+  std::string org_value = get_setshow_command_value_string (*set_cmd->var);
 
   /* Tweak the setting to the new temporary value.  */
   do_set_command (temp_value.c_str (), from_tty, set_cmd);
@@ -2092,31 +2093,31 @@ setting_cmd (const char *fnname, struct cmd_list_element *showlist,
 /* Builds a value from the show CMD.  */
 
 static struct value *
-value_from_setting (const cmd_list_element *cmd, struct gdbarch *gdbarch)
+value_from_setting (const setting &var, struct gdbarch *gdbarch)
 {
-  switch (cmd->var_type)
+  switch (var.type ())
     {
     case var_integer:
-      if (*(int *) cmd->var == INT_MAX)
+      if (var.get<int> () == INT_MAX)
 	return value_from_longest (builtin_type (gdbarch)->builtin_int,
 				   0);
       else
 	return value_from_longest (builtin_type (gdbarch)->builtin_int,
-				   *(int *) cmd->var);
+				   var.get<int> ());
     case var_zinteger:
       return value_from_longest (builtin_type (gdbarch)->builtin_int,
-				 *(int *) cmd->var);
+				 var.get<int> ());
     case var_boolean:
       return value_from_longest (builtin_type (gdbarch)->builtin_int,
-				 *(bool *) cmd->var ? 1 : 0);
+				 var.get<bool> () ? 1 : 0);
     case var_zuinteger_unlimited:
       return value_from_longest (builtin_type (gdbarch)->builtin_int,
-				 *(int *) cmd->var);
+				 var.get<int> ());
     case var_auto_boolean:
       {
 	int val;
 
-	switch (*(enum auto_boolean*) cmd->var)
+	switch (var.get<enum auto_boolean> ())
 	  {
 	  case AUTO_BOOLEAN_TRUE:
 	    val = 1;
@@ -2134,27 +2135,35 @@ value_from_setting (const cmd_list_element *cmd, struct gdbarch *gdbarch)
 				   val);
       }
     case var_uinteger:
-      if (*(unsigned int *) cmd->var == UINT_MAX)
+      if (var.get<unsigned int> () == UINT_MAX)
 	return value_from_ulongest
 	  (builtin_type (gdbarch)->builtin_unsigned_int, 0);
       else
 	return value_from_ulongest
 	  (builtin_type (gdbarch)->builtin_unsigned_int,
-	   *(unsigned int *) cmd->var);
+	   var.get<unsigned int> ());
     case var_zuinteger:
       return value_from_ulongest (builtin_type (gdbarch)->builtin_unsigned_int,
-				  *(unsigned int *) cmd->var);
+				  var.get<unsigned int> ());
     case var_string:
     case var_string_noescape:
     case var_optional_filename:
     case var_filename:
     case var_enum:
-      if (*(char **) cmd->var)
-	return value_cstring (*(char **) cmd->var, strlen (*(char **) cmd->var),
-			      builtin_type (gdbarch)->builtin_char);
-      else
-	return value_cstring ("", 1,
-			      builtin_type (gdbarch)->builtin_char);
+      {
+	const char *value;
+	if (var.type () == var_enum)
+	  value = var.get<const char *> ();
+	else
+	  value = var.get<char *> ();
+
+	if (value != nullptr)
+	  return value_cstring (value, strlen (value),
+				builtin_type (gdbarch)->builtin_char);
+	else
+	  return value_cstring ("", 1,
+				builtin_type (gdbarch)->builtin_char);
+      }
     default:
       gdb_assert_not_reached ("bad var_type");
     }
@@ -2167,9 +2176,12 @@ gdb_setting_internal_fn (struct gdbarch *gdbarch,
 			 const struct language_defn *language,
 			 void *cookie, int argc, struct value **argv)
 {
-  return value_from_setting (setting_cmd ("$_gdb_setting", showlist,
-					  argc, argv),
-			     gdbarch);
+  cmd_list_element *show_cmd
+    = setting_cmd ("$_gdb_setting", showlist, argc, argv);
+
+  gdb_assert (show_cmd->var.has_value ());
+
+  return value_from_setting (*show_cmd->var, gdbarch);
 }
 
 /* Implementation of the convenience function $_gdb_maint_setting.  */
@@ -2179,18 +2191,20 @@ gdb_maint_setting_internal_fn (struct gdbarch *gdbarch,
 			       const struct language_defn *language,
 			       void *cookie, int argc, struct value **argv)
 {
-  return value_from_setting (setting_cmd ("$_gdb_maint_setting",
-					  maintenance_show_cmdlist,
-					  argc, argv),
-			     gdbarch);
+  cmd_list_element *show_cmd
+    = setting_cmd ("$_gdb_maint_setting", maintenance_show_cmdlist, argc, argv);
+
+  gdb_assert (show_cmd->var.has_value ());
+
+  return value_from_setting (*show_cmd->var, gdbarch);
 }
 
 /* Builds a string value from the show CMD.  */
 
 static struct value *
-str_value_from_setting (const cmd_list_element *cmd, struct gdbarch *gdbarch)
+str_value_from_setting (const setting &var, struct gdbarch *gdbarch)
 {
-  switch (cmd->var_type)
+  switch (var.type ())
     {
     case var_integer:
     case var_zinteger:
@@ -2200,7 +2214,7 @@ str_value_from_setting (const cmd_list_element *cmd, struct gdbarch *gdbarch)
     case var_uinteger:
     case var_zuinteger:
       {
-	std::string cmd_val = get_setshow_command_value_string (cmd);
+	std::string cmd_val = get_setshow_command_value_string (var);
 
 	return value_cstring (cmd_val.c_str (), cmd_val.size (),
 			      builtin_type (gdbarch)->builtin_char);
@@ -2213,15 +2227,22 @@ str_value_from_setting (const cmd_list_element *cmd, struct gdbarch *gdbarch)
     case var_enum:
       /* For these cases, we do not use get_setshow_command_value_string,
 	 as this function handle some characters specially, e.g. by
-	 escaping quotes.  So, we directly use the cmd->var string value,
-	 similarly to the value_from_setting code for these cases.  */
-      if (*(char **) cmd->var)
-	return value_cstring (*(char **) cmd->var, strlen (*(char **) cmd->var),
-			      builtin_type (gdbarch)->builtin_char);
-      else
-	return value_cstring ("", 1,
-			      builtin_type (gdbarch)->builtin_char);
+	 escaping quotevar.  So, we directly use the var string value,
+	 similarly to the value_from_setting code for these casevar.  */
+      {
+	const char *value;
+	if (var.type () == var_enum)
+	  value = var.get<const char *> ();
+	else
+	  value = var.get<char *> ();
 
+	if (value != nullptr)
+	  return value_cstring (value, strlen (value),
+				builtin_type (gdbarch)->builtin_char);
+	else
+	  return value_cstring ("", 1,
+				builtin_type (gdbarch)->builtin_char);
+      }
     default:
       gdb_assert_not_reached ("bad var_type");
     }
@@ -2234,9 +2255,12 @@ gdb_setting_str_internal_fn (struct gdbarch *gdbarch,
 			     const struct language_defn *language,
 			     void *cookie, int argc, struct value **argv)
 {
-  return str_value_from_setting (setting_cmd ("$_gdb_setting_str",
-					      showlist, argc, argv),
-				 gdbarch);
+  cmd_list_element *show_cmd
+    = setting_cmd ("$_gdb_setting_str", showlist, argc, argv);
+
+  gdb_assert (show_cmd->var.has_value ());
+
+  return str_value_from_setting (*show_cmd->var, gdbarch);
 }
 
 
@@ -2247,10 +2271,13 @@ gdb_maint_setting_str_internal_fn (struct gdbarch *gdbarch,
 				   const struct language_defn *language,
 				   void *cookie, int argc, struct value **argv)
 {
-  return str_value_from_setting (setting_cmd ("$_gdb_maint_setting_str",
-					      maintenance_show_cmdlist,
-					      argc, argv),
-				 gdbarch);
+  cmd_list_element *show_cmd
+    = setting_cmd ("$_gdb_maint_setting_str", maintenance_show_cmdlist, argc,
+		   argv);
+
+  gdb_assert (show_cmd->var.has_value ());
+
+  return str_value_from_setting (*show_cmd->var, gdbarch);
 }
 
 void _initialize_cli_cmds ();
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index 06f3de0f038..56befc979dc 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -467,7 +467,7 @@ add_set_or_show_cmd (const char *name,
 		     enum cmd_types type,
 		     enum command_class theclass,
 		     var_types var_type,
-		     void *var,
+		     const setting::erased_args &arg,
 		     const char *doc,
 		     struct cmd_list_element **list)
 {
@@ -475,8 +475,8 @@ add_set_or_show_cmd (const char *name,
 
   gdb_assert (type == set_cmd || type == show_cmd);
   c->type = type;
-  c->var_type = var_type;
-  c->var = var;
+  c->var.emplace (var_type, arg);
+
   /* This needs to be something besides NULL so that this isn't
      treated as a help class.  */
   c->func = empty_func;
@@ -493,15 +493,16 @@ add_set_or_show_cmd (const char *name,
    Return the newly created set and show commands.  */
 
 static set_show_commands
-add_setshow_cmd_full (const char *name,
-		      enum command_class theclass,
-		      var_types var_type, void *var,
-		      const char *set_doc, const char *show_doc,
-		      const char *help_doc,
-		      cmd_func_ftype *set_func,
-		      show_value_ftype *show_func,
-		      struct cmd_list_element **set_list,
-		      struct cmd_list_element **show_list)
+add_setshow_cmd_full_erased (const char *name,
+			     enum command_class theclass,
+			     var_types var_type,
+			     const setting::erased_args &args,
+			     const char *set_doc, const char *show_doc,
+			     const char *help_doc,
+			     cmd_func_ftype *set_func,
+			     show_value_ftype *show_func,
+			     struct cmd_list_element **set_list,
+			     struct cmd_list_element **show_list)
 {
   struct cmd_list_element *set;
   struct cmd_list_element *show;
@@ -518,14 +519,14 @@ add_setshow_cmd_full (const char *name,
       full_set_doc = xstrdup (set_doc);
       full_show_doc = xstrdup (show_doc);
     }
-  set = add_set_or_show_cmd (name, set_cmd, theclass, var_type, var,
+  set = add_set_or_show_cmd (name, set_cmd, theclass, var_type, args,
 			     full_set_doc, set_list);
   set->doc_allocated = 1;
 
   if (set_func != NULL)
     set->func = set_func;
 
-  show = add_set_or_show_cmd (name, show_cmd, theclass, var_type, var,
+  show = add_set_or_show_cmd (name, show_cmd, theclass, var_type, args,
 			      full_show_doc, show_list);
   show->doc_allocated = 1;
   show->show_value_func = show_func;
@@ -536,6 +537,32 @@ add_setshow_cmd_full (const char *name,
   return {set, show};
 }
 
+template<typename T>
+static set_show_commands
+add_setshow_cmd_full (const char *name,
+		      enum command_class theclass,
+		      var_types var_type, T *var,
+		      const char *set_doc, const char *show_doc,
+		      const char *help_doc,
+		      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);
+
+  return add_setshow_cmd_full_erased (name,
+				      theclass,
+				      var_type, erased_args,
+				      set_doc, show_doc,
+				      help_doc,
+				      set_func,
+				      show_func,
+				      set_list,
+				      show_list);
+}
+
 /* Add element named NAME to command list LIST (the list for set or
    some sublist thereof).  CLASS is as in add_cmd.  ENUMLIST is a list
    of strings which may follow NAME.  VAR is address of the variable
@@ -555,10 +582,10 @@ add_setshow_enum_cmd (const char *name,
 		      struct cmd_list_element **show_list)
 {
   set_show_commands commands
-    =  add_setshow_cmd_full (name, theclass, var_enum, var,
-			     set_doc, show_doc, help_doc,
-			     set_func, show_func,
-			     set_list, show_list);
+    =  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);
   commands.set->enums = enumlist;
   return commands;
 }
@@ -583,10 +610,11 @@ add_setshow_auto_boolean_cmd (const char *name,
 			      struct cmd_list_element **show_list)
 {
   set_show_commands commands
-    = add_setshow_cmd_full (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,
+					       set_func, show_func,
+					       set_list, show_list);
 
   commands.set->enums = auto_boolean_enums;
 
@@ -612,10 +640,10 @@ add_setshow_boolean_cmd (const char *name, enum command_class theclass, bool *va
 			 struct cmd_list_element **show_list)
 {
   set_show_commands commands
-    = add_setshow_cmd_full (name, theclass, var_boolean, var,
-			    set_doc, show_doc, help_doc,
-			    set_func, show_func,
-			    set_list, show_list);
+    = add_setshow_cmd_full<bool> (name, theclass, var_boolean, var,
+				  set_doc, show_doc, help_doc,
+				  set_func, show_func,
+				  set_list, show_list);
 
   commands.set->enums = boolean_enums;
 
@@ -636,10 +664,10 @@ add_setshow_filename_cmd (const char *name, enum command_class theclass,
 			  struct cmd_list_element **show_list)
 {
   set_show_commands commands
-    = add_setshow_cmd_full (name, theclass, var_filename, var,
-			    set_doc, show_doc, help_doc,
-			    set_func, show_func,
-			    set_list, show_list);
+    = add_setshow_cmd_full<char *> (name, theclass, var_filename, var,
+				    set_doc, show_doc, help_doc,
+				    set_func, show_func,
+				    set_list, show_list);
 
   set_cmd_completer (commands.set, filename_completer);
 
@@ -660,10 +688,10 @@ add_setshow_string_cmd (const char *name, enum command_class theclass,
 			struct cmd_list_element **show_list)
 {
   set_show_commands commands
-    = add_setshow_cmd_full (name, theclass, var_string, var,
-			    set_doc, show_doc, help_doc,
-			    set_func, show_func,
-			    set_list, show_list);
+    = add_setshow_cmd_full<char *> (name, theclass, var_string, var,
+				    set_doc, show_doc, help_doc,
+				    set_func, show_func,
+				    set_list, show_list);
 
   /* Disable the default symbol completer.  */
   set_cmd_completer (commands.set, nullptr);
@@ -685,10 +713,9 @@ add_setshow_string_noescape_cmd (const char *name, enum command_class theclass,
 				 struct cmd_list_element **show_list)
 {
   set_show_commands commands
-    = add_setshow_cmd_full (name, theclass, var_string_noescape, var,
-			    set_doc, show_doc, help_doc,
-			    set_func, show_func,
-			    set_list, show_list);
+    = add_setshow_cmd_full<char *> (name, theclass, var_string_noescape, var,
+				    set_doc, show_doc, help_doc, set_func,
+				    show_func, set_list, show_list);
 
   /* Disable the default symbol completer.  */
   set_cmd_completer (commands.set, nullptr);
@@ -710,11 +737,10 @@ add_setshow_optional_filename_cmd (const char *name, enum command_class theclass
 				   struct cmd_list_element **show_list)
 {
   set_show_commands commands
-    = add_setshow_cmd_full (name, theclass, var_optional_filename, var,
-			    set_doc, show_doc, help_doc,
-			    set_func, show_func,
-			    set_list, show_list);
-		
+    = add_setshow_cmd_full<char *> (name, theclass, var_optional_filename,
+				    var, set_doc, show_doc, help_doc,
+				    set_func, show_func, set_list, show_list);
+
   set_cmd_completer (commands.set, filename_completer);
 
   return commands;
@@ -754,10 +780,9 @@ 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 (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, set_func, show_func,
+				 set_list, show_list);
 
   set_cmd_completer (commands.set, integer_unlimited_completer);
 
@@ -780,10 +805,10 @@ add_setshow_uinteger_cmd (const char *name, enum command_class theclass,
 			  struct cmd_list_element **show_list)
 {
   set_show_commands commands
-    = add_setshow_cmd_full (name, theclass, var_uinteger, var,
-			    set_doc, show_doc, help_doc,
-			    set_func, show_func,
-			    set_list, show_list);
+    = 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);
 
   set_cmd_completer (commands.set, integer_unlimited_completer);
 
@@ -805,10 +830,10 @@ add_setshow_zinteger_cmd (const char *name, enum command_class theclass,
 			  struct cmd_list_element **set_list,
 			  struct cmd_list_element **show_list)
 {
-  return add_setshow_cmd_full (name, theclass, var_zinteger, var,
-			       set_doc, show_doc, help_doc,
-			       set_func, show_func,
-			       set_list, show_list);
+  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);
 }
 
 set_show_commands
@@ -824,10 +849,9 @@ add_setshow_zuinteger_unlimited_cmd (const char *name,
 				     struct cmd_list_element **show_list)
 {
   set_show_commands commands
-    = add_setshow_cmd_full (name, theclass, var_zuinteger_unlimited, var,
-			    set_doc, show_doc, help_doc,
-			    set_func, show_func,
-			    set_list, show_list);
+    = 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_cmd_completer (commands.set, integer_unlimited_completer);
 
@@ -849,10 +873,10 @@ add_setshow_zuinteger_cmd (const char *name, enum command_class theclass,
 			   struct cmd_list_element **set_list,
 			   struct cmd_list_element **show_list)
 {
-  return add_setshow_cmd_full (name, theclass, var_zuinteger, var,
-			       set_doc, show_doc, help_doc,
-			       set_func, show_func,
-			       set_list, show_list);
+  return add_setshow_cmd_full<unsigned int> (name, theclass, var_zuinteger,
+					     var, set_doc, show_doc, help_doc,
+					     set_func, show_func, set_list,
+					     show_list);
 }
 
 /* Remove the command named NAME from the command list.  Return the
diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h
index 651d1ef8abb..f7945ba2bf5 100644
--- a/gdb/cli/cli-decode.h
+++ b/gdb/cli/cli-decode.h
@@ -55,7 +55,6 @@ struct cmd_list_element
       allow_unknown (0),
       abbrev_flag (0),
       type (not_set_cmd),
-      var_type (var_boolean),
       doc (doc_)
   {
     memset (&function, 0, sizeof (function));
@@ -160,9 +159,6 @@ struct cmd_list_element
      or "show").  */
   ENUM_BITFIELD (cmd_types) type : 2;
 
-  /* What kind of variable is *VAR?  */
-  ENUM_BITFIELD (var_types) var_type : 4;
-
   /* Function definition of this command.  NULL for command class
      names and for help topics that are not really commands.  NOTE:
      cagney/2002-02-02: This function signature is evolving.  For
@@ -228,9 +224,8 @@ struct cmd_list_element
      used to finalize the CONTEXT field, if needed.  */
   void (*destroyer) (struct cmd_list_element *self, void *context) = nullptr;
 
-  /* Pointer to variable affected by "set" and "show".  Doesn't
-     matter if type is not_set.  */
-  void *var = nullptr;
+  /* Setting affected by "set" and "show".  Not used if type is not_set_cmd.  */
+  gdb::optional<setting> var;
 
   /* Pointer to NULL terminated list of enumerated values (like
      argv).  */
diff --git a/gdb/cli/cli-setshow.c b/gdb/cli/cli-setshow.c
index 1025ed2f620..a247f5eb58d 100644
--- a/gdb/cli/cli-setshow.c
+++ b/gdb/cli/cli-setshow.c
@@ -130,10 +130,14 @@ deprecated_show_value_hack (struct ui_file *ignore_file,
   /* If there's no command or value, don't try to print it out.  */
   if (c == NULL || value == NULL)
     return;
+
   /* Print doc minus "Show " at start.  Tell print_doc_line that
      this is for a 'show value' prefix.  */
   print_doc_line (gdb_stdout, c->doc + 5, true);
-  switch (c->var_type)
+
+  gdb_assert (c->var.has_value ());
+
+  switch (c->var->type ())
     {
     case var_string:
     case var_string_noescape:
@@ -142,6 +146,7 @@ deprecated_show_value_hack (struct ui_file *ignore_file,
     case var_enum:
       printf_filtered ((" is \"%s\".\n"), value);
       break;
+
     default:
       printf_filtered ((" is %s.\n"), value);
       break;
@@ -312,7 +317,9 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
   if (arg == NULL)
     arg = "";
 
-  switch (c->var_type)
+  gdb_assert (c->var.has_value ());
+
+  switch (c->var->type ())
     {
     case var_string:
       {
@@ -353,11 +360,12 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
 	*q++ = '\0';
 	newobj = (char *) xrealloc (newobj, q - newobj);
 
-	if (*(char **) c->var == NULL
-	    || strcmp (*(char **) c->var, newobj) != 0)
+	char * const var = c->var->get<char *> ();
+	if (var == nullptr
+	    || strcmp (var, newobj) != 0)
 	  {
-	    xfree (*(char **) c->var);
-	    *(char **) c->var = newobj;
+	    xfree (var);
+	    c->var->set<char *> (newobj);
 
 	    option_changed = true;
 	  }
@@ -366,13 +374,17 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
       }
       break;
     case var_string_noescape:
-      if (*(char **) c->var == NULL || strcmp (*(char **) c->var, arg) != 0)
-	{
-	  xfree (*(char **) c->var);
-	  *(char **) c->var = xstrdup (arg);
+      {
+	char * const var = c->var->get<char *> ();
+	if (var == nullptr
+	    || strcmp (var, arg) != 0)
+	  {
+	    xfree (var);
+	    c->var->set<char *> (xstrdup (arg));
 
-	  option_changed = true;
-	}
+	    option_changed = true;
+	  }
+      }
       break;
     case var_filename:
       if (*arg == '\0')
@@ -398,11 +410,12 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
 	else
 	  val = xstrdup ("");
 
-	if (*(char **) c->var == NULL
-	    || strcmp (*(char **) c->var, val) != 0)
+	char * const var = c->var->get<char *> ();
+	if (var == nullptr
+	    || strcmp (var, val) != 0)
 	  {
-	    xfree (*(char **) c->var);
-	    *(char **) c->var = val;
+	    xfree (var);
+	    c->var->set<char *> (val);
 
 	    option_changed = true;
 	  }
@@ -416,9 +429,9 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
 
 	if (val < 0)
 	  error (_("\"on\" or \"off\" expected."));
-	if (val != *(bool *) c->var)
+	if (val != c->var->get<bool> ())
 	  {
-	    *(bool *) c->var = val;
+	    c->var->set<bool> (val);
 
 	    option_changed = true;
 	  }
@@ -428,9 +441,9 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
       {
 	enum auto_boolean val = parse_auto_binary_operation (arg);
 
-	if (*(enum auto_boolean *) c->var != val)
+	if (c->var->get<enum auto_boolean> () != val)
 	  {
-	    *(enum auto_boolean *) c->var = val;
+	    c->var->set<enum auto_boolean> (val);
 
 	    option_changed = true;
 	  }
@@ -439,11 +452,12 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
     case var_uinteger:
     case var_zuinteger:
       {
-	unsigned int val = parse_cli_var_uinteger (c->var_type, &arg, true);
+	unsigned int val
+	  = parse_cli_var_uinteger (c->var->type (), &arg, true);
 
-	if (*(unsigned int *) c->var != val)
+	if (c->var->get<unsigned int> () != val)
 	  {
-	    *(unsigned int *) c->var = val;
+	    c->var->set<unsigned int> (val);
 
 	    option_changed = true;
 	  }
@@ -456,35 +470,35 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
 
 	if (*arg == '\0')
 	  {
-	    if (c->var_type == var_integer)
+	    if (c->var->type () == var_integer)
 	      error_no_arg (_("integer to set it to, or \"unlimited\"."));
 	    else
 	      error_no_arg (_("integer to set it to."));
 	  }
 
-	if (c->var_type == var_integer && is_unlimited_literal (&arg, true))
+	if (c->var->type () == var_integer && is_unlimited_literal (&arg, true))
 	  val = 0;
 	else
 	  val = parse_and_eval_long (arg);
 
-	if (val == 0 && c->var_type == var_integer)
+	if (val == 0 && c->var->type () == var_integer)
 	  val = INT_MAX;
 	else if (val < INT_MIN
 		 /* For var_integer, don't let the user set the value
 		    to INT_MAX directly, as that exposes an
 		    implementation detail to the user interface.  */
-		 || (c->var_type == var_integer && val >= INT_MAX)
-		 || (c->var_type == var_zinteger && val > INT_MAX))
+		 || (c->var->type () == var_integer && val >= INT_MAX)
+		 || (c->var->type () == var_zinteger && val > INT_MAX))
 	  error (_("integer %s out of range"), plongest (val));
 
-	if (*(int *) c->var != val)
+	if (c->var->get<int> () != val)
 	  {
-	    *(int *) c->var = val;
+	    c->var->set<int> (val);
 
 	    option_changed = true;
 	  }
-	break;
       }
+      break;
     case var_enum:
       {
 	const char *end_arg = arg;
@@ -495,9 +509,9 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
 	if (*after != '\0')
 	  error (_("Junk after item \"%.*s\": %s"), len, arg, after);
 
-	if (*(const char **) c->var != match)
+	if (c->var->get<const char *> () != match)
 	  {
-	    *(const char **) c->var = match;
+	    c->var->set<const char *> (match);
 
 	    option_changed = true;
 	  }
@@ -507,9 +521,9 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
       {
 	int val = parse_cli_var_zuinteger_unlimited (&arg, true);
 
-	if (*(int *) c->var != val)
+	if (c->var->get<int> () != val)
 	  {
-	    *(int *) c->var = val;
+	    c->var->set<int> (val);
 	    option_changed = true;
 	  }
       }
@@ -578,25 +592,33 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
 
       xfree (cmds);
 
-      switch (c->var_type)
+      switch (c->var->type ())
 	{
 	case var_string:
 	case var_string_noescape:
 	case var_filename:
 	case var_optional_filename:
 	case var_enum:
-	  gdb::observers::command_param_changed.notify (name, *(char **) c->var);
+	  {
+	    const char *var;
+	    if (c->var->type () == var_enum)
+	      var = c->var->get<const char *> ();
+	    else
+	      var = c->var->get<char *> ();
+	    gdb::observers::command_param_changed.notify (name, var);
+	  }
 	  break;
 	case var_boolean:
 	  {
-	    const char *opt = *(bool *) c->var ? "on" : "off";
+	    const char *opt = c->var->get<bool> () ? "on" : "off";
 
 	    gdb::observers::command_param_changed.notify (name, opt);
 	  }
 	  break;
 	case var_auto_boolean:
 	  {
-	    const char *s = auto_boolean_enums[*(enum auto_boolean *) c->var];
+	    const char *s
+	      = auto_boolean_enums[c->var->get<enum auto_boolean> ()];
 
 	    gdb::observers::command_param_changed.notify (name, s);
 	  }
@@ -606,7 +628,7 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
 	  {
 	    char s[64];
 
-	    xsnprintf (s, sizeof s, "%u", *(unsigned int *) c->var);
+	    xsnprintf (s, sizeof s, "%u", c->var->get<unsigned int> ());
 	    gdb::observers::command_param_changed.notify (name, s);
 	  }
 	  break;
@@ -616,7 +638,7 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
 	  {
 	    char s[64];
 
-	    xsnprintf (s, sizeof s, "%d", *(int *) c->var);
+	    xsnprintf (s, sizeof s, "%d", c->var->get<int> ());
 	    gdb::observers::command_param_changed.notify (name, s);
 	  }
 	  break;
@@ -628,28 +650,40 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
 /* See cli/cli-setshow.h.  */
 
 std::string
-get_setshow_command_value_string (const cmd_list_element *c)
+get_setshow_command_value_string (const setting &var)
 {
   string_file stb;
 
-  switch (c->var_type)
+  switch (var.type ())
     {
     case var_string:
-      if (*(char **) c->var)
-	stb.putstr (*(char **) c->var, '"');
+      {
+	char *value = var.get<char *> ();
+
+	if (value != nullptr)
+	  stb.putstr (value, '"');
+      }
       break;
     case var_string_noescape:
     case var_optional_filename:
     case var_filename:
     case var_enum:
-      if (*(char **) c->var)
-	stb.puts (*(char **) c->var);
+      {
+	const char *value;
+	if (var.type () == var_enum)
+	  value = var.get<const char *> ();
+	else
+	  value = var.get<char *> ();
+
+	if (value != nullptr)
+	  stb.puts (value);
+      }
       break;
     case var_boolean:
-      stb.puts (*(bool *) c->var ? "on" : "off");
+      stb.puts (var.get<bool> () ? "on" : "off");
       break;
     case var_auto_boolean:
-      switch (*(enum auto_boolean*) c->var)
+      switch (var.get<enum auto_boolean> ())
 	{
 	case AUTO_BOOLEAN_TRUE:
 	  stb.puts ("on");
@@ -667,26 +701,35 @@ get_setshow_command_value_string (const cmd_list_element *c)
       break;
     case var_uinteger:
     case var_zuinteger:
-      if (c->var_type == var_uinteger
-	  && *(unsigned int *) c->var == UINT_MAX)
-	stb.puts ("unlimited");
-      else
-	stb.printf ("%u", *(unsigned int *) c->var);
+      {
+	unsigned int const value = var.get<unsigned int> ();
+
+	if (var.type () == var_uinteger
+	    && value == UINT_MAX)
+	  stb.puts ("unlimited");
+	else
+	  stb.printf ("%u", value);
+      }
       break;
     case var_integer:
     case var_zinteger:
-      if (c->var_type == var_integer
-	  && *(int *) c->var == INT_MAX)
-	stb.puts ("unlimited");
-      else
-	stb.printf ("%d", *(int *) c->var);
+      {
+	int const value = var.get<int> ();
+
+	if (var.type () == var_integer
+	    && value == INT_MAX)
+	  stb.puts ("unlimited");
+	else
+	  stb.printf ("%d", value);
+      }
       break;
     case var_zuinteger_unlimited:
       {
-	if (*(int *) c->var == -1)
+	int const value = var.get<int> ();
+	if (value == -1)
 	  stb.puts ("unlimited");
 	else
-	  stb.printf ("%d", *(int *) c->var);
+	  stb.printf ("%d", value);
       }
       break;
     default:
@@ -708,8 +751,9 @@ do_show_command (const char *arg, int from_tty, struct cmd_list_element *c)
   struct ui_out *uiout = current_uiout;
 
   gdb_assert (c->type == show_cmd);
+  gdb_assert (c->var.has_value ());
 
-  std::string val = get_setshow_command_value_string (c);
+  std::string val = get_setshow_command_value_string (*c->var);
 
   /* FIXME: cagney/2005-02-10: There should be MI and CLI specific
      versions of code to print the value out.  */
diff --git a/gdb/cli/cli-setshow.h b/gdb/cli/cli-setshow.h
index 05f11b170eb..5a8f32189fa 100644
--- a/gdb/cli/cli-setshow.h
+++ b/gdb/cli/cli-setshow.h
@@ -57,8 +57,8 @@ extern void do_set_command (const char *arg, int from_tty,
 extern void do_show_command (const char *arg, int from_tty,
 			     struct cmd_list_element *c);
 
-/* Get a string version of C's current value.  */
-extern std::string get_setshow_command_value_string (const cmd_list_element *c);
+/* Get a string version of VAR's value.  */
+extern std::string get_setshow_command_value_string (const setting &var);
 
 extern void cmd_show_list (struct cmd_list_element *list, int from_tty);
 
diff --git a/gdb/command.h b/gdb/command.h
index baf34401a07..5667c781077 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -125,6 +125,151 @@ typedef enum var_types
   }
 var_types;
 
+/* Return true if a setting of type VAR_TYPE is backed with type T.
+
+   This function is left without definition intentionally.  This template is
+   specialized for all valid types that are used to back var_types.  Therefore
+   if one tries to instantiate this un-specialized template it means the T
+   parameter is not a type used to back a var_type and it is most likely a
+   programming error.  */
+template<typename T>
+bool var_type_uses (var_types var_type) = delete;
+
+/* Return true if a setting of type T is backed by a bool variable.  */
+template<>
+inline bool var_type_uses<bool> (var_types t)
+{
+  return t == var_boolean;
+};
+
+/* Return true if a setting of type T is backed by a auto_boolean variable.
+*/
+template<>
+inline bool var_type_uses<enum auto_boolean> (var_types t)
+{
+  return t == var_auto_boolean;
+}
+
+/* Return true if a setting of type T is backed by an unsigned int variable.
+*/
+template<>
+inline bool var_type_uses<unsigned int> (var_types t)
+{
+  return (t == var_uinteger || t == var_zinteger || t == var_zuinteger);
+}
+
+/* Return true if a setting of type T is backed by an int variable.  */
+template<>
+inline bool var_type_uses<int> (var_types t)
+{
+  return (t == var_integer || t == var_zinteger
+	  || t == var_zuinteger_unlimited);
+}
+
+/* Return true if a setting of type T is backed by a char * variable.  */
+template<>
+inline bool var_type_uses<char *> (var_types t)
+{
+  return (t == var_string || t == var_string_noescape
+	  || t == var_optional_filename || t == var_filename);
+}
+
+/* Return true if a setting of type T is backed by a const char * variable.
+*/
+template<>
+inline bool var_type_uses<const char *> (var_types t)
+{
+  return t == var_enum;
+}
+
+/* Interface for getting and setting a setting's value.
+
+   The underlying data can be of any VAR_TYPES type.  */
+struct setting
+{
+  /* Create a setting backed by a variable of type T.
+
+     Type T must match the var type VAR_TYPE (see VAR_TYPE_USES).  */
+  template<typename T>
+  setting (var_types var_type, T *var)
+    : m_var_type (var_type), m_var (var)
+  {
+    gdb_assert (var != nullptr);
+    gdb_assert (var_type_uses<T> (var_type));
+  }
+
+  /* A setting can also be constructed with a pre-validated
+     type-erased variable.  Use the following function to
+     validate & type-erase said variable/function pointers.  */
+
+  struct erased_args
+  {
+    void *var;
+  };
+
+  template<typename T>
+  static erased_args erase_args (var_types var_type, T *var)
+  {
+    gdb_assert (var_type_uses<T> (var_type));
+
+    return {var};
+  }
+
+  /* Create a setting backed by pre-validated type-erased args.
+     ERASED_VAR's fields' real types must match the var type VAR_TYPE
+     (see VAR_TYPE_USES).  */
+  setting (var_types var_type, const erased_args &args)
+    : m_var_type (var_type),
+      m_var (args.var)
+  {
+  }
+
+  /* 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
+     setting.
+
+     The returned value cannot be NULL (this is checked at runtime).*/
+  template<typename T>
+  const T &get () const
+  {
+    gdb_assert (var_type_uses<T> (m_var_type));
+    gdb_assert (m_var != nullptr);
+
+    return *static_cast<T const *> (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.
+
+     The var_type of the setting must match T.  */
+  template<typename T>
+  void set (const T &v)
+  {
+    /* Check that the current instance is of one of the supported types for
+       this instantiation.  */
+    gdb_assert (var_type_uses<T> (m_var_type));
+
+    *static_cast<T *> (m_var) = v;
+  }
+
+private:
+  /* The type of the variable M_VAR is pointing to.  */
+  var_types m_var_type;
+
+  /* Pointer to the enclosed variable.  The type of the variable is encoded
+     in M_VAR_TYPE.  */
+  void *m_var;
+};
+
 /* This structure records one command'd definition.  */
 struct cmd_list_element;
 
diff --git a/gdb/guile/scm-param.c b/gdb/guile/scm-param.c
index 44ea167be27..0ae368a1e64 100644
--- a/gdb/guile/scm-param.c
+++ b/gdb/guile/scm-param.c
@@ -113,6 +113,30 @@ struct param_smob
   SCM containing_scm;
 };
 
+/* Wraps a setting around an existing param_smob.  This abstraction
+   is used to manipulate the value in S->VALUE in a type safe manner using
+   the setting interface.  */
+
+static setting
+make_setting (param_smob *s)
+{
+
+  if (var_type_uses<bool> (s->type))
+    return setting (s->type, &s->value.boolval);
+  else if (var_type_uses<int> (s->type))
+    return setting (s->type, &s->value.intval);
+  else if (var_type_uses<auto_boolean> (s->type))
+    return setting (s->type, &s->value.autoboolval);
+  else if (var_type_uses<unsigned int> (s->type))
+    return setting (s->type, &s->value.uintval);
+  else if (var_type_uses<char *> (s->type))
+    return setting (s->type, &s->value.stringval);
+  else if (var_type_uses<const char *> (s->type))
+    return setting (s->type, &s->value.cstringval);
+  else
+    gdb_assert_not_reached ("unhandled var type");
+}
+
 static const char param_smob_name[] = "gdb:parameter";
 
 /* The tag Guile knows the param smob by.  */
@@ -133,8 +157,8 @@ static SCM unlimited_keyword;
 
 static int pascm_is_valid (param_smob *);
 static const char *pascm_param_type_name (enum var_types type);
-static SCM pascm_param_value (enum var_types type, void *var,
-			      int arg_pos, const char *func_name);
+static SCM pascm_param_value (const setting &var, int arg_pos,
+			      const char *func_name);
 \f
 /* Administrivia for parameter smobs.  */
 
@@ -153,8 +177,7 @@ pascm_print_param_smob (SCM self, SCM port, scm_print_state *pstate)
 
   gdbscm_printf (port, " %s ", pascm_param_type_name (p_smob->type));
 
-  value = pascm_param_value (p_smob->type, &p_smob->value,
-			     GDBSCM_ARG_NONE, NULL);
+  value = pascm_param_value (make_setting (p_smob), GDBSCM_ARG_NONE, NULL);
   scm_display (value, port);
 
   scm_puts (">", port);
@@ -444,7 +467,7 @@ add_setshow_generic (enum var_types param_type, enum command_class cmd_class,
 				       show_doc, help_doc, set_func, show_func,
 				       set_list, show_list);
       /* Initialize the value, just in case.  */
-      self->value.cstringval = self->enumeration[0];
+      make_setting (self).set<const char *> (self->enumeration[0]);
       break;
 
     default:
@@ -563,17 +586,17 @@ pascm_param_type_name (enum var_types param_type)
 }
 
 /* Return the value of a gdb parameter as a Scheme value.
-   If TYPE is not supported, then a <gdb:exception> object is returned.  */
+   If the var_type of VAR is not supported, then a <gdb:exception> object is
+   returned.  */
 
 static SCM
-pascm_param_value (enum var_types type, void *var,
-		   int arg_pos, const char *func_name)
+pascm_param_value (const setting &var, int arg_pos, const char *func_name)
 {
   /* Note: We *could* support var_integer here in case someone is trying to get
      the value of a Python-created parameter (which is the only place that
      still supports var_integer).  To further discourage its use we do not.  */
 
-  switch (type)
+  switch (var.type ())
     {
     case var_string:
     case var_string_noescape:
@@ -581,16 +604,20 @@ pascm_param_value (enum var_types type, void *var,
     case var_filename:
     case var_enum:
       {
-	const char *str = *(char **) var;
+	const char *str;
+	if (var.type () == var_enum)
+	  str = var.get<const char *> ();
+	else
+	  str = var.get<char *> ();
 
-	if (str == NULL)
+	if (str == nullptr)
 	  str = "";
 	return gdbscm_scm_from_host_string (str, strlen (str));
       }
 
     case var_boolean:
       {
-	if (* (bool *) var)
+	if (var.get<bool> ())
 	  return SCM_BOOL_T;
 	else
 	  return SCM_BOOL_F;
@@ -598,7 +625,7 @@ pascm_param_value (enum var_types type, void *var,
 
     case var_auto_boolean:
       {
-	enum auto_boolean ab = * (enum auto_boolean *) var;
+	enum auto_boolean ab = var.get<enum auto_boolean> ();
 
 	if (ab == AUTO_BOOLEAN_TRUE)
 	  return SCM_BOOL_T;
@@ -609,67 +636,69 @@ pascm_param_value (enum var_types type, void *var,
       }
 
     case var_zuinteger_unlimited:
-      if (* (int *) var == -1)
+      if (var.get<int> () == -1)
 	return unlimited_keyword;
-      gdb_assert (* (int *) var >= 0);
+      gdb_assert (var.get<int> () >= 0);
       /* Fall through.  */
     case var_zinteger:
-      return scm_from_int (* (int *) var);
+      return scm_from_int (var.get<int> ());
 
     case var_uinteger:
-      if (* (unsigned int *) var == UINT_MAX)
+      if (var.get<unsigned int> ()== UINT_MAX)
 	return unlimited_keyword;
       /* Fall through.  */
     case var_zuinteger:
-      return scm_from_uint (* (unsigned int *) var);
+      return scm_from_uint (var.get<unsigned int> ());
 
     default:
       break;
     }
 
   return gdbscm_make_out_of_range_error (func_name, arg_pos,
-					 scm_from_int (type),
+					 scm_from_int (var.type ()),
 					 _("program error: unhandled type"));
 }
 
-/* Set the value of a parameter of type TYPE in VAR from VALUE.
+/* Set the value of a parameter of type P_SMOB->TYPE in P_SMOB->VAR from VALUE.
    ENUMERATION is the list of enum values for enum parameters, otherwise NULL.
    Throws a Scheme exception if VALUE_SCM is invalid for TYPE.  */
 
 static void
-pascm_set_param_value_x (enum var_types type, union pascm_variable *var,
+pascm_set_param_value_x (param_smob *p_smob,
 			 const char * const *enumeration,
 			 SCM value, int arg_pos, const char *func_name)
 {
-  switch (type)
+  setting var = make_setting (p_smob);
+
+  switch (var.type ())
     {
     case var_string:
     case var_string_noescape:
     case var_optional_filename:
     case var_filename:
       SCM_ASSERT_TYPE (scm_is_string (value)
-		       || (type != var_filename
+		       || (var.type () != var_filename
 			   && gdbscm_is_false (value)),
 		       value, arg_pos, func_name,
 		       _("string or #f for non-PARAM_FILENAME parameters"));
       if (gdbscm_is_false (value))
 	{
-	  xfree (var->stringval);
-	  if (type == var_optional_filename)
-	    var->stringval = xstrdup ("");
+	  xfree (var.get<char *> ());
+	  if (var.type () == var_optional_filename)
+	    var.set<char *> (xstrdup (""));
 	  else
-	    var->stringval = NULL;
+	    var.set<char *> (nullptr);
 	}
       else
 	{
 	  SCM exception;
 
 	  gdb::unique_xmalloc_ptr<char> string
-	    = gdbscm_scm_to_host_string (value, NULL, &exception);
-	  if (string == NULL)
+	    = gdbscm_scm_to_host_string (value, nullptr, &exception);
+	  if (string == nullptr)
 	    gdbscm_throw (exception);
-	  xfree (var->stringval);
-	  var->stringval = string.release ();
+	  xfree (var.get<char *> ());
+	  var.set<char *> (string.release ());
 	}
       break;
 
@@ -681,27 +710,27 @@ pascm_set_param_value_x (enum var_types type, union pascm_variable *var,
 	SCM_ASSERT_TYPE (scm_is_string (value), value, arg_pos, func_name,
 		       _("string"));
 	gdb::unique_xmalloc_ptr<char> str
-	  = gdbscm_scm_to_host_string (value, NULL, &exception);
-	if (str == NULL)
+	  = gdbscm_scm_to_host_string (value, nullptr, &exception);
+	if (str == nullptr)
 	  gdbscm_throw (exception);
 	for (i = 0; enumeration[i]; ++i)
 	  {
 	    if (strcmp (enumeration[i], str.get ()) == 0)
 	      break;
 	  }
-	if (enumeration[i] == NULL)
+	if (enumeration[i] == nullptr)
 	  {
 	    gdbscm_out_of_range_error (func_name, arg_pos, value,
 				       _("not member of enumeration"));
 	  }
-	var->cstringval = enumeration[i];
+	var.set<const char *> (enumeration[i]);
 	break;
       }
 
     case var_boolean:
       SCM_ASSERT_TYPE (gdbscm_is_bool (value), value, arg_pos, func_name,
 		       _("boolean"));
-      var->boolval = gdbscm_is_true (value);
+      var.set<bool> (gdbscm_is_true (value));
       break;
 
     case var_auto_boolean:
@@ -710,19 +739,19 @@ pascm_set_param_value_x (enum var_types type, union pascm_variable *var,
 		       value, arg_pos, func_name,
 		       _("boolean or #:auto"));
       if (scm_is_eq (value, auto_keyword))
-	var->autoboolval = AUTO_BOOLEAN_AUTO;
+	var.set<enum auto_boolean> (AUTO_BOOLEAN_AUTO);
       else if (gdbscm_is_true (value))
-	var->autoboolval = AUTO_BOOLEAN_TRUE;
+	var.set<enum auto_boolean> (AUTO_BOOLEAN_TRUE);
       else
-	var->autoboolval = AUTO_BOOLEAN_FALSE;
+	var.set<enum auto_boolean> (AUTO_BOOLEAN_FALSE);
       break;
 
     case var_zinteger:
     case var_uinteger:
     case var_zuinteger:
     case var_zuinteger_unlimited:
-      if (type == var_uinteger
-	  || type == var_zuinteger_unlimited)
+      if (var.type () == var_uinteger
+	  || var.type () == var_zuinteger_unlimited)
 	{
 	  SCM_ASSERT_TYPE (gdbscm_is_bool (value)
 			   || scm_is_eq (value, unlimited_keyword),
@@ -730,10 +759,10 @@ pascm_set_param_value_x (enum var_types type, union pascm_variable *var,
 			   _("integer or #:unlimited"));
 	  if (scm_is_eq (value, unlimited_keyword))
 	    {
-	      if (type == var_uinteger)
-		var->intval = UINT_MAX;
+	      if (var.type () == var_uinteger)
+		var.set<unsigned int> (UINT_MAX);
 	      else
-		var->intval = -1;
+		var.set<int> (-1);
 	      break;
 	    }
 	}
@@ -743,25 +772,25 @@ pascm_set_param_value_x (enum var_types type, union pascm_variable *var,
 			   _("integer"));
 	}
 
-      if (type == var_uinteger
-	  || type == var_zuinteger)
+      if (var.type () == var_uinteger
+	  || var.type () == var_zuinteger)
 	{
 	  unsigned int u = scm_to_uint (value);
 
-	  if (type == var_uinteger && u == 0)
+	  if (var.type () == var_uinteger && u == 0)
 	    u = UINT_MAX;
-	  var->uintval = u;
+	  var.set<unsigned int> (u);
 	}
       else
 	{
 	  int i = scm_to_int (value);
 
-	  if (type == var_zuinteger_unlimited && i < -1)
+	  if (var.type () == var_zuinteger_unlimited && i < -1)
 	    {
 	      gdbscm_out_of_range_error (func_name, arg_pos, value,
 					 _("must be >= -1"));
 	    }
-	  var->intval = i;
+	  var.set<int> (i);
 	}
       break;
 
@@ -934,7 +963,7 @@ gdbscm_make_parameter (SCM name_scm, SCM rest)
 	  if (gdbscm_is_exception (initial_value_scm))
 	    gdbscm_throw (initial_value_scm);
 	}
-      pascm_set_param_value_x (p_smob->type, &p_smob->value, enum_list,
+      pascm_set_param_value_x (p_smob, enum_list,
 			       initial_value_scm,
 			       initial_value_arg_pos, FUNC_NAME);
     }
@@ -1030,8 +1059,7 @@ gdbscm_parameter_value (SCM self)
       param_smob *p_smob = pascm_get_param_smob_arg_unsafe (self, SCM_ARG1,
 							    FUNC_NAME);
 
-      return pascm_param_value (p_smob->type, &p_smob->value,
-				SCM_ARG1, FUNC_NAME);
+      return pascm_param_value (make_setting (p_smob), SCM_ARG1, FUNC_NAME);
     }
   else
     {
@@ -1062,13 +1090,14 @@ gdbscm_parameter_value (SCM self)
 	  gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG1, self,
 				     _("parameter not found"));
 	}
-      if (cmd->var == NULL)
+
+      if (!cmd->var.has_value ())
 	{
 	  gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG1, self,
 				     _("not a parameter"));
 	}
 
-      return pascm_param_value (cmd->var_type, cmd->var, SCM_ARG1, FUNC_NAME);
+      return pascm_param_value (*cmd->var, SCM_ARG1, FUNC_NAME);
     }
 }
 
@@ -1080,7 +1109,7 @@ gdbscm_set_parameter_value_x (SCM self, SCM value)
   param_smob *p_smob = pascm_get_param_smob_arg_unsafe (self, SCM_ARG1,
 							FUNC_NAME);
 
-  pascm_set_param_value_x (p_smob->type, &p_smob->value, p_smob->enumeration,
+  pascm_set_param_value_x (p_smob, p_smob->enumeration,
 			   value, SCM_ARG2, FUNC_NAME);
 
   return SCM_UNSPECIFIED;
diff --git a/gdb/maint.c b/gdb/maint.c
index c6d13a3a732..8aae53bdd65 100644
--- a/gdb/maint.c
+++ b/gdb/maint.c
@@ -1112,7 +1112,7 @@ set_per_command_cmd (const char *args, int from_tty)
     error (_("Bad value for 'mt set per-command no'."));
 
   for (list = per_command_setlist; list != NULL; list = list->next)
-    if (list->var_type == var_boolean)
+    if (list->var->type () == var_boolean)
       {
 	gdb_assert (list->type == set_cmd);
 	do_set_command (args, from_tty, list);
diff --git a/gdb/python/py-param.c b/gdb/python/py-param.c
index f9dcb076c60..1dd716bba14 100644
--- a/gdb/python/py-param.c
+++ b/gdb/python/py-param.c
@@ -88,6 +88,30 @@ struct parmpy_object
   const char **enumeration;
 };
 
+/* Wraps a setting around an existing parmpy_object.  This abstraction
+   is used to manipulate the value in S->VALUE in a type safe manner using
+   the setting interface.  */
+
+static setting
+make_setting (parmpy_object *s)
+{
+
+  if (var_type_uses<bool> (s->type))
+    return setting (s->type, &s->value.boolval);
+  else if (var_type_uses<int> (s->type))
+    return setting (s->type, &s->value.intval);
+  else if (var_type_uses<auto_boolean> (s->type))
+    return setting (s->type, &s->value.autoboolval);
+  else if (var_type_uses<unsigned int> (s->type))
+    return setting (s->type, &s->value.uintval);
+  else if (var_type_uses<char *> (s->type))
+    return setting (s->type, &s->value.stringval);
+  else if (var_type_uses<const char *> (s->type))
+    return setting (s->type, &s->value.cstringval);
+  else
+    gdb_assert_not_reached ("unhandled var type");
+}
+
 extern PyTypeObject parmpy_object_type
     CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("parmpy_object");
 
@@ -110,7 +134,7 @@ get_attr (PyObject *obj, PyObject *attr_name)
     {
       parmpy_object *self = (parmpy_object *) obj;
 
-      return gdbpy_parameter_value (self->type, &self->value);
+      return gdbpy_parameter_value (make_setting (self));
     }
 
   return PyObject_GenericGetAttr (obj, attr_name);
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 368681b797c..022d4a67172 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -438,7 +438,7 @@ PyObject *gdbpy_create_ptid_object (ptid_t ptid);
 PyObject *gdbpy_selected_thread (PyObject *self, PyObject *args);
 PyObject *gdbpy_selected_inferior (PyObject *self, PyObject *args);
 PyObject *gdbpy_string_to_argv (PyObject *self, PyObject *args);
-PyObject *gdbpy_parameter_value (enum var_types type, void *var);
+PyObject *gdbpy_parameter_value (const setting &var);
 gdb::unique_xmalloc_ptr<char> gdbpy_parse_command_name
   (const char *name, struct cmd_list_element ***base_list,
    struct cmd_list_element **start_list);
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 37eacefd8fc..a26c37352ff 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -449,9 +449,9 @@ python_command (const char *arg, int from_tty)
    NULL (and set a Python exception) on error.  Helper function for
    get_parameter.  */
 PyObject *
-gdbpy_parameter_value (enum var_types type, void *var)
+gdbpy_parameter_value (const setting &var)
 {
-  switch (type)
+  switch (var.type ())
     {
     case var_string:
     case var_string_noescape:
@@ -459,16 +459,20 @@ gdbpy_parameter_value (enum var_types type, void *var)
     case var_filename:
     case var_enum:
       {
-	const char *str = *(char **) var;
+	const char *str;
+        if (var.type () == var_enum)
+          str = var.get<const char *> ();
+        else
+          str = var.get<char *> ();
 
-	if (! str)
+	if (str == nullptr)
 	  str = "";
 	return host_string_to_python_string (str).release ();
       }
 
     case var_boolean:
       {
-	if (* (bool *) var)
+	if (var.get<bool> ())
 	  Py_RETURN_TRUE;
 	else
 	  Py_RETURN_FALSE;
@@ -476,7 +480,7 @@ gdbpy_parameter_value (enum var_types type, void *var)
 
     case var_auto_boolean:
       {
-	enum auto_boolean ab = * (enum auto_boolean *) var;
+	enum auto_boolean ab = var.get<enum auto_boolean> ();
 
 	if (ab == AUTO_BOOLEAN_TRUE)
 	  Py_RETURN_TRUE;
@@ -487,16 +491,16 @@ gdbpy_parameter_value (enum var_types type, void *var)
       }
 
     case var_integer:
-      if ((* (int *) var) == INT_MAX)
+      if (var.get<int> () == INT_MAX)
 	Py_RETURN_NONE;
       /* Fall through.  */
     case var_zinteger:
     case var_zuinteger_unlimited:
-      return gdb_py_object_from_longest (* (int *) var).release ();
+      return gdb_py_object_from_longest (var.get<int> ()).release ();
 
     case var_uinteger:
       {
-	unsigned int val = * (unsigned int *) var;
+	unsigned int val = var.get<unsigned int> ();
 
 	if (val == UINT_MAX)
 	  Py_RETURN_NONE;
@@ -505,7 +509,7 @@ gdbpy_parameter_value (enum var_types type, void *var)
 
     case var_zuinteger:
       {
-	unsigned int val = * (unsigned int *) var;
+	unsigned int val = var.get<unsigned int> ();
 	return gdb_py_object_from_ulongest (val).release ();
       }
     }
@@ -542,10 +546,11 @@ gdbpy_parameter (PyObject *self, PyObject *args)
     return PyErr_Format (PyExc_RuntimeError,
 			 _("Could not find parameter `%s'."), arg);
 
-  if (! cmd->var)
+  if (!cmd->var.has_value ())
     return PyErr_Format (PyExc_RuntimeError,
 			 _("`%s' is not a parameter."), arg);
-  return gdbpy_parameter_value (cmd->var_type, cmd->var);
+
+  return gdbpy_parameter_value (*cmd->var);
 }
 
 /* Wrapper for target_charset.  */
diff --git a/gdb/remote.c b/gdb/remote.c
index c4a31e0c2f8..29b18c9427f 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -2239,12 +2239,13 @@ show_remote_protocol_packet_cmd (struct ui_file *file, int from_tty,
 				 const char *value)
 {
   struct packet_config *packet;
+  gdb_assert (c->var.has_value ());
 
   for (packet = remote_protocol_packets;
        packet < &remote_protocol_packets[PACKET_MAX];
        packet++)
     {
-      if (&packet->detect == c->var)
+      if (&packet->detect == &c->var->get<enum auto_boolean> ())
 	{
 	  show_packet_config_cmd (packet);
 	  return;
-- 
2.33.0


^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH v4 2/4] gdb: make string-like set show commands use std::string variable
  2021-09-29 21:50 [PATCH v4 0/4] Refactor cmd_list_element.var Lancelot SIX
  2021-09-29 21:50 ` [PATCH v4 1/4] gdb: Introduce setting construct within cmd_list_element Lancelot SIX
@ 2021-09-29 21:50 ` Lancelot SIX
  2021-09-29 21:50 ` [PATCH v4 3/4] gdb: Have setter and getter callbacks for settings Lancelot SIX
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 18+ messages in thread
From: Lancelot SIX @ 2021-09-29 21:50 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi, Lancelot SIX

From: Simon Marchi <simon.marchi@efficios.com>

String-like settings (var_string, var_filename, var_optional_filename,
var_string_noescape) currently take a pointer to a `char *` storage
variable (typically global) that holds the setting's value.  I'd like to
"mordernize" this by changing them to use an std::string for storage.

An obvious reason is that string operations on std::string are often
easier to write than with C strings.  And they avoid having to do any
manual memory management.

Another interesting reason is that, with `char *`, nullptr and an empty
string often both have the same meaning of "no value".  String settings
are initially nullptr (unless initialized otherwise).  But when doing
"set foo" (where `foo` is a string setting), the setting now points to
an empty string.  For example, solib_search_path is nullptr at startup,
but points to an empty string after doing "set solib-search-path".  This
leads to some code that needs to check for both to check for "no value".
Or some code that converts back and forth between NULL and "" when
getting or setting the value.  I find this very error-prone, because it
is very easy to forget one or the other.  With std::string, we at least
know that the variable is not "NULL".  There is only one way of
representing an empty string setting, that is with an empty string.

I was wondering whether the distinction between NULL and "" would be
important for some setting, but it doesn't seem so.  If that ever
happens, it would be more C++-y and self-descriptive to use
optional<string> anyway.

Actually, there's one spot where this distinction mattered, it's in
init_history, for the test gdb.base/gdbinit-history.exp.  init_history
sets the history filename to the default ".gdb_history" if it sees that
the setting was never set - if history_filename is nullptr.  If
history_filename is an empty string, it means the setting was explicitly
cleared, so it leaves it as-is.  With the change to std::string, this
distinction doesn't exist anymore.  This can be fixed by moving the code
that chooses a good default value for history_filename to
_initialize_top.  This is ran before -ex commands are processed, so an
-ex command can then clear that value if needed (what
gdb.base/gdbinit-history.exp tests).

Another small improvement, in my opinion is that we can now easily
give string parameters initial values, by simply initializing the global
variables, instead of xstrdup-ing it in the _initialize function.

In Python and Guile, when registering a string-like parameter, we
allocate (with new) an std::string that is owned by the param_smob (in
Guile) and the parmpy_object (in Python) objects.

This patch started by changing all relevant add_setshow_* commands to
take an `std::string *` instead of a `char **` and fixing everything
that failed to build.  That includes of course all string setting
variable and their uses.

string_option_def now uses an std::string also, because there's a
connection between options and settings (see
add_setshow_cmds_for_options).

The add_path function in source.c is really complex and twisted, I'd
rather not try to change it to work on an std::string right now.
Instead, I added an overload that copies the std:string to a `char *`
and back.  This means more copying, but this is not used in a hot path
at all, so I think it is acceptable.

Change-Id: I92c50a1bdd8307141cdbacb388248e4e4fc08c93
Co-authored-by: Lancelot SIX <lsix@lancelotsix.com>
---
 gdb/auto-load.c           |  48 ++++++----------
 gdb/breakpoint.c          |  22 ++++----
 gdb/build-id.c            |   4 +-
 gdb/cli/cli-cmds.c        |  49 ++++++++++-------
 gdb/cli/cli-decode.c      |  38 +++++++------
 gdb/cli/cli-logging.c     |  23 ++++----
 gdb/cli/cli-option.c      |   9 ++-
 gdb/cli/cli-option.h      |   4 +-
 gdb/cli/cli-setshow.c     |  59 ++++++++------------
 gdb/command.h             |  23 ++++----
 gdb/compile/compile.c     |  46 ++++++++--------
 gdb/corefile.c            |  17 +++---
 gdb/defs.h                |   4 +-
 gdb/disasm.c              |  11 ++--
 gdb/disasm.h              |   2 +-
 gdb/dwarf2/dwz.c          |   2 +-
 gdb/dwarf2/index-cache.c  |  10 ++--
 gdb/dwarf2/read.c         |  10 ++--
 gdb/event-top.c           |  12 ++--
 gdb/fork-child.c          |   7 +--
 gdb/guile/scm-param.c     |  62 ++++++++++++---------
 gdb/infcmd.c              |  14 ++---
 gdb/linux-thread-db.c     |  17 ++----
 gdb/main.c                |  17 ++----
 gdb/maint-test-options.c  |  11 +---
 gdb/maint-test-settings.c |   8 +--
 gdb/mi/mi-cmd-env.c       |  18 +++---
 gdb/proc-api.c            |   5 +-
 gdb/python/py-param.c     |  45 ++++++++-------
 gdb/python/python.c       |  10 ++--
 gdb/remote-sim.c          |   7 +--
 gdb/remote.c              |  10 ++--
 gdb/serial.c              |   8 +--
 gdb/solib.c               |  20 +++----
 gdb/source.c              |  66 ++++++++++++----------
 gdb/source.h              |   5 +-
 gdb/stack.c               |  22 ++++----
 gdb/symfile.c             |  49 ++++++++---------
 gdb/symtab.c              |  46 ++++++++--------
 gdb/target-descriptions.c |   2 +-
 gdb/top.c                 | 112 ++++++++++++++++++--------------------
 gdb/top.h                 |   2 +-
 gdb/tracepoint.c          |  29 +++++-----
 gdb/tracepoint.h          |   2 +-
 44 files changed, 477 insertions(+), 510 deletions(-)

diff --git a/gdb/auto-load.c b/gdb/auto-load.c
index 7f0bb74c32a..36d87252b3f 100644
--- a/gdb/auto-load.c
+++ b/gdb/auto-load.c
@@ -137,7 +137,7 @@ show_auto_load_local_gdbinit (struct ui_file *file, int from_tty,
 /* Directory list from which to load auto-loaded scripts.  It is not checked
    for absolute paths but they are strongly recommended.  It is initialized by
    _initialize_auto_load.  */
-static char *auto_load_dir;
+static std::string auto_load_dir = AUTO_LOAD_DIR;
 
 /* "set" command for the auto_load_dir configuration variable.  */
 
@@ -145,11 +145,8 @@ static void
 set_auto_load_dir (const char *args, int from_tty, struct cmd_list_element *c)
 {
   /* Setting the variable to "" resets it to the compile time defaults.  */
-  if (auto_load_dir[0] == '\0')
-    {
-      xfree (auto_load_dir);
-      auto_load_dir = xstrdup (AUTO_LOAD_DIR);
-    }
+  if (auto_load_dir.empty ())
+    auto_load_dir = AUTO_LOAD_DIR;
 }
 
 /* "show" command for the auto_load_dir configuration variable.  */
@@ -166,7 +163,7 @@ show_auto_load_dir (struct ui_file *file, int from_tty,
 /* Directory list safe to hold auto-loaded files.  It is not checked for
    absolute paths but they are strongly recommended.  It is initialized by
    _initialize_auto_load.  */
-static char *auto_load_safe_path;
+static std::string auto_load_safe_path = AUTO_LOAD_SAFE_PATH;
 
 /* Vector of directory elements of AUTO_LOAD_SAFE_PATH with each one normalized
    by tilde_expand and possibly each entries has added its gdb_realpath
@@ -181,7 +178,7 @@ auto_load_expand_dir_vars (const char *string)
 {
   char *s = xstrdup (string);
   substitute_path_component (&s, "$datadir", gdb_datadir.c_str ());
-  substitute_path_component (&s, "$debugdir", debug_file_directory);
+  substitute_path_component (&s, "$debugdir", debug_file_directory.c_str ());
 
   if (debug_auto_load && strcmp (s, string) != 0)
     auto_load_debug_printf ("Expanded $-variables to \"%s\".", s);
@@ -199,9 +196,10 @@ static void
 auto_load_safe_path_vec_update (void)
 {
   auto_load_debug_printf ("Updating directories of \"%s\".",
-			  auto_load_safe_path);
+			  auto_load_safe_path.c_str ());
 
-  auto_load_safe_path_vec = auto_load_expand_dir_vars (auto_load_safe_path);
+  auto_load_safe_path_vec
+    = auto_load_expand_dir_vars (auto_load_safe_path.c_str ());
   size_t len = auto_load_safe_path_vec.size ();
 
   /* Apply tilde_expand and gdb_realpath to each AUTO_LOAD_SAFE_PATH_VEC
@@ -253,11 +251,8 @@ set_auto_load_safe_path (const char *args,
 			 int from_tty, struct cmd_list_element *c)
 {
   /* Setting the variable to "" resets it to the compile time defaults.  */
-  if (auto_load_safe_path[0] == '\0')
-    {
-      xfree (auto_load_safe_path);
-      auto_load_safe_path = xstrdup (AUTO_LOAD_SAFE_PATH);
-    }
+  if (auto_load_safe_path.empty ())
+    auto_load_safe_path = AUTO_LOAD_SAFE_PATH;
 
   auto_load_safe_path_vec_update ();
 }
@@ -291,17 +286,14 @@ show_auto_load_safe_path (struct ui_file *file, int from_tty,
 static void
 add_auto_load_safe_path (const char *args, int from_tty)
 {
-  char *s;
-
   if (args == NULL || *args == 0)
     error (_("\
 Directory argument required.\n\
 Use 'set auto-load safe-path /' for disabling the auto-load safe-path security.\
 "));
 
-  s = xstrprintf ("%s%c%s", auto_load_safe_path, DIRNAME_SEPARATOR, args);
-  xfree (auto_load_safe_path);
-  auto_load_safe_path = s;
+  auto_load_safe_path = string_printf ("%s%c%s", auto_load_safe_path.c_str (),
+				       DIRNAME_SEPARATOR, args);
 
   auto_load_safe_path_vec_update ();
 }
@@ -312,14 +304,11 @@ Use 'set auto-load safe-path /' for disabling the auto-load safe-path security.\
 static void
 add_auto_load_dir (const char *args, int from_tty)
 {
-  char *s;
-
   if (args == NULL || *args == 0)
     error (_("Directory argument required."));
 
-  s = xstrprintf ("%s%c%s", auto_load_dir, DIRNAME_SEPARATOR, args);
-  xfree (auto_load_dir);
-  auto_load_dir = s;
+  auto_load_dir = string_printf ("%s%c%s", auto_load_dir.c_str (),
+				 DIRNAME_SEPARATOR, args);
 }
 
 /* Implementation for filename_is_in_pattern overwriting the caller's FILENAME
@@ -459,7 +448,7 @@ file_is_auto_load_safe (const char *filename)
   warning (_("File \"%ps\" auto-loading has been declined by your "
 	     "`auto-load safe-path' set to \"%s\"."),
 	   styled_string (file_name_style.style (), filename_real.get ()),
-	   auto_load_safe_path);
+	   auto_load_safe_path.c_str ());
 
   if (!advice_printed)
     {
@@ -749,11 +738,11 @@ auto_load_objfile_script_1 (struct objfile *objfile, const char *realname,
 	 directory.  */
 
       std::vector<gdb::unique_xmalloc_ptr<char>> vec
-	= auto_load_expand_dir_vars (auto_load_dir);
+	= auto_load_expand_dir_vars (auto_load_dir.c_str ());
 
       auto_load_debug_printf
 	("Searching 'set auto-load scripts-directory' path \"%s\".",
-	 auto_load_dir);
+	 auto_load_dir.c_str ());
 
       /* Convert Windows file name from c:/dir/file to /c/dir/file.  */
       if (HAS_DRIVE_SPEC (debugfile))
@@ -1542,8 +1531,6 @@ This option has security implications for untrusted inferiors."),
 Usage: info auto-load local-gdbinit"),
 	   auto_load_info_cmdlist_get ());
 
-  auto_load_dir = xstrdup (AUTO_LOAD_DIR);
-
   suffix = ext_lang_auto_load_suffix (get_ext_lang_defn (EXT_LANG_GDB));
   gdb_name_help
     = xstrprintf (_("\
@@ -1595,7 +1582,6 @@ Show the list of directories from which to load auto-loaded scripts."),
   xfree (gdb_name_help);
   xfree (guile_name_help);
 
-  auto_load_safe_path = xstrdup (AUTO_LOAD_SAFE_PATH);
   auto_load_safe_path_vec_update ();
   add_setshow_optional_filename_cmd ("safe-path", class_support,
 				     &auto_load_safe_path, _("\
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 3b626b8f4aa..e742a1eccfe 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -276,7 +276,7 @@ static const char *dprintf_style = dprintf_style_gdb;
    copied into the command, so it can be anything that GDB can
    evaluate to a callable address, not necessarily a function name.  */
 
-static char *dprintf_function;
+static std::string dprintf_function = "printf";
 
 /* The channel to use for dynamic printf if the preferred style is to
    call into the inferior; if a nonempty string, it will be passed to
@@ -286,7 +286,7 @@ static char *dprintf_function;
    "stderr", this could be an app-specific expression like
    "mystreams[curlogger]".  */
 
-static char *dprintf_channel;
+static std::string dprintf_channel;
 
 /* True if dprintf commands should continue to operate even if GDB
    has disconnected.  */
@@ -6658,7 +6658,7 @@ default_collect_info (void)
   /* If it has no value (which is frequently the case), say nothing; a
      message like "No default-collect." gets in user's face when it's
      not wanted.  */
-  if (!*default_collect)
+  if (default_collect.empty ())
     return;
 
   /* The following phrase lines up nicely with per-tracepoint collect
@@ -8759,17 +8759,17 @@ update_dprintf_command_list (struct breakpoint *b)
     printf_line = xstrprintf ("printf %s", dprintf_args);
   else if (strcmp (dprintf_style, dprintf_style_call) == 0)
     {
-      if (!dprintf_function)
+      if (dprintf_function.empty ())
 	error (_("No function supplied for dprintf call"));
 
-      if (dprintf_channel && strlen (dprintf_channel) > 0)
+      if (!dprintf_channel.empty ())
 	printf_line = xstrprintf ("call (void) %s (%s,%s)",
-				  dprintf_function,
-				  dprintf_channel,
+				  dprintf_function.c_str (),
+				  dprintf_channel.c_str (),
 				  dprintf_args);
       else
 	printf_line = xstrprintf ("call (void) %s (%s)",
-				  dprintf_function,
+				  dprintf_function.c_str (),
 				  dprintf_args);
     }
   else if (strcmp (dprintf_style, dprintf_style_agent) == 0)
@@ -15102,8 +15102,8 @@ save_breakpoints (const char *filename, int from_tty,
 	}
     }
 
-  if (extra_trace_bits && *default_collect)
-    fp.printf ("set default-collect %s\n", default_collect);
+  if (extra_trace_bits && !default_collect.empty ())
+    fp.printf ("set default-collect %s\n", default_collect.c_str ());
 
   if (from_tty)
     printf_filtered (_("Saved to file '%s'.\n"), expanded_filename.get ());
@@ -16014,7 +16014,6 @@ output stream by setting dprintf-function and dprintf-channel."),
 			update_dprintf_commands, NULL,
 			&setlist, &showlist);
 
-  dprintf_function = xstrdup ("printf");
   add_setshow_string_cmd ("dprintf-function", class_support,
 			  &dprintf_function, _("\
 Set the function to use for dynamic printf."), _("\
@@ -16022,7 +16021,6 @@ Show the function to use for dynamic printf."), NULL,
 			  update_dprintf_commands, NULL,
 			  &setlist, &showlist);
 
-  dprintf_channel = xstrdup ("");
   add_setshow_string_cmd ("dprintf-channel", class_support,
 			  &dprintf_channel, _("\
 Set the channel to use for dynamic printf."), _("\
diff --git a/gdb/build-id.c b/gdb/build-id.c
index d2f2576d96d..553d6cec3d2 100644
--- a/gdb/build-id.c
+++ b/gdb/build-id.c
@@ -130,7 +130,7 @@ build_id_to_bfd_suffix (size_t build_id_len, const bfd_byte *build_id,
      cause "/.build-id/..." lookups.  */
 
   std::vector<gdb::unique_xmalloc_ptr<char>> debugdir_vec
-    = dirnames_to_char_ptr_vec (debug_file_directory);
+    = dirnames_to_char_ptr_vec (debug_file_directory.c_str ());
 
   for (const gdb::unique_xmalloc_ptr<char> &debugdir : debugdir_vec)
     {
@@ -167,7 +167,7 @@ build_id_to_bfd_suffix (size_t build_id_len, const bfd_byte *build_id,
 	 Don't do it if the sysroot is the target system ("target:").  It
 	 could work in theory, but the lrealpath in build_id_to_debug_bfd_1
 	 only works with local paths.  */
-      if (strcmp (gdb_sysroot, TARGET_SYSROOT_PREFIX) != 0)
+      if (gdb_sysroot != TARGET_SYSROOT_PREFIX)
 	{
 	  link = gdb_sysroot + link;
 	  debug_bfd = build_id_to_debug_bfd_1 (link, build_id_len, build_id);
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index ecbe5a4b43c..f8f013348db 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -650,7 +650,7 @@ find_and_open_script (const char *script_file, int search_path)
   /* Search for and open 'file' on the search path used for source
      files.  Put the full location in *FULL_PATHP.  */
   gdb::unique_xmalloc_ptr<char> full_path;
-  fd = openp (source_path, search_flags,
+  fd = openp (source_path.c_str (), search_flags,
 	      file.get (), O_RDONLY, &full_path);
 
   if (fd == -1)
@@ -1042,12 +1042,7 @@ edit_command (const char *arg, int from_tty)
 struct pipe_cmd_opts
 {
   /* For "-d".  */
-  char *delimiter = nullptr;
-
-  ~pipe_cmd_opts ()
-  {
-    xfree (delimiter);
-  }
+  std::string delimiter;
 };
 
 static const gdb::option::option_def pipe_cmd_option_defs[] = {
@@ -1084,8 +1079,8 @@ pipe_command (const char *arg, int from_tty)
     (&arg, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, grp);
 
   const char *delim = "|";
-  if (opts.delimiter != nullptr)
-    delim = opts.delimiter;
+  if (!opts.delimiter.empty ())
+    delim = opts.delimiter.c_str ();
 
   const char *command = arg;
   if (command == nullptr)
@@ -1148,8 +1143,8 @@ pipe_command_completer (struct cmd_list_element *ignore,
     return;
 
   const char *delimiter = "|";
-  if (opts.delimiter != nullptr)
-    delimiter = opts.delimiter;
+  if (!opts.delimiter.empty ())
+    delimiter = opts.delimiter.c_str ();
 
   /* Check if we're past option values already.  */
   if (text > org_text && !isspace (text[-1]))
@@ -2152,13 +2147,21 @@ value_from_setting (const setting &var, struct gdbarch *gdbarch)
     case var_enum:
       {
 	const char *value;
+	size_t len;
 	if (var.type () == var_enum)
-	  value = var.get<const char *> ();
+	  {
+	    value = var.get<const char *> ();
+	    len = strlen (value);
+	  }
 	else
-	  value = var.get<char *> ();
+	  {
+	    const std::string &st = var.get<std::string> ();
+	    value = st.c_str ();
+	    len = st.length ();
+	  }
 
-	if (value != nullptr)
-	  return value_cstring (value, strlen (value),
+	if (len > 0)
+	  return value_cstring (value, len,
 				builtin_type (gdbarch)->builtin_char);
 	else
 	  return value_cstring ("", 1,
@@ -2231,13 +2234,21 @@ str_value_from_setting (const setting &var, struct gdbarch *gdbarch)
 	 similarly to the value_from_setting code for these casevar.  */
       {
 	const char *value;
+	size_t len;
 	if (var.type () == var_enum)
-	  value = var.get<const char *> ();
+	  {
+	    value = var.get<const char *> ();
+	    len = strlen (value);
+	  }
 	else
-	  value = var.get<char *> ();
+	  {
+	    const std::string &st = var.get<std::string> ();
+	    value = st.c_str ();
+	    len = st.length ();
+	  }
 
-	if (value != nullptr)
-	  return value_cstring (value, strlen (value),
+	if (len > 0)
+	  return value_cstring (value, len,
 				builtin_type (gdbarch)->builtin_char);
 	else
 	  return value_cstring ("", 1,
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index 56befc979dc..b3bf6276157 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -655,7 +655,7 @@ add_setshow_boolean_cmd (const char *name, enum command_class theclass, bool *va
 
 set_show_commands
 add_setshow_filename_cmd (const char *name, enum command_class theclass,
-			  char **var,
+			  std::string *var,
 			  const char *set_doc, const char *show_doc,
 			  const char *help_doc,
 			  cmd_func_ftype *set_func,
@@ -664,10 +664,10 @@ add_setshow_filename_cmd (const char *name, enum command_class theclass,
 			  struct cmd_list_element **show_list)
 {
   set_show_commands commands
-    = add_setshow_cmd_full<char *> (name, theclass, var_filename, var,
-				    set_doc, show_doc, help_doc,
-				    set_func, show_func,
-				    set_list, show_list);
+    = 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);
 
   set_cmd_completer (commands.set, filename_completer);
 
@@ -679,7 +679,7 @@ add_setshow_filename_cmd (const char *name, enum command_class theclass,
 
 set_show_commands
 add_setshow_string_cmd (const char *name, enum command_class theclass,
-			char **var,
+			std::string *var,
 			const char *set_doc, const char *show_doc,
 			const char *help_doc,
 			cmd_func_ftype *set_func,
@@ -688,10 +688,10 @@ add_setshow_string_cmd (const char *name, enum command_class theclass,
 			struct cmd_list_element **show_list)
 {
   set_show_commands commands
-    = add_setshow_cmd_full<char *> (name, theclass, var_string, var,
-				    set_doc, show_doc, help_doc,
-				    set_func, show_func,
-				    set_list, show_list);
+    = 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);
 
   /* Disable the default symbol completer.  */
   set_cmd_completer (commands.set, nullptr);
@@ -704,7 +704,7 @@ add_setshow_string_cmd (const char *name, enum command_class theclass,
 
 set_show_commands
 add_setshow_string_noescape_cmd (const char *name, enum command_class theclass,
-				 char **var,
+				 std::string *var,
 				 const char *set_doc, const char *show_doc,
 				 const char *help_doc,
 				 cmd_func_ftype *set_func,
@@ -713,9 +713,10 @@ add_setshow_string_noescape_cmd (const char *name, enum command_class theclass,
 				 struct cmd_list_element **show_list)
 {
   set_show_commands commands
-    = add_setshow_cmd_full<char *> (name, theclass, var_string_noescape, var,
-				    set_doc, show_doc, help_doc, set_func,
-				    show_func, set_list, show_list);
+    = 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);
 
   /* Disable the default symbol completer.  */
   set_cmd_completer (commands.set, nullptr);
@@ -728,7 +729,7 @@ add_setshow_string_noescape_cmd (const char *name, enum command_class theclass,
 
 set_show_commands
 add_setshow_optional_filename_cmd (const char *name, enum command_class theclass,
-				   char **var,
+				   std::string *var,
 				   const char *set_doc, const char *show_doc,
 				   const char *help_doc,
 				   cmd_func_ftype *set_func,
@@ -737,9 +738,10 @@ add_setshow_optional_filename_cmd (const char *name, enum command_class theclass
 				   struct cmd_list_element **show_list)
 {
   set_show_commands commands
-    = add_setshow_cmd_full<char *> (name, theclass, var_optional_filename,
-				    var, set_doc, show_doc, help_doc,
-				    set_func, show_func, set_list, show_list);
+    = 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);
 
   set_cmd_completer (commands.set, filename_completer);
 
diff --git a/gdb/cli/cli-logging.c b/gdb/cli/cli-logging.c
index dfedd7599a2..c9093520a71 100644
--- a/gdb/cli/cli-logging.c
+++ b/gdb/cli/cli-logging.c
@@ -25,7 +25,7 @@
 
 static char *saved_filename;
 
-static char *logging_filename;
+static std::string logging_filename = "gdb.txt";
 static void
 show_logging_filename (struct ui_file *file, int from_tty,
 		       struct cmd_list_element *c, const char *value)
@@ -102,7 +102,7 @@ handle_redirections (int from_tty)
     }
 
   stdio_file_up log (new no_terminal_escape_file ());
-  if (!log->open (logging_filename, logging_overwrite ? "w" : "a"))
+  if (!log->open (logging_filename.c_str (), logging_overwrite ? "w" : "a"))
     perror_with_name (_("set logging"));
 
   /* Redirects everything to gdb_stdout while this is running.  */
@@ -110,20 +110,20 @@ handle_redirections (int from_tty)
     {
       if (!logging_redirect)
 	fprintf_unfiltered (gdb_stdout, "Copying output to %s.\n",
-			    logging_filename);
+			    logging_filename.c_str ());
       else
 	fprintf_unfiltered (gdb_stdout, "Redirecting output to %s.\n",
-			    logging_filename);
+			    logging_filename.c_str ());
 
       if (!debug_redirect)
 	fprintf_unfiltered (gdb_stdout, "Copying debug output to %s.\n",
-			    logging_filename);
+			    logging_filename.c_str ());
       else
 	fprintf_unfiltered (gdb_stdout, "Redirecting debug output to %s.\n",
-			    logging_filename);
+			    logging_filename.c_str ());
     }
 
-  saved_filename = xstrdup (logging_filename);
+  saved_filename = xstrdup (logging_filename.c_str ());
 
   /* Let the interpreter do anything it needs.  */
   current_interp_set_logging (std::move (log), logging_redirect,
@@ -145,10 +145,8 @@ set_logging_on (const char *args, int from_tty)
   const char *rest = args;
 
   if (rest && *rest)
-    {
-      xfree (logging_filename);
-      logging_filename = xstrdup (rest);
-    }
+    logging_filename = rest;
+
   handle_redirections (from_tty);
 }
 
@@ -201,6 +199,7 @@ If debug redirect is on, debug will go only to the log file."),
 			   set_logging_redirect,
 			   show_logging_redirect,
 			   &set_logging_cmdlist, &show_logging_cmdlist);
+
   add_setshow_filename_cmd ("file", class_support, &logging_filename, _("\
 Set the current logfile."), _("\
 Show the current logfile."), _("\
@@ -212,6 +211,4 @@ The logfile is used when directing GDB's output."),
 	   _("Enable logging."), &set_logging_cmdlist);
   add_cmd ("off", class_support, set_logging_off,
 	   _("Disable logging."), &set_logging_cmdlist);
-
-  logging_filename = xstrdup ("gdb.txt");
 }
diff --git a/gdb/cli/cli-option.c b/gdb/cli/cli-option.c
index ab76f194c3d..846b8198985 100644
--- a/gdb/cli/cli-option.c
+++ b/gdb/cli/cli-option.c
@@ -45,7 +45,7 @@ union option_value
   const char *enumeration;
 
   /* For var_string options.  This is malloc-allocated.  */
-  char *string;
+  std::string *string;
 };
 
 /* Holds an options definition and its value.  */
@@ -87,7 +87,7 @@ struct option_def_and_value
     if (value.has_value ())
       {
 	if (option.type == var_string)
-	  xfree (value->string);
+	  delete value->string;
       }
   }
 
@@ -439,7 +439,7 @@ parse_option (gdb::array_view<const option_def_group> options_group,
 	  error (_("-%s requires an argument"), match->name);
 
 	option_value val;
-	val.string = xstrdup (str.c_str ());
+	val.string = new std::string (std::move (str));
 	return option_def_and_value {*match, match_ctx, val};
       }
 
@@ -603,8 +603,7 @@ save_option_value_in_ctx (gdb::optional<option_def_and_value> &ov)
       break;
     case var_string:
       *ov->option.var_address.string (ov->option, ov->ctx)
-	= ov->value->string;
-      ov->value->string = nullptr;
+	= std::move (*ov->value->string);
       break;
     default:
       gdb_assert_not_reached ("unhandled option type");
diff --git a/gdb/cli/cli-option.h b/gdb/cli/cli-option.h
index aa2ccbed5d0..b7ede458748 100644
--- a/gdb/cli/cli-option.h
+++ b/gdb/cli/cli-option.h
@@ -86,7 +86,7 @@ struct option_def
       unsigned int *(*uinteger) (const option_def &, void *ctx);
       int *(*integer) (const option_def &, void *ctx);
       const char **(*enumeration) (const option_def &, void *ctx);
-      char **(*string) (const option_def &, void *ctx);
+      std::string *(*string) (const option_def &, void *ctx);
     }
   var_address;
 
@@ -268,7 +268,7 @@ template<typename Context>
 struct string_option_def : option_def
 {
   string_option_def (const char *long_option_,
-		     char **(*get_var_address_cb_) (Context *),
+		     std::string *(*get_var_address_cb_) (Context *),
 		     show_value_ftype *show_cmd_cb_,
 		     const char *set_doc_,
 		     const char *show_doc_ = nullptr,
diff --git a/gdb/cli/cli-setshow.c b/gdb/cli/cli-setshow.c
index a247f5eb58d..a027891de05 100644
--- a/gdb/cli/cli-setshow.c
+++ b/gdb/cli/cli-setshow.c
@@ -360,27 +360,22 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
 	*q++ = '\0';
 	newobj = (char *) xrealloc (newobj, q - newobj);
 
-	char * const var = c->var->get<char *> ();
-	if (var == nullptr
-	    || strcmp (var, newobj) != 0)
+	const std::string &cur_val = c->var->get<std::string> ();
+	if (strcmp (cur_val.c_str(),  newobj) != 0)
 	  {
-	    xfree (var);
-	    c->var->set<char *> (newobj);
+	    c->var->set<std::string> (std::string (newobj));
 
 	    option_changed = true;
 	  }
-	else
-	  xfree (newobj);
+	xfree (newobj);
       }
       break;
     case var_string_noescape:
       {
-	char * const var = c->var->get<char *> ();
-	if (var == nullptr
-	    || strcmp (var, arg) != 0)
+	const std::string &cur_val = c->var->get<std::string> ();
+	if (strcmp (cur_val.c_str (), arg) != 0)
 	  {
-	    xfree (var);
-	    c->var->set<char *> (xstrdup (arg));
+	    c->var->set<std::string> (std::string (arg));
 
 	    option_changed = true;
 	  }
@@ -410,17 +405,14 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
 	else
 	  val = xstrdup ("");
 
-	char * const var = c->var->get<char *> ();
-	if (var == nullptr
-	    || strcmp (var, val) != 0)
+	const std::string &cur_val = c->var->get<std::string> ();
+	if (strcmp (cur_val.c_str (), val) != 0)
 	  {
-	    xfree (var);
-	    c->var->set<char *> (val);
+	    c->var->set<std::string> (std::string (val));
 
 	    option_changed = true;
 	  }
-	else
-	  xfree (val);
+	xfree (val);
       }
       break;
     case var_boolean:
@@ -598,15 +590,12 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
 	case var_string_noescape:
 	case var_filename:
 	case var_optional_filename:
+	  gdb::observers::command_param_changed.notify
+	    (name, c->var->get<std::string> ().c_str ());
+	  break;
 	case var_enum:
-	  {
-	    const char *var;
-	    if (c->var->type () == var_enum)
-	      var = c->var->get<const char *> ();
-	    else
-	      var = c->var->get<char *> ();
-	    gdb::observers::command_param_changed.notify (name, var);
-	  }
+	  gdb::observers::command_param_changed.notify
+	    (name, c->var->get<const char *> ());
 	  break;
 	case var_boolean:
 	  {
@@ -658,23 +647,19 @@ get_setshow_command_value_string (const setting &var)
     {
     case var_string:
       {
-	char *value = var.get<char *> ();
-
-	if (value != nullptr)
-	  stb.putstr (value, '"');
+	std::string value = var.get<std::string> ();
+	if (!value.empty ())
+	  stb.putstr (value.c_str (), '"');
       }
       break;
     case var_string_noescape:
     case var_optional_filename:
     case var_filename:
+      stb.puts (var.get<std::string> ().c_str ());
+      break;
     case var_enum:
       {
-	const char *value;
-	if (var.type () == var_enum)
-	  value = var.get<const char *> ();
-	else
-	  value = var.get<char *> ();
-
+	const char *value = var.get<const char *> ();
 	if (value != nullptr)
 	  stb.puts (value);
       }
diff --git a/gdb/command.h b/gdb/command.h
index 5667c781077..c660bccf662 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -97,16 +97,15 @@ typedef enum var_types
 
     /* String which the user enters with escapes (e.g. the user types
        \n and it is a real newline in the stored string).
-       *VAR is a malloc'd string, or NULL if the string is empty.  */
+       *VAR is a std::string, "" if the string is empty.  */
     var_string,
     /* String which stores what the user types verbatim.
-       *VAR is a malloc'd string, or NULL if the string is empty.  */
+       *VAR is std::string, "" if the string is empty.  */
     var_string_noescape,
-    /* String which stores a filename.  (*VAR) is a malloc'd string,
-       or "" if the string was empty.  */
+    /* String which stores a filename.  (*VAR) is a std::string,
+       "" if the string was empty.  */
     var_optional_filename,
-    /* String which stores a filename.  (*VAR) is a malloc'd
-       string.  */
+    /* String which stores a filename.  (*VAR) is a std::string.  */
     var_filename,
     /* ZeroableInteger.  *VAR is an int.  Like var_integer except
        that zero really means zero.  */
@@ -166,9 +165,9 @@ inline bool var_type_uses<int> (var_types t)
 	  || t == var_zuinteger_unlimited);
 }
 
-/* Return true if a setting of type T is backed by a char * variable.  */
+/* Return true if a setting of type T is backed by a std::string variable.  */
 template<>
-inline bool var_type_uses<char *> (var_types t)
+inline bool var_type_uses<std::string> (var_types t)
 {
   return (t == var_string || t == var_string_noescape
 	  || t == var_optional_filename || t == var_filename);
@@ -564,25 +563,25 @@ extern set_show_commands add_setshow_boolean_cmd
    cmd_list_element **show_list);
 
 extern set_show_commands add_setshow_filename_cmd
-  (const char *name, command_class theclass, char **var, const char *set_doc,
+  (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, char **var, const char *set_doc,
+  (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, char **var, const char *set_doc,
+  (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, char **var, const char *set_doc,
+  (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);
diff --git a/gdb/compile/compile.c b/gdb/compile/compile.c
index e815348ff07..25cfd283b33 100644
--- a/gdb/compile/compile.c
+++ b/gdb/compile/compile.c
@@ -495,7 +495,22 @@ get_expr_block_and_pc (CORE_ADDR *pc)
 }
 
 /* String for 'set compile-args' and 'show compile-args'.  */
-static char *compile_args;
+static std::string compile_args =
+  /* Override flags possibly coming from DW_AT_producer.  */
+  "-O0 -gdwarf-4"
+  /* We use -fPIE Otherwise GDB would need to reserve space large enough for
+     any object file in the inferior in advance to get the final address when
+     to link the object file to and additionally the default system linker
+     script would need to be modified so that one can specify there the
+     absolute target address.
+     -fPIC is not used at is would require from GDB to generate .got.  */
+  " -fPIE"
+  /* We want warnings, except for some commonly happening for GDB commands.  */
+  " -Wall "
+  " -Wno-unused-but-set-variable"
+  " -Wno-unused-variable"
+  /* Override CU's possible -fstack-protector-strong.  */
+  " -fno-stack-protector";
 
 /* Parsed form of COMPILE_ARGS.  */
 static gdb_argv compile_args_argv;
@@ -505,7 +520,7 @@ static gdb_argv compile_args_argv;
 static void
 set_compile_args (const char *args, int from_tty, struct cmd_list_element *c)
 {
-  compile_args_argv = gdb_argv (compile_args);
+  compile_args_argv = gdb_argv (compile_args.c_str ());
 }
 
 /* Implement 'show compile-args'.  */
@@ -520,7 +535,7 @@ show_compile_args (struct ui_file *file, int from_tty,
 }
 
 /* String for 'set compile-gcc' and 'show compile-gcc'.  */
-static char *compile_gcc;
+static std::string compile_gcc;
 
 /* Implement 'show compile-gcc'.  */
 
@@ -696,13 +711,13 @@ compile_to_object (struct command_line *cmd, const char *cmd_string,
 
   compiler->set_verbose (compile_debug);
 
-  if (compile_gcc[0] != 0)
+  if (!compile_gcc.empty ())
     {
       if (compiler->version () < GCC_FE_VERSION_1)
 	error (_("Command 'set compile-gcc' requires GCC version 6 or higher "
 		 "(libcc1 interface version 1 or higher)"));
 
-      compiler->set_driver_filename (compile_gcc);
+      compiler->set_driver_filename (compile_gcc.c_str ());
     }
   else
     {
@@ -1029,23 +1044,9 @@ String quoting is parsed like in shell, for example:\n\
   -mno-align-double \"-I/dir with a space/include\""),
 			  set_compile_args, show_compile_args, &setlist, &showlist);
 
-  /* Override flags possibly coming from DW_AT_producer.  */
-  compile_args = xstrdup ("-O0 -gdwarf-4"
-  /* We use -fPIE Otherwise GDB would need to reserve space large enough for
-     any object file in the inferior in advance to get the final address when
-     to link the object file to and additionally the default system linker
-     script would need to be modified so that one can specify there the
-     absolute target address.
-     -fPIC is not used at is would require from GDB to generate .got.  */
-			 " -fPIE"
-  /* We want warnings, except for some commonly happening for GDB commands.  */
-			 " -Wall "
-			 " -Wno-unused-but-set-variable"
-			 " -Wno-unused-variable"
-  /* Override CU's possible -fstack-protector-strong.  */
-			 " -fno-stack-protector"
-  );
-  set_compile_args (compile_args, 0, NULL);
+
+  /* Initialize compile_args_argv.  */
+  set_compile_args (compile_args.c_str (), 0, NULL);
 
   add_setshow_optional_filename_cmd ("compile-gcc", class_support,
 				     &compile_gcc,
@@ -1058,5 +1059,4 @@ It should be absolute filename of the gcc executable.\n\
 If empty the default target triplet will be searched in $PATH."),
 				     NULL, show_compile_gcc, &setlist,
 				     &showlist);
-  compile_gcc = xstrdup ("");
 }
diff --git a/gdb/corefile.c b/gdb/corefile.c
index ff2494738b0..4b1451487e2 100644
--- a/gdb/corefile.c
+++ b/gdb/corefile.c
@@ -395,7 +395,7 @@ write_memory_signed_integer (CORE_ADDR addr, int len,
 const char *gnutarget;
 
 /* Same thing, except it is "auto" not NULL for the default case.  */
-static char *gnutarget_string;
+static std::string gnutarget_string;
 static void
 show_gnutarget_string (struct ui_file *file, int from_tty,
 		       struct cmd_list_element *c,
@@ -409,15 +409,15 @@ static void
 set_gnutarget_command (const char *ignore, int from_tty,
 		       struct cmd_list_element *c)
 {
-  char *gend = gnutarget_string + strlen (gnutarget_string);
+  const char *gend = gnutarget_string.c_str () + gnutarget_string.size ();
+  gend = remove_trailing_whitespace (gnutarget_string.c_str (), gend);
+  gnutarget_string
+    = gnutarget_string.substr (0, gend - gnutarget_string.data ());
 
-  gend = remove_trailing_whitespace (gnutarget_string, gend);
-  *gend = '\0';
-
-  if (strcmp (gnutarget_string, "auto") == 0)
+  if (gnutarget_string == "auto")
     gnutarget = NULL;
   else
-    gnutarget = gnutarget_string;
+    gnutarget = gnutarget_string.c_str ();
 }
 
 /* A completion function for "set gnutarget".  */
@@ -449,8 +449,7 @@ complete_set_gnutarget (struct cmd_list_element *cmd,
 void
 set_gnutarget (const char *newtarget)
 {
-  xfree (gnutarget_string);
-  gnutarget_string = xstrdup (newtarget);
+  gnutarget_string = newtarget;
   set_gnutarget_command (NULL, 0, NULL);
 }
 
diff --git a/gdb/defs.h b/gdb/defs.h
index 6dea4099554..f7e09eca9db 100644
--- a/gdb/defs.h
+++ b/gdb/defs.h
@@ -121,7 +121,7 @@ using RequireLongest = gdb::Requires<gdb::Or<std::is_same<T, LONGEST>,
 extern int dbx_commands;
 
 /* * System root path, used to find libraries etc.  */
-extern char *gdb_sysroot;
+extern std::string gdb_sysroot;
 
 /* * GDB datadir, used to store data files.  */
 extern std::string gdb_datadir;
@@ -131,7 +131,7 @@ extern std::string gdb_datadir;
 extern std::string python_libdir;
 
 /* * Search path for separate debug files.  */
-extern char *debug_file_directory;
+extern std::string debug_file_directory;
 
 /* GDB's SIGINT handler basically sets a flag; code that might take a
    long time before it gets back to the event loop, and which ought to
diff --git a/gdb/disasm.c b/gdb/disasm.c
index 7f730f68188..c788f5b618c 100644
--- a/gdb/disasm.c
+++ b/gdb/disasm.c
@@ -39,7 +39,7 @@
 
 /* This variable is used to hold the prospective disassembler_options value
    which is set by the "set disassembler_options" command.  */
-static char *prospective_options = NULL;
+static std::string prospective_options;
 
 /* This structure is used to store line number information for the
    deprecated /m option.
@@ -928,13 +928,16 @@ get_disassembler_options (struct gdbarch *gdbarch)
 }
 
 void
-set_disassembler_options (char *prospective_options)
+set_disassembler_options (const char *prospective_options)
 {
   struct gdbarch *gdbarch = get_current_arch ();
   char **disassembler_options = gdbarch_disassembler_options (gdbarch);
   const disasm_options_and_args_t *valid_options_and_args;
   const disasm_options_t *valid_options;
-  char *options = remove_whitespace_and_extra_commas (prospective_options);
+  gdb::unique_xmalloc_ptr<char> prospective_options_local
+    = make_unique_xstrdup (prospective_options);
+  char *options = remove_whitespace_and_extra_commas
+    (prospective_options_local.get ());
   const char *opt;
 
   /* Allow all architectures, even ones that do not support 'set disassembler',
@@ -1003,7 +1006,7 @@ static void
 set_disassembler_options_sfunc (const char *args, int from_tty,
 				struct cmd_list_element *c)
 {
-  set_disassembler_options (prospective_options);
+  set_disassembler_options (prospective_options.c_str ());
 }
 
 static void
diff --git a/gdb/disasm.h b/gdb/disasm.h
index eb82bc3cd01..6cbfdcd4f6b 100644
--- a/gdb/disasm.h
+++ b/gdb/disasm.h
@@ -164,6 +164,6 @@ extern char *get_disassembler_options (struct gdbarch *gdbarch);
 
 /* Sets the active gdbarch's disassembler options to OPTIONS.  */
 
-extern void set_disassembler_options (char *options);
+extern void set_disassembler_options (const char *options);
 
 #endif
diff --git a/gdb/dwarf2/dwz.c b/gdb/dwarf2/dwz.c
index f9d5db6b48a..5e963d5b47a 100644
--- a/gdb/dwarf2/dwz.c
+++ b/gdb/dwarf2/dwz.c
@@ -118,7 +118,7 @@ dwz_search_other_debugdirs (std::string &filename, bfd_byte *buildid,
 
   gdb_bfd_ref_ptr dwz_bfd;
   std::vector<gdb::unique_xmalloc_ptr<char>> debugdir_vec
-    = dirnames_to_char_ptr_vec (debug_file_directory);
+    = dirnames_to_char_ptr_vec (debug_file_directory.c_str ());
 
   for (const gdb::unique_xmalloc_ptr<char> &debugdir : debugdir_vec)
     {
diff --git a/gdb/dwarf2/index-cache.c b/gdb/dwarf2/index-cache.c
index c3decf16ead..f439b37db5a 100644
--- a/gdb/dwarf2/index-cache.c
+++ b/gdb/dwarf2/index-cache.c
@@ -37,7 +37,7 @@
 static bool debug_index_cache = false;
 
 /* The index cache directory, used for "set/show index-cache directory".  */
-static char *index_cache_directory = NULL;
+static std::string index_cache_directory;
 
 /* See dwarf-index.cache.h.  */
 index_cache global_index_cache;
@@ -290,9 +290,9 @@ set_index_cache_directory_command (const char *arg, int from_tty,
 				   cmd_list_element *element)
 {
   /* Make sure the index cache directory is absolute and tilde-expanded.  */
-  gdb::unique_xmalloc_ptr<char> abs (gdb_abspath (index_cache_directory));
-  xfree (index_cache_directory);
-  index_cache_directory = abs.release ();
+  gdb::unique_xmalloc_ptr<char> abs
+    = gdb_abspath (index_cache_directory.c_str ());
+  index_cache_directory = abs.get ();
   global_index_cache.set_directory (index_cache_directory);
 }
 
@@ -325,7 +325,7 @@ _initialize_index_cache ()
   std::string cache_dir = get_standard_cache_dir ();
   if (!cache_dir.empty ())
     {
-      index_cache_directory = xstrdup (cache_dir.c_str ());
+      index_cache_directory = cache_dir;
       global_index_cache.set_directory (std::move (cache_dir));
     }
   else
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 00aa64dd0ab..412ddef5081 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -12134,10 +12134,10 @@ try_open_dwop_file (dwarf2_per_objfile *per_objfile,
   gdb::unique_xmalloc_ptr<char> search_path_holder;
   if (search_cwd)
     {
-      if (*debug_file_directory != '\0')
+      if (!debug_file_directory.empty ())
 	{
 	  search_path_holder.reset (concat (".", dirname_separator_string,
-					    debug_file_directory,
+					    debug_file_directory.c_str (),
 					    (char *) NULL));
 	  search_path = search_path_holder.get ();
 	}
@@ -12145,7 +12145,7 @@ try_open_dwop_file (dwarf2_per_objfile *per_objfile,
 	search_path = ".";
     }
   else
-    search_path = debug_file_directory;
+    search_path = debug_file_directory.c_str ();
 
   /* Add the path for the executable binary to the list of search paths.  */
   std::string objfile_dir = ldirname (objfile_name (per_objfile->objfile));
@@ -12216,7 +12216,7 @@ open_dwo_file (dwarf2_per_objfile *per_objfile,
   /* That didn't work, try debug-file-directory, which, despite its name,
      is a list of paths.  */
 
-  if (*debug_file_directory == '\0')
+  if (debug_file_directory.empty ())
     return NULL;
 
   return try_open_dwop_file (per_objfile, file_name,
@@ -12545,7 +12545,7 @@ open_dwp_file (dwarf2_per_objfile *per_objfile, const char *file_name)
      [IWBN if the dwp file name was recorded in the executable, akin to
      .gnu_debuglink, but that doesn't exist yet.]
      Strip the directory from FILE_NAME and search again.  */
-  if (*debug_file_directory != '\0')
+  if (!debug_file_directory.empty ())
     {
       /* Don't implicitly search the current directory here.
 	 If the user wants to search "." to handle this case,
diff --git a/gdb/event-top.c b/gdb/event-top.c
index 64c624ce4d7..530ea298247 100644
--- a/gdb/event-top.c
+++ b/gdb/event-top.c
@@ -439,13 +439,11 @@ display_gdb_prompt (const char *new_prompt)
 static std::string
 top_level_prompt (void)
 {
-  char *prompt;
-
   /* Give observers a chance of changing the prompt.  E.g., the python
      `gdb.prompt_hook' is installed as an observer.  */
-  gdb::observers::before_prompt.notify (get_prompt ());
+  gdb::observers::before_prompt.notify (get_prompt ().c_str ());
 
-  prompt = get_prompt ();
+  const std::string &prompt = get_prompt ();
 
   if (annotation_level >= 2)
     {
@@ -456,7 +454,7 @@ top_level_prompt (void)
 	 beginning.  */
       const char suffix[] = "\n\032\032prompt\n";
 
-      return std::string (prefix) + prompt + suffix;
+      return std::string (prefix) + prompt.c_str () + suffix;
     }
 
   return prompt;
@@ -1253,13 +1251,13 @@ handle_sigtstp (int sig)
 static void
 async_sigtstp_handler (gdb_client_data arg)
 {
-  char *prompt = get_prompt ();
+  const std::string &prompt = get_prompt ();
 
   signal (SIGTSTP, SIG_DFL);
   unblock_signal (SIGTSTP);
   raise (SIGTSTP);
   signal (SIGTSTP, handle_sigtstp);
-  printf_unfiltered ("%s", prompt);
+  printf_unfiltered ("%s", prompt.c_str ());
   gdb_flush (gdb_stdout);
 
   /* Forget about any previous command -- null line now will do
diff --git a/gdb/fork-child.c b/gdb/fork-child.c
index 3ce7d64b855..3e995ed6f65 100644
--- a/gdb/fork-child.c
+++ b/gdb/fork-child.c
@@ -33,14 +33,14 @@
 /* The exec-wrapper, if any, that will be used when starting the
    inferior.  */
 
-static char *exec_wrapper = NULL;
+static std::string exec_wrapper;
 
 /* See gdbsupport/common-inferior.h.  */
 
 const char *
 get_exec_wrapper ()
 {
-  return exec_wrapper;
+  return !exec_wrapper.empty () ? exec_wrapper.c_str () : nullptr;
 }
 
 /* See nat/fork-inferior.h.  */
@@ -139,8 +139,7 @@ gdb_startup_inferior (pid_t pid, int num_traps)
 static void
 unset_exec_wrapper_command (const char *args, int from_tty)
 {
-  xfree (exec_wrapper);
-  exec_wrapper = NULL;
+  exec_wrapper.clear ();
 }
 
 static void
diff --git a/gdb/guile/scm-param.c b/gdb/guile/scm-param.c
index 0ae368a1e64..17746daaf33 100644
--- a/gdb/guile/scm-param.c
+++ b/gdb/guile/scm-param.c
@@ -44,7 +44,7 @@ union pascm_variable
   unsigned int uintval;
 
   /* Hold a string, for the various string types.  */
-  char *stringval;
+  std::string *stringval;
 
   /* Hold a string, for enums.  */
   const char *cstringval;
@@ -57,10 +57,7 @@ union pascm_variable
    2) Call register-parameter! to add the parameter to gdb.
    It is done this way so that the constructor, make-parameter, doesn't have
    any side-effects.  This means that the smob needs to store everything
-   that was passed to make-parameter.
-
-   N.B. There is no free function for this smob.
-   All objects pointed to by this smob must live in GC space.  */
+   that was passed to make-parameter.  */
 
 struct param_smob
 {
@@ -120,7 +117,6 @@ struct param_smob
 static setting
 make_setting (param_smob *s)
 {
-
   if (var_type_uses<bool> (s->type))
     return setting (s->type, &s->value.boolval);
   else if (var_type_uses<int> (s->type))
@@ -129,8 +125,8 @@ make_setting (param_smob *s)
     return setting (s->type, &s->value.autoboolval);
   else if (var_type_uses<unsigned int> (s->type))
     return setting (s->type, &s->value.uintval);
-  else if (var_type_uses<char *> (s->type))
-    return setting (s->type, &s->value.stringval);
+  else if (var_type_uses<std::string> (s->type))
+    return setting (s->type, s->value.stringval);
   else if (var_type_uses<const char *> (s->type))
     return setting (s->type, &s->value.cstringval);
   else
@@ -432,14 +428,14 @@ add_setshow_generic (enum var_types param_type, enum command_class cmd_class,
 
     case var_string:
       commands = add_setshow_string_cmd (cmd_name, cmd_class,
-					 &self->value.stringval, set_doc,
+					 self->value.stringval, set_doc,
 					 show_doc, help_doc, set_func,
 					 show_func, set_list, show_list);
       break;
 
     case var_string_noescape:
       commands = add_setshow_string_noescape_cmd (cmd_name, cmd_class,
-						  &self->value.stringval,
+						  self->value.stringval,
 						  set_doc, show_doc, help_doc,
 						  set_func, show_func, set_list,
 						  show_list);
@@ -448,7 +444,7 @@ add_setshow_generic (enum var_types param_type, enum command_class cmd_class,
 
     case var_optional_filename:
       commands = add_setshow_optional_filename_cmd (cmd_name, cmd_class,
-						    &self->value.stringval,
+						    self->value.stringval,
 						    set_doc, show_doc, help_doc,
 						    set_func, show_func,
 						    set_list, show_list);
@@ -456,7 +452,7 @@ add_setshow_generic (enum var_types param_type, enum command_class cmd_class,
 
     case var_filename:
       commands = add_setshow_filename_cmd (cmd_name, cmd_class,
-					   &self->value.stringval, set_doc,
+					   self->value.stringval, set_doc,
 					   show_doc, help_doc, set_func,
 					   show_func, set_list, show_list);
       break;
@@ -602,14 +598,14 @@ pascm_param_value (const setting &var, int arg_pos, const char *func_name)
     case var_string_noescape:
     case var_optional_filename:
     case var_filename:
-    case var_enum:
       {
-	const char *str;
-	if (var.type () == var_enum)
-	  str = var.get<const char *> ();
-	else
-	  str = var.get<char *> ();
+	const std::string &str = var.get<std::string> ();
+	return gdbscm_scm_from_host_string (str.c_str (), str.length ());
+      }
 
+    case var_enum:
+      {
+	const char *str = var.get<const char *> ();
 	if (str == nullptr)
 	  str = "";
 	return gdbscm_scm_from_host_string (str, strlen (str));
@@ -682,13 +678,7 @@ pascm_set_param_value_x (param_smob *p_smob,
 		       value, arg_pos, func_name,
 		       _("string or #f for non-PARAM_FILENAME parameters"));
       if (gdbscm_is_false (value))
-	{
-	  xfree (var.get<char *> ());
-	  if (var.type () == var_optional_filename)
-	    var.set<char *> (xstrdup (""));
-	  else
-	    var.set<char *> (nullptr);
-	}
+	var.set<std::string> ("");
       else
 	{
 	  SCM exception;
@@ -697,8 +687,7 @@ pascm_set_param_value_x (param_smob *p_smob,
 	    = gdbscm_scm_to_host_string (value, nullptr, &exception);
 	  if (string == nullptr)
 	    gdbscm_throw (exception);
-	  xfree (var.get<char *> ());
-	  var.set<char *> (string.release ());
+	  var.set<std::string> (string.release ());
 	}
       break;
 
@@ -798,6 +787,21 @@ pascm_set_param_value_x (param_smob *p_smob,
       gdb_assert_not_reached ("bad parameter type");
     }
 }
+
+/* Free function for a param_smob.  */
+static size_t
+pascm_free_parameter_smob (SCM self)
+{
+  param_smob *p_smob = (param_smob *) SCM_SMOB_DATA (self);
+
+  if (var_type_uses<std::string> (p_smob->type))
+    {
+      delete p_smob->value.stringval;
+      p_smob->value.stringval = nullptr;
+    }
+
+  return 0;
+}
 \f
 /* Parameter Scheme functions.  */
 
@@ -954,6 +958,10 @@ gdbscm_make_parameter (SCM name_scm, SCM rest)
   p_smob->set_func = set_func;
   p_smob->show_func = show_func;
 
+  scm_set_smob_free (parameter_smob_tag, pascm_free_parameter_smob);
+  if (var_type_uses<std::string> (p_smob->type))
+    p_smob->value.stringval = new std::string;
+
   if (initial_value_arg_pos > 0)
     {
       if (gdbscm_is_procedure (initial_value_scm))
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index d948f4bafc5..b55a56c020d 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -71,16 +71,16 @@ static void step_1 (int, int, const char *);
    Arguments are separated by spaces.  Empty string (pointer to '\0')
    means no args.  */
 
-static char *inferior_args_scratch;
+static std::string inferior_args_scratch;
 
 /* Scratch area where the new cwd will be stored by 'set cwd'.  */
 
-static char *inferior_cwd_scratch;
+static std::string inferior_cwd_scratch;
 
 /* Scratch area where 'set inferior-tty' will store user-provided value.
    We'll immediate copy it into per-inferior storage.  */
 
-static char *inferior_io_terminal_scratch;
+static std::string inferior_io_terminal_scratch;
 
 /* Pid of our debugged inferior, or 0 if no inferior now.
    Since various parts of infrun.c test this to see whether there is a program
@@ -2021,7 +2021,6 @@ path_info (const char *args, int from_tty)
 static void
 path_command (const char *dirname, int from_tty)
 {
-  char *exec_path;
   const char *env;
 
   dont_repeat ();
@@ -2029,10 +2028,9 @@ path_command (const char *dirname, int from_tty)
   /* Can be null if path is not set.  */
   if (!env)
     env = "";
-  exec_path = xstrdup (env);
-  mod_path (dirname, &exec_path);
-  current_inferior ()->environment.set (path_var_name, exec_path);
-  xfree (exec_path);
+  std::string exec_path = env;
+  mod_path (dirname, exec_path);
+  current_inferior ()->environment.set (path_var_name, exec_path.c_str ());
   if (from_tty)
     path_info (NULL, from_tty);
 }
diff --git a/gdb/linux-thread-db.c b/gdb/linux-thread-db.c
index ac85ea39bba..54e68ccd0d7 100644
--- a/gdb/linux-thread-db.c
+++ b/gdb/linux-thread-db.c
@@ -110,7 +110,7 @@ class thread_db_target final : public target_ops
   gdb::byte_vector thread_info_to_thread_handle (struct thread_info *) override;
 };
 
-static char *libthread_db_search_path;
+static std::string libthread_db_search_path = LIBTHREAD_DB_SEARCH_PATH;
 
 /* Set to true if thread_db auto-loading is enabled
    by the "set auto-load libthread-db" command.  */
@@ -135,11 +135,8 @@ static void
 set_libthread_db_search_path (const char *ignored, int from_tty,
 			      struct cmd_list_element *c)
 {
-  if (*libthread_db_search_path == '\0')
-    {
-      xfree (libthread_db_search_path);
-      libthread_db_search_path = xstrdup (LIBTHREAD_DB_SEARCH_PATH);
-    }
+  if (libthread_db_search_path.empty ())
+    libthread_db_search_path = LIBTHREAD_DB_SEARCH_PATH;
 }
 
 /* If non-zero, print details of libthread_db processing.  */
@@ -941,7 +938,7 @@ try_thread_db_load_1 (struct thread_db_info *info)
 
   printf_unfiltered (_("[Thread debugging using libthread_db enabled]\n"));
 
-  if (*libthread_db_search_path || libthread_db_debug)
+  if (!libthread_db_search_path.empty () || libthread_db_debug)
     {
       struct ui_file *file;
       const char *library;
@@ -954,7 +951,7 @@ try_thread_db_load_1 (struct thread_db_info *info)
 	 disabled, still print it to gdb_stdout if debug output is
 	 enabled.  User visible output should not depend on debug
 	 settings.  */
-      file = *libthread_db_search_path != '\0' ? gdb_stdout : gdb_stdlog;
+      file = !libthread_db_search_path.empty () ? gdb_stdout : gdb_stdlog;
       fprintf_unfiltered (file,
 			  _("Using host libthread_db library \"%ps\".\n"),
 			  styled_string (file_name_style.style (), library));
@@ -1142,7 +1139,7 @@ thread_db_load_search (void)
   bool rc = false;
 
   std::vector<gdb::unique_xmalloc_ptr<char>> dir_vec
-    = dirnames_to_char_ptr_vec (libthread_db_search_path);
+    = dirnames_to_char_ptr_vec (libthread_db_search_path.c_str ());
 
   for (const gdb::unique_xmalloc_ptr<char> &this_dir_up : dir_vec)
     {
@@ -2005,8 +2002,6 @@ _initialize_thread_db ()
      and until there is a running inferior, we can't tell which
      libthread_db is the correct one to load.  */
 
-  libthread_db_search_path = xstrdup (LIBTHREAD_DB_SEARCH_PATH);
-
   add_setshow_optional_filename_cmd ("libthread-db-search-path",
 				     class_support,
 				     &libthread_db_search_path, _("\
diff --git a/gdb/main.c b/gdb/main.c
index 5761ce2bdbe..ca4ccc375dc 100644
--- a/gdb/main.c
+++ b/gdb/main.c
@@ -65,7 +65,7 @@ char *interpreter_p;
 int dbx_commands = 0;
 
 /* System root path, used to find libraries etc.  */
-char *gdb_sysroot = 0;
+std::string gdb_sysroot;
 
 /* GDB datadir, used to store data files.  */
 std::string gdb_datadir;
@@ -710,19 +710,14 @@ captured_main_1 (struct captured_main_args *context)
     perror_warning_with_name (_("error finding working directory"));
 
   /* Set the sysroot path.  */
-  gdb_sysroot
-    = xstrdup (relocate_gdb_directory (TARGET_SYSTEM_ROOT,
-				     TARGET_SYSTEM_ROOT_RELOCATABLE).c_str ());
+  gdb_sysroot = relocate_gdb_directory (TARGET_SYSTEM_ROOT,
+					TARGET_SYSTEM_ROOT_RELOCATABLE);
 
-  if (*gdb_sysroot == '\0')
-    {
-      xfree (gdb_sysroot);
-      gdb_sysroot = xstrdup (TARGET_SYSROOT_PREFIX);
-    }
+  if (gdb_sysroot.empty ())
+    gdb_sysroot = TARGET_SYSROOT_PREFIX;
 
   debug_file_directory
-    = xstrdup (relocate_gdb_directory (DEBUGDIR,
-				     DEBUGDIR_RELOCATABLE).c_str ());
+    = relocate_gdb_directory (DEBUGDIR, DEBUGDIR_RELOCATABLE);
 
   gdb_datadir = relocate_gdb_directory (GDB_DATADIR,
 					GDB_DATADIR_RELOCATABLE);
diff --git a/gdb/maint-test-options.c b/gdb/maint-test-options.c
index b773e759bb7..77a013d3b7a 100644
--- a/gdb/maint-test-options.c
+++ b/gdb/maint-test-options.c
@@ -133,17 +133,12 @@ struct test_options_opts
   const char *enum_opt = test_options_enum_values_xxx;
   unsigned int uint_opt = 0;
   int zuint_unl_opt = 0;
-  char *string_opt = nullptr;
+  std::string string_opt;
 
   test_options_opts () = default;
 
   DISABLE_COPY_AND_ASSIGN (test_options_opts);
 
-  ~test_options_opts ()
-  {
-    xfree (string_opt);
-  }
-
   /* Dump the options to FILE.  ARGS is the remainder unprocessed
      arguments.  */
   void dump (ui_file *file, const char *args) const
@@ -162,9 +157,7 @@ struct test_options_opts
 			(zuint_unl_opt == -1
 			 ? "unlimited"
 			 : plongest (zuint_unl_opt)),
-			(string_opt != nullptr
-			 ? string_opt
-			 : ""),
+			string_opt.c_str (),
 			args);
   }
 };
diff --git a/gdb/maint-test-settings.c b/gdb/maint-test-settings.c
index 0ce88905518..eea3ea99572 100644
--- a/gdb/maint-test-settings.c
+++ b/gdb/maint-test-settings.c
@@ -44,13 +44,13 @@ static unsigned int maintenance_test_settings_zuinteger;
 
 static int maintenance_test_settings_zuinteger_unlimited;
 
-static char *maintenance_test_settings_string;
+static std::string maintenance_test_settings_string;
 
-static char *maintenance_test_settings_string_noescape;
+static std::string maintenance_test_settings_string_noescape;
 
-static char *maintenance_test_settings_optional_filename;
+static std::string maintenance_test_settings_optional_filename;
 
-static char *maintenance_test_settings_filename;
+static std::string maintenance_test_settings_filename;
 
 /* Enum values for the "maintenance set/show test-settings boolean"
    commands.  */
diff --git a/gdb/mi/mi-cmd-env.c b/gdb/mi/mi-cmd-env.c
index 703c63251fd..f9685a515ad 100644
--- a/gdb/mi/mi-cmd-env.c
+++ b/gdb/mi/mi-cmd-env.c
@@ -93,7 +93,7 @@ mi_cmd_env_cd (const char *command, char **argv, int argc)
 }
 
 static void
-env_mod_path (const char *dirname, char **which_path)
+env_mod_path (const char *dirname, std::string &which_path)
 {
   if (dirname == 0 || dirname[0] == '\0')
     return;
@@ -109,7 +109,6 @@ void
 mi_cmd_env_path (const char *command, char **argv, int argc)
 {
   struct ui_out *uiout = current_uiout;
-  char *exec_path;
   const char *env;
   int reset = 0;
   int oind = 0;
@@ -152,11 +151,11 @@ mi_cmd_env_path (const char *command, char **argv, int argc)
   argv += oind;
   argc -= oind;
 
-
+  std::string exec_path;
   if (reset)
     {
       /* Reset implies resetting to original path first.  */
-      exec_path = xstrdup (orig_path);
+      exec_path = orig_path;
     }
   else
     {
@@ -166,14 +165,14 @@ mi_cmd_env_path (const char *command, char **argv, int argc)
       /* Can be null if path is not set.  */
       if (!env)
 	env = "";
-      exec_path = xstrdup (env);
+
+      exec_path = env;
     }
 
   for (i = argc - 1; i >= 0; --i)
-    env_mod_path (argv[i], &exec_path);
+    env_mod_path (argv[i], exec_path);
 
-  current_inferior ()->environment.set (path_var_name, exec_path);
-  xfree (exec_path);
+  current_inferior ()->environment.set (path_var_name, exec_path.c_str ());
   env = current_inferior ()->environment.get (path_var_name);
   uiout->field_string ("path", env);
 }
@@ -228,12 +227,11 @@ mi_cmd_env_dir (const char *command, char **argv, int argc)
   if (reset)
     {
       /* Reset means setting to default path first.  */
-      xfree (source_path);
       init_source_path ();
     }
 
   for (i = argc - 1; i >= 0; --i)
-    env_mod_path (argv[i], &source_path);
+    env_mod_path (argv[i], source_path);
 
   uiout->field_string ("source-path", source_path);
   forget_cached_source_info ();
diff --git a/gdb/proc-api.c b/gdb/proc-api.c
index 5f60eeeb129..40bde7893bd 100644
--- a/gdb/proc-api.c
+++ b/gdb/proc-api.c
@@ -50,14 +50,14 @@ struct trans {
 
 static bool  procfs_trace   = false;
 static FILE *procfs_file     = NULL;
-static char *procfs_filename;
+static std::string procfs_filename = "procfs_trace";
 
 static void
 prepare_to_trace (void)
 {
   if (procfs_trace)			/* if procfs tracing turned on */
     if (procfs_file == NULL)		/* if output file not yet open */
-      procfs_file = fopen (procfs_filename, "a");	/* open output file */
+      procfs_file = fopen (procfs_filename.c_str (), "a");	/* open output file */
 }
 
 static void
@@ -425,7 +425,6 @@ Show tracing for /proc api calls."), NULL,
 			   NULL, /* FIXME: i18n: */
 			   &setlist, &showlist);
 
-  procfs_filename = xstrdup ("procfs_trace");
   add_setshow_filename_cmd ("procfs-file", no_class, &procfs_filename, _("\
 Set filename for /proc tracefile."), _("\
 Show filename for /proc tracefile."), NULL,
diff --git a/gdb/python/py-param.c b/gdb/python/py-param.c
index 1dd716bba14..407db045d93 100644
--- a/gdb/python/py-param.c
+++ b/gdb/python/py-param.c
@@ -64,8 +64,9 @@ union parmpy_variable
   /* Hold an unsigned integer value, for uinteger.  */
   unsigned int uintval;
 
-  /* Hold a string, for the various string types.  */
-  char *stringval;
+  /* Hold a string, for the various string types.  The std::string is
+     new-ed.  */
+  std::string *stringval;
 
   /* Hold a string, for enums.  */
   const char *cstringval;
@@ -95,7 +96,6 @@ struct parmpy_object
 static setting
 make_setting (parmpy_object *s)
 {
-
   if (var_type_uses<bool> (s->type))
     return setting (s->type, &s->value.boolval);
   else if (var_type_uses<int> (s->type))
@@ -104,8 +104,8 @@ make_setting (parmpy_object *s)
     return setting (s->type, &s->value.autoboolval);
   else if (var_type_uses<unsigned int> (s->type))
     return setting (s->type, &s->value.uintval);
-  else if (var_type_uses<char *> (s->type))
-    return setting (s->type, &s->value.stringval);
+  else if (var_type_uses<std::string> (s->type))
+    return setting (s->type, s->value.stringval);
   else if (var_type_uses<const char *> (s->type))
     return setting (s->type, &s->value.cstringval);
   else
@@ -163,13 +163,7 @@ set_parameter_value (parmpy_object *self, PyObject *value)
 	  return -1;
 	}
       if (value == Py_None)
-	{
-	  xfree (self->value.stringval);
-	  if (self->type == var_optional_filename)
-	    self->value.stringval = xstrdup ("");
-	  else
-	    self->value.stringval = NULL;
-	}
+	self->value.stringval->clear ();
       else
 	{
 	  gdb::unique_xmalloc_ptr<char>
@@ -177,8 +171,7 @@ set_parameter_value (parmpy_object *self, PyObject *value)
 	  if (string == NULL)
 	    return -1;
 
-	  xfree (self->value.stringval);
-	  self->value.stringval = string.release ();
+	  *self->value.stringval = string.get ();
 	}
       break;
 
@@ -525,14 +518,14 @@ add_setshow_generic (int parmclass, enum command_class cmdclass,
 
     case var_string:
       commands = add_setshow_string_cmd (cmd_name.get (), cmdclass,
-					 &self->value.stringval, set_doc,
+					 self->value.stringval, set_doc,
 					 show_doc, help_doc, get_set_value,
 					 get_show_value, set_list, show_list);
       break;
 
     case var_string_noescape:
       commands = add_setshow_string_noescape_cmd (cmd_name.get (), cmdclass,
-						  &self->value.stringval,
+						  self->value.stringval,
 						  set_doc, show_doc, help_doc,
 						  get_set_value, get_show_value,
 						  set_list, show_list);
@@ -540,7 +533,7 @@ add_setshow_generic (int parmclass, enum command_class cmdclass,
 
     case var_optional_filename:
       commands = add_setshow_optional_filename_cmd (cmd_name.get (), cmdclass,
-						    &self->value.stringval,
+						    self->value.stringval,
 						    set_doc, show_doc, help_doc,
 						    get_set_value,
 						    get_show_value, set_list,
@@ -549,7 +542,7 @@ add_setshow_generic (int parmclass, enum command_class cmdclass,
 
     case var_filename:
       commands = add_setshow_filename_cmd (cmd_name.get (), cmdclass,
-					   &self->value.stringval, set_doc,
+					   self->value.stringval, set_doc,
 					   show_doc, help_doc, get_set_value,
 					   get_show_value, set_list, show_list);
       break;
@@ -732,6 +725,9 @@ parmpy_init (PyObject *self, PyObject *args, PyObject *kwds)
   obj->type = (enum var_types) parmclass;
   memset (&obj->value, 0, sizeof (obj->value));
 
+  if (var_type_uses<std::string> (obj->type))
+    obj->value.stringval = new std::string;
+
   gdb::unique_xmalloc_ptr<char> cmd_name
     = gdbpy_parse_command_name (name, &set_list, &setlist);
   if (cmd_name == nullptr)
@@ -764,7 +760,16 @@ parmpy_init (PyObject *self, PyObject *args, PyObject *kwds)
   return 0;
 }
 
-\f
+/* Deallocate function for a gdb.Parameter.  */
+
+static void
+parmpy_dealloc (PyObject *obj)
+{
+  parmpy_object *parm_obj = (parmpy_object *) obj;
+
+  if (var_type_uses<std::string> (parm_obj->type))
+    delete parm_obj->value.stringval;
+}
 
 /* Initialize the 'parameters' module.  */
 int
@@ -803,7 +808,7 @@ PyTypeObject parmpy_object_type =
   "gdb.Parameter",		  /*tp_name*/
   sizeof (parmpy_object),	  /*tp_basicsize*/
   0,				  /*tp_itemsize*/
-  0,				  /*tp_dealloc*/
+  parmpy_dealloc,		  /*tp_dealloc*/
   0,				  /*tp_print*/
   0,				  /*tp_getattr*/
   0,				  /*tp_setattr*/
diff --git a/gdb/python/python.c b/gdb/python/python.c
index a26c37352ff..fcd367fe2ec 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -460,13 +460,11 @@ gdbpy_parameter_value (const setting &var)
     case var_enum:
       {
 	const char *str;
-        if (var.type () == var_enum)
-          str = var.get<const char *> ();
-        else
-          str = var.get<char *> ();
+	if (var.type () == var_enum)
+	  str = var.get<const char *> ();
+	else
+	  str = var.get<std::string> ().c_str ();
 
-	if (str == nullptr)
-	  str = "";
 	return host_string_to_python_string (str).release ();
       }
 
diff --git a/gdb/remote-sim.c b/gdb/remote-sim.c
index 5bedc04011d..5bfce2a9a31 100644
--- a/gdb/remote-sim.c
+++ b/gdb/remote-sim.c
@@ -680,10 +680,9 @@ gdbsim_target_open (const char *args, int from_tty)
   int len;
   char *arg_buf;
   struct sim_inferior_data *sim_data;
-  const char *sysroot;
   SIM_DESC gdbsim_desc;
 
-  sysroot = gdb_sysroot;
+  const std::string &sysroot = gdb_sysroot;
   if (is_target_filename (sysroot))
     sysroot += strlen (TARGET_SYSROOT_PREFIX);
 
@@ -704,7 +703,7 @@ gdbsim_target_open (const char *args, int from_tty)
   len = (7 + 1			/* gdbsim */
 	 + strlen (" -E little")
 	 + strlen (" --architecture=xxxxxxxxxx")
-	 + strlen (" --sysroot=") + strlen (sysroot) +
+	 + strlen (" --sysroot=") + sysroot.length () +
 	 + (args ? strlen (args) : 0)
 	 + 50) /* slack */ ;
   arg_buf = (char *) alloca (len);
@@ -731,7 +730,7 @@ gdbsim_target_open (const char *args, int from_tty)
     }
   /* Pass along gdb's concept of the sysroot.  */
   strcat (arg_buf, " --sysroot=");
-  strcat (arg_buf, sysroot);
+  strcat (arg_buf, sysroot.c_str ());
   /* finally, any explicit args */
   if (args)
     {
diff --git a/gdb/remote.c b/gdb/remote.c
index 29b18c9427f..d5eb40ce578 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1004,7 +1004,7 @@ static const struct program_space_key<char, gdb::xfree_deleter<char>>
    remote exec-file commands.  While the remote exec-file setting is
    per-program-space, the set/show machinery uses this as the 
    location of the remote exec-file value.  */
-static char *remote_exec_file_var;
+static std::string remote_exec_file_var;
 
 /* The size to align memory write packets, when practical.  The protocol
    does not guarantee any alignment, and gdb will generate short
@@ -1355,8 +1355,8 @@ static void
 set_remote_exec_file (const char *ignored, int from_tty,
 		      struct cmd_list_element *c)
 {
-  gdb_assert (remote_exec_file_var != NULL);
-  set_pspace_remote_exec_file (current_program_space, remote_exec_file_var);
+  set_pspace_remote_exec_file (current_program_space,
+			       remote_exec_file_var.c_str ());
 }
 
 /* The "set/show remote exec-file" show command hook.  */
@@ -12628,7 +12628,7 @@ remote_target::filesystem_is_local ()
      this case we treat the remote filesystem as local if the
      sysroot is exactly TARGET_SYSROOT_PREFIX and if the stub
      does not support vFile:open.  */
-  if (strcmp (gdb_sysroot, TARGET_SYSROOT_PREFIX) == 0)
+  if (gdb_sysroot == TARGET_SYSROOT_PREFIX)
     {
       enum packet_support ps = packet_support (PACKET_vFile_open);
 
@@ -13256,7 +13256,7 @@ remote_target::download_tracepoint (struct bp_location *loc)
 		   "ignoring tp %d cond"), b->number);
     }
 
-  if (b->commands || *default_collect)
+  if (b->commands || !default_collect.empty ())
     {
       size_left = buf.size () - strlen (buf.data ());
 
diff --git a/gdb/serial.c b/gdb/serial.c
index 7d1bc535ed9..5ec79a11aac 100644
--- a/gdb/serial.c
+++ b/gdb/serial.c
@@ -38,7 +38,7 @@ static struct serial *scb_base;
 /* Non-NULL gives filename which contains a recording of the remote session,
    suitable for playback by gdbserver.  */
 
-static char *serial_logfile = NULL;
+static std::string serial_logfile;
 static struct ui_file *serial_logfp = NULL;
 
 static const struct serial_ops *serial_interface_lookup (const char *);
@@ -251,12 +251,12 @@ serial_open_ops_1 (const struct serial_ops *ops, const char *open_name)
   scb->next = scb_base;
   scb_base = scb;
 
-  if (serial_logfile != NULL)
+  if (!serial_logfile.empty ())
     {
       stdio_file_up file (new stdio_file ());
 
-      if (!file->open (serial_logfile, "w"))
-	perror_with_name (serial_logfile);
+      if (!file->open (serial_logfile.c_str (), "w"))
+	perror_with_name (serial_logfile.c_str ());
 
       serial_logfp = file.release ();
     }
diff --git a/gdb/solib.c b/gdb/solib.c
index e30affbb7e7..d97123b43bb 100644
--- a/gdb/solib.c
+++ b/gdb/solib.c
@@ -98,7 +98,7 @@ struct target_so_ops *current_target_so_ops;
 /* If non-empty, this is a search path for loading non-absolute shared library
    symbol files.  This takes precedence over the environment variables PATH
    and LD_LIBRARY_PATH.  */
-static char *solib_search_path = NULL;
+static std::string solib_search_path;
 static void
 show_solib_search_path (struct ui_file *file, int from_tty,
 			struct cmd_list_element *c, const char *value)
@@ -157,7 +157,7 @@ solib_find_1 (const char *in_pathname, int *fd, bool is_solib)
   int found_file = -1;
   gdb::unique_xmalloc_ptr<char> temp_pathname;
   const char *fskind = effective_target_file_system_kind ();
-  const char *sysroot = gdb_sysroot;
+  const char *sysroot = gdb_sysroot.c_str ();
   int prefix_len, orig_prefix_len;
 
   /* If the absolute prefix starts with "target:" but the filesystem
@@ -321,8 +321,8 @@ solib_find_1 (const char *in_pathname, int *fd, bool is_solib)
 
   /* If not found, and we're looking for a solib, search the
      solib_search_path (if any).  */
-  if (is_solib && found_file < 0 && solib_search_path != NULL)
-    found_file = openp (solib_search_path,
+  if (is_solib && found_file < 0 && !solib_search_path.empty ())
+    found_file = openp (solib_search_path.c_str (),
 			OPF_TRY_CWD_FIRST | OPF_RETURN_REALPATH,
 			in_pathname, O_RDONLY | O_BINARY, &temp_pathname);
 
@@ -330,8 +330,8 @@ solib_find_1 (const char *in_pathname, int *fd, bool is_solib)
      solib_search_path (if any) for the basename only (ignoring the
      path).  This is to allow reading solibs from a path that differs
      from the opened path.  */
-  if (is_solib && found_file < 0 && solib_search_path != NULL)
-    found_file = openp (solib_search_path,
+  if (is_solib && found_file < 0 && !solib_search_path.empty ())
+    found_file = openp (solib_search_path.c_str (),
 			OPF_TRY_CWD_FIRST | OPF_RETURN_REALPATH,
 			target_lbasename (fskind, in_pathname),
 			O_RDONLY | O_BINARY, &temp_pathname);
@@ -380,7 +380,7 @@ exec_file_find (const char *in_pathname, int *fd)
   if (in_pathname == NULL)
     return NULL;
 
-  if (*gdb_sysroot != '\0' && IS_TARGET_ABSOLUTE_PATH (fskind, in_pathname))
+  if (!gdb_sysroot.empty () && IS_TARGET_ABSOLUTE_PATH (fskind, in_pathname))
     {
       result = solib_find_1 (in_pathname, fd, false);
 
@@ -1396,18 +1396,18 @@ gdb_sysroot_changed (const char *ignored, int from_tty,
   const char *old_prefix = "remote:";
   const char *new_prefix = TARGET_SYSROOT_PREFIX;
 
-  if (startswith (gdb_sysroot, old_prefix))
+  if (startswith (gdb_sysroot.c_str (), old_prefix))
     {
       static bool warning_issued = false;
 
       gdb_assert (strlen (old_prefix) == strlen (new_prefix));
-      memcpy (gdb_sysroot, new_prefix, strlen (new_prefix));
+      gdb_sysroot = new_prefix + gdb_sysroot.substr (strlen (old_prefix));
 
       if (!warning_issued)
 	{
 	  warning (_("\"%s\" is deprecated, use \"%s\" instead."),
 		   old_prefix, new_prefix);
-	  warning (_("sysroot set to \"%s\"."), gdb_sysroot);
+	  warning (_("sysroot set to \"%s\"."), gdb_sysroot.c_str ());
 
 	  warning_issued = true;
 	}
diff --git a/gdb/source.c b/gdb/source.c
index eec6e77eaa3..15d3b5b9bb1 100644
--- a/gdb/source.c
+++ b/gdb/source.c
@@ -57,7 +57,7 @@
 /* Path of directories to search for source files.
    Same format as the PATH environment variable's value.  */
 
-char *source_path;
+std::string source_path;
 
 /* Support for source path substitution commands.  */
 
@@ -379,17 +379,15 @@ set_directories_command (const char *args,
 {
   /* This is the value that was set.
      It needs to be processed to maintain $cdir:$cwd and remove dups.  */
-  char *set_path = source_path;
+  std::string set_path = source_path;
 
   /* We preserve the invariant that $cdir:$cwd begins life at the end of
      the list by calling init_source_path.  If they appear earlier in
      SET_PATH then mod_path will move them appropriately.
      mod_path will also remove duplicates.  */
   init_source_path ();
-  if (*set_path != '\0')
-    mod_path (set_path, &source_path);
-
-  xfree (set_path);
+  if (!set_path.empty ())
+    mod_path (set_path.c_str (), source_path);
 }
 
 /* Print the list of source directories.
@@ -400,7 +398,7 @@ static void
 show_directories_1 (char *ignore, int from_tty)
 {
   puts_filtered ("Source directories searched: ");
-  puts_filtered (source_path);
+  puts_filtered (source_path.c_str ());
   puts_filtered ("\n");
 }
 
@@ -451,10 +449,7 @@ forget_cached_source_info (void)
 void
 init_source_path (void)
 {
-  char buf[20];
-
-  xsnprintf (buf, sizeof (buf), "$cdir%c$cwd", DIRNAME_SEPARATOR);
-  source_path = xstrdup (buf);
+  source_path = string_printf ("$cdir%c$cwd", DIRNAME_SEPARATOR);
   forget_cached_source_info ();
 }
 
@@ -470,20 +465,20 @@ directory_command (const char *dirname, int from_tty)
     {
       if (!from_tty || query (_("Reinitialize source path to empty? ")))
 	{
-	  xfree (source_path);
 	  init_source_path ();
 	  value_changed = true;
 	}
     }
   else
     {
-      mod_path (dirname, &source_path);
+      mod_path (dirname, source_path);
       forget_cached_source_info ();
       value_changed = true;
     }
   if (value_changed)
     {
-      gdb::observers::command_param_changed.notify ("directories", source_path);
+      gdb::observers::command_param_changed.notify ("directories",
+						    source_path.c_str ());
       if (from_tty)
 	show_directories_1 ((char *) 0, from_tty);
     }
@@ -495,13 +490,13 @@ directory_command (const char *dirname, int from_tty)
 void
 directory_switch (const char *dirname, int from_tty)
 {
-  add_path (dirname, &source_path, 0);
+  add_path (dirname, source_path, 0);
 }
 
 /* Add zero or more directories to the front of an arbitrary path.  */
 
 void
-mod_path (const char *dirname, char **which_path)
+mod_path (const char *dirname, std::string &which_path)
 {
   add_path (dirname, which_path, 1);
 }
@@ -689,6 +684,17 @@ add_path (const char *dirname, char **which_path, int parse_separators)
     }
 }
 
+/* add_path would need to be re-written to work on an std::string, but this is
+   not trivial.  Hence this overload which copies to a `char *` and back.  */
+
+void
+add_path (const char *dirname, std::string &which_path, int parse_separators)
+{
+  char *which_path_copy = xstrdup (which_path.data ());
+  add_path (dirname, &which_path_copy, parse_separators);
+  which_path = which_path_copy;
+  xfree (which_path_copy);
+}
 
 static void
 info_source_command (const char *ignore, int from_tty)
@@ -967,7 +973,7 @@ source_full_path_of (const char *filename,
 {
   int fd;
 
-  fd = openp (source_path,
+  fd = openp (source_path.c_str (),
 	      OPF_TRY_CWD_FIRST | OPF_SEARCH_IN_PATH | OPF_RETURN_REALPATH,
 	      filename, O_RDONLY, full_pathname);
   if (fd < 0)
@@ -1058,7 +1064,8 @@ find_and_open_source (const char *filename,
 		      const char *dirname,
 		      gdb::unique_xmalloc_ptr<char> *fullname)
 {
-  char *path = source_path;
+  const char *path = source_path.c_str ();
+  std::string expanded_path_holder;
   const char *p;
   int result;
 
@@ -1106,19 +1113,22 @@ find_and_open_source (const char *filename,
       /* Replace a path entry of $cdir with the compilation directory
 	 name.  */
 #define	cdir_len	5
-      p = strstr (source_path, "$cdir");
+      p = strstr (source_path.c_str (), "$cdir");
       if (p && (p == path || p[-1] == DIRNAME_SEPARATOR)
 	  && (p[cdir_len] == DIRNAME_SEPARATOR || p[cdir_len] == '\0'))
 	{
-	  int len;
-
-	  path = (char *)
-	    alloca (strlen (source_path) + 1 + strlen (dirname) + 1);
-	  len = p - source_path;
-	  strncpy (path, source_path, len);	/* Before $cdir */
-	  strcpy (path + len, dirname);		/* new stuff */
-	  strcat (path + len, source_path + len + cdir_len);	/* After
-								   $cdir */
+	  int len = p - source_path.c_str ();
+
+	  /* Before $cdir */
+	  expanded_path_holder = source_path.substr (0, len);
+
+	  /* new stuff */
+	  expanded_path_holder += dirname;
+
+	  /* After $cdir */
+	  expanded_path_holder += source_path.c_str () + len + cdir_len;
+
+	  path = expanded_path_holder.c_str ();
 	}
     }
 
diff --git a/gdb/source.h b/gdb/source.h
index 6c383efbbb2..2b9e8f38c03 100644
--- a/gdb/source.h
+++ b/gdb/source.h
@@ -39,13 +39,14 @@ extern int openp (const char *, openp_flags, const char *, int,
 
 extern int source_full_path_of (const char *, gdb::unique_xmalloc_ptr<char> *);
 
-extern void mod_path (const char *, char **);
+extern void mod_path (const char *, std::string &);
 
 extern void add_path (const char *, char **, int);
+extern void add_path (const char *, std::string &, int);
 
 extern void directory_switch (const char *, int);
 
-extern char *source_path;
+extern std::string source_path;
 
 extern void init_source_path (void);
 
diff --git a/gdb/stack.c b/gdb/stack.c
index 7c4cf9491cc..8b29ab505e5 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -2426,12 +2426,7 @@ print_frame_local_vars (struct frame_info *frame,
 struct info_print_options
 {
   bool quiet = false;
-  char *type_regexp = nullptr;
-
-  ~info_print_options ()
-  {
-    xfree (type_regexp);
-  }
+  std::string type_regexp;
 };
 
 /* The options used by the 'info locals' and 'info args' commands.  */
@@ -2490,9 +2485,11 @@ info_locals_command (const char *args, int from_tty)
   if (args != nullptr && *args == '\0')
     args = nullptr;
 
-  print_frame_local_vars (get_selected_frame (_("No frame selected.")),
-			  opts.quiet, args, opts.type_regexp,
-			  0, gdb_stdout);
+  print_frame_local_vars
+    (get_selected_frame (_("No frame selected.")),
+     opts.quiet, args,
+     opts.type_regexp.empty () ? nullptr : opts.type_regexp.c_str (),
+     0, gdb_stdout);
 }
 
 /* Iterate over all the argument variables in block B.  */
@@ -2601,8 +2598,11 @@ info_args_command (const char *args, int from_tty)
   if (args != nullptr && *args == '\0')
     args = nullptr;
 
-  print_frame_arg_vars (get_selected_frame (_("No frame selected.")),
-			opts.quiet, args, opts.type_regexp, gdb_stdout);
+  print_frame_arg_vars
+    (get_selected_frame (_("No frame selected.")),
+     opts.quiet, args,
+     opts.type_regexp.empty () ? nullptr : opts.type_regexp.c_str (),
+     gdb_stdout);
 }
 \f
 /* Return the symbol-block in which the selected frame is executing.
diff --git a/gdb/symfile.c b/gdb/symfile.c
index 0eb48d04d5b..9e5c2d48881 100644
--- a/gdb/symfile.c
+++ b/gdb/symfile.c
@@ -1349,7 +1349,7 @@ separate_debug_file_exists (const std::string &name, unsigned long crc,
   return 1;
 }
 
-char *debug_file_directory = NULL;
+std::string debug_file_directory;
 static void
 show_debug_file_directory (struct ui_file *file, int from_tty,
 			   struct cmd_list_element *c, const char *value)
@@ -1406,8 +1406,9 @@ find_separate_debug_file (const char *dir,
   bool target_prefix = startswith (dir, "target:");
   const char *dir_notarget = target_prefix ? dir + strlen ("target:") : dir;
   std::vector<gdb::unique_xmalloc_ptr<char>> debugdir_vec
-    = dirnames_to_char_ptr_vec (debug_file_directory);
-  gdb::unique_xmalloc_ptr<char> canon_sysroot = gdb_realpath (gdb_sysroot);
+    = dirnames_to_char_ptr_vec (debug_file_directory.c_str ());
+  gdb::unique_xmalloc_ptr<char> canon_sysroot
+    = gdb_realpath (gdb_sysroot.c_str ());
 
  /* MS-Windows/MS-DOS don't allow colons in file names; we must
     convert the drive letter into a one-letter directory, so that the
@@ -1448,7 +1449,7 @@ find_separate_debug_file (const char *dir,
 	  if (canon_sysroot.get () != NULL)
 	    base_path = child_path (canon_sysroot.get (), canon_dir);
 	  else
-	    base_path = child_path (gdb_sysroot, canon_dir);
+	    base_path = child_path (gdb_sysroot.c_str (), canon_dir);
 	}
       if (base_path != NULL)
 	{
@@ -2654,7 +2655,7 @@ add_filename_language (const char *ext, enum language lang)
   filename_language_table.emplace_back (ext, lang);
 }
 
-static char *ext_args;
+static std::string ext_args;
 static void
 show_ext_args (struct ui_file *file, int from_tty,
 	       struct cmd_list_element *c, const char *value)
@@ -2669,48 +2670,48 @@ static void
 set_ext_lang_command (const char *args,
 		      int from_tty, struct cmd_list_element *e)
 {
-  char *cp = ext_args;
-  enum language lang;
+  const char *begin = ext_args.c_str ();
+  const char *end = ext_args.c_str ();
 
   /* First arg is filename extension, starting with '.'  */
-  if (*cp != '.')
-    error (_("'%s': Filename extension must begin with '.'"), ext_args);
+  if (*end != '.')
+    error (_("'%s': Filename extension must begin with '.'"), ext_args.c_str ());
 
   /* Find end of first arg.  */
-  while (*cp && !isspace (*cp))
-    cp++;
+  while (*end != '\0' && !isspace (*end))
+    end++;
 
-  if (*cp == '\0')
+  if (*end == '\0')
     error (_("'%s': two arguments required -- "
 	     "filename extension and language"),
-	   ext_args);
+	   ext_args.c_str ());
 
-  /* Null-terminate first arg.  */
-  *cp++ = '\0';
+  /* Extract first arg, the extension.  */
+  std::string extension = ext_args.substr (0, end - begin);
 
   /* Find beginning of second arg, which should be a source language.  */
-  cp = skip_spaces (cp);
+  begin = skip_spaces (end);
 
-  if (*cp == '\0')
+  if (*begin == '\0')
     error (_("'%s': two arguments required -- "
 	     "filename extension and language"),
-	   ext_args);
+	   ext_args.c_str ());
 
   /* Lookup the language from among those we know.  */
-  lang = language_enum (cp);
+  language lang = language_enum (begin);
 
   auto it = filename_language_table.begin ();
   /* Now lookup the filename extension: do we already know it?  */
   for (; it != filename_language_table.end (); it++)
     {
-      if (it->ext == ext_args)
+      if (it->ext == extension)
 	break;
     }
 
   if (it == filename_language_table.end ())
     {
       /* New file extension.  */
-      add_filename_language (ext_args, lang);
+      add_filename_language (extension.data (), lang);
     }
   else
     {
@@ -3784,8 +3785,7 @@ test_set_ext_lang_command ()
   SELF_CHECK (lang == language_unknown);
 
   /* Test adding a new extension using the CLI command.  */
-  auto args_holder = make_unique_xstrdup (".hello rust");
-  ext_args = args_holder.get ();
+  ext_args = ".hello rust";
   set_ext_lang_command (NULL, 1, NULL);
 
   lang = deduce_language_from_filename ("cake.hello");
@@ -3793,8 +3793,7 @@ test_set_ext_lang_command ()
 
   /* Test overriding an existing extension using the CLI command.  */
   int size_before = filename_language_table.size ();
-  args_holder.reset (xstrdup (".hello pascal"));
-  ext_args = args_holder.get ();
+  ext_args = ".hello pascal";
   set_ext_lang_command (NULL, 1, NULL);
   int size_after = filename_language_table.size ();
 
diff --git a/gdb/symtab.c b/gdb/symtab.c
index a30d900cf8d..f7aa806f90a 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -5146,12 +5146,7 @@ struct info_vars_funcs_options
 {
   bool quiet = false;
   bool exclude_minsyms = false;
-  char *type_regexp = nullptr;
-
-  ~info_vars_funcs_options ()
-  {
-    xfree (type_regexp);
-  }
+  std::string type_regexp;
 };
 
 /* The options used by the 'info variables' and 'info functions'
@@ -5174,8 +5169,7 @@ static const gdb::option::option_def info_vars_funcs_options_defs[] = {
 
   gdb::option::string_option_def<info_vars_funcs_options> {
     "t",
-    [] (info_vars_funcs_options *opt) { return &opt->type_regexp;
-  },
+    [] (info_vars_funcs_options *opt) { return &opt->type_regexp; },
     nullptr, /* show_cmd_cb */
     nullptr /* set_doc */
   }
@@ -5219,8 +5213,10 @@ info_variables_command (const char *args, int from_tty)
   if (args != nullptr && *args == '\0')
     args = nullptr;
 
-  symtab_symbol_info (opts.quiet, opts.exclude_minsyms, args, VARIABLES_DOMAIN,
-		      opts.type_regexp, from_tty);
+  symtab_symbol_info
+    (opts.quiet, opts.exclude_minsyms, args, VARIABLES_DOMAIN,
+     opts.type_regexp.empty () ? nullptr : opts.type_regexp.c_str (),
+     from_tty);
 }
 
 /* Implement the 'info functions' command.  */
@@ -5236,8 +5232,10 @@ info_functions_command (const char *args, int from_tty)
   if (args != nullptr && *args == '\0')
     args = nullptr;
 
-  symtab_symbol_info (opts.quiet, opts.exclude_minsyms, args,
-		      FUNCTIONS_DOMAIN, opts.type_regexp, from_tty);
+  symtab_symbol_info
+    (opts.quiet, opts.exclude_minsyms, args, FUNCTIONS_DOMAIN,
+     opts.type_regexp.empty () ? nullptr : opts.type_regexp.c_str (),
+     from_tty);
 }
 
 /* Holds the -q option for the 'info types' command.  */
@@ -6749,14 +6747,8 @@ info_module_subcommand (bool quiet, const char *module_regexp,
 struct info_modules_var_func_options
 {
   bool quiet = false;
-  char *type_regexp = nullptr;
-  char *module_regexp = nullptr;
-
-  ~info_modules_var_func_options ()
-  {
-    xfree (type_regexp);
-    xfree (module_regexp);
-  }
+  std::string type_regexp;
+  std::string module_regexp;
 };
 
 /* The options used by 'info module variables' and 'info module functions'
@@ -6806,8 +6798,11 @@ info_module_functions_command (const char *args, int from_tty)
   if (args != nullptr && *args == '\0')
     args = nullptr;
 
-  info_module_subcommand (opts.quiet, opts.module_regexp, args,
-			  opts.type_regexp, FUNCTIONS_DOMAIN);
+  info_module_subcommand
+    (opts.quiet,
+     opts.module_regexp.empty () ? nullptr : opts.module_regexp.c_str (), args,
+     opts.type_regexp.empty () ? nullptr : opts.type_regexp.c_str (),
+     FUNCTIONS_DOMAIN);
 }
 
 /* Implements the 'info module variables' command.  */
@@ -6822,8 +6817,11 @@ info_module_variables_command (const char *args, int from_tty)
   if (args != nullptr && *args == '\0')
     args = nullptr;
 
-  info_module_subcommand (opts.quiet, opts.module_regexp, args,
-			  opts.type_regexp, VARIABLES_DOMAIN);
+  info_module_subcommand
+    (opts.quiet,
+     opts.module_regexp.empty () ? nullptr : opts.module_regexp.c_str (), args,
+     opts.type_regexp.empty () ? nullptr : opts.type_regexp.c_str (),
+     VARIABLES_DOMAIN);
 }
 
 /* Command completer for 'info module ...' sub-commands.  */
diff --git a/gdb/target-descriptions.c b/gdb/target-descriptions.c
index f94ace756d4..f5ec617bb2e 100644
--- a/gdb/target-descriptions.c
+++ b/gdb/target-descriptions.c
@@ -510,7 +510,7 @@ target_desc_info_free (struct target_desc_info *tdesc_info)
 
 /* The string manipulated by the "set tdesc filename ..." command.  */
 
-static char *tdesc_filename_cmd_string;
+static std::string tdesc_filename_cmd_string;
 
 /* Fetch the current target's description, and switch the current
    architecture to one which incorporates that description.  */
diff --git a/gdb/top.c b/gdb/top.c
index 7e95ed3969c..43ec0f8020c 100644
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -84,8 +84,6 @@
 
 extern void initialize_all_files (void);
 
-static bool history_filename_empty (void);
-
 #define PROMPT(X) the_prompts.prompt_stack[the_prompts.top + X].prompt
 #define PREFIX(X) the_prompts.prompt_stack[the_prompts.top + X].prefix
 #define SUFFIX(X) the_prompts.prompt_stack[the_prompts.top + X].suffix
@@ -923,12 +921,16 @@ static bool command_editing_p;
    variable must be set to something sensible.  */
 static bool write_history_p;
 
+/* The name of the file in which GDB history will be written.  If this is
+   set to NULL, of the empty string then history will not be written.  */
+static std::string history_filename;
+
 /* Implement 'show history save'.  */
 static void
 show_write_history_p (struct ui_file *file, int from_tty,
 		      struct cmd_list_element *c, const char *value)
 {
-  if (!write_history_p || !history_filename_empty ())
+  if (!write_history_p || !history_filename.empty ())
     fprintf_filtered (file, _("Saving of the history record on exit is %s.\n"),
 		      value);
   else
@@ -962,24 +964,12 @@ show_history_remove_duplicates (struct ui_file *file, int from_tty,
 		    value);
 }
 
-/* The name of the file in which GDB history will be written.  If this is
-   set to NULL, of the empty string then history will not be written.  */
-static char *history_filename;
-
-/* Return true if the history_filename is either NULL or the empty string,
-   indicating that we should not try to read, nor write out the history.  */
-static bool
-history_filename_empty (void)
-{
-  return (history_filename == nullptr || *history_filename == '\0');
-}
-
 /* Implement 'show history filename'.  */
 static void
 show_history_filename (struct ui_file *file, int from_tty,
 		       struct cmd_list_element *c, const char *value)
 {
-  if (!history_filename_empty ())
+  if (!history_filename.empty ())
     fprintf_filtered (file, _("The filename in which to record "
 			      "the command history is \"%ps\".\n"),
 		      styled_string (file_name_style.style (), value));
@@ -1246,14 +1236,15 @@ gdb_safe_append_history (void)
   int ret, saved_errno;
 
   std::string local_history_filename
-    = string_printf ("%s-gdb%ld~", history_filename, (long) getpid ());
+    = string_printf ("%s-gdb%ld~", history_filename.c_str (), (long) getpid ());
 
-  ret = rename (history_filename, local_history_filename.c_str ());
+  ret = rename (history_filename.c_str (), local_history_filename.c_str ());
   saved_errno = errno;
   if (ret < 0 && saved_errno != ENOENT)
     {
       warning (_("Could not rename %ps to %ps: %s"),
-	       styled_string (file_name_style.style (), history_filename),
+	       styled_string (file_name_style.style (),
+			      history_filename.c_str ()),
 	       styled_string (file_name_style.style (),
 			      local_history_filename.c_str ()),
 	       safe_strerror (saved_errno));
@@ -1281,11 +1272,11 @@ gdb_safe_append_history (void)
 				   history_max_entries);
 	}
 
-      ret = rename (local_history_filename.c_str (), history_filename);
+      ret = rename (local_history_filename.c_str (), history_filename.c_str ());
       saved_errno = errno;
       if (ret < 0 && saved_errno != EEXIST)
 	warning (_("Could not rename %s to %s: %s"),
-		 local_history_filename.c_str (), history_filename,
+		 local_history_filename.c_str (), history_filename.c_str (),
 		 safe_strerror (saved_errno));
     }
 }
@@ -1658,12 +1649,12 @@ tree, and GDB will still find it.)\n\
 
 /* The current top level prompt, settable with "set prompt", and/or
    with the python `gdb.prompt_hook' hook.  */
-static char *top_prompt;
+static std::string top_prompt;
 
 /* Access method for the GDB prompt string.  */
 
-char *
-get_prompt (void)
+const std::string &
+get_prompt ()
 {
   return top_prompt;
 }
@@ -1673,10 +1664,7 @@ get_prompt (void)
 void
 set_prompt (const char *s)
 {
-  char *p = xstrdup (s);
-
-  xfree (top_prompt);
-  top_prompt = p;
+  top_prompt = s;
 }
 \f
 
@@ -1816,7 +1804,7 @@ quit_force (int *exit_arg, int from_tty)
   /* Save the history information if it is appropriate to do so.  */
   try
     {
-      if (write_history_p && history_filename)
+      if (write_history_p && !history_filename.empty ())
 	{
 	  int save = 0;
 
@@ -2064,27 +2052,8 @@ init_history (void)
 
   set_readline_history_size (history_size_setshow_var);
 
-  tmpenv = getenv ("GDBHISTFILE");
-  if (tmpenv != nullptr)
-    history_filename = xstrdup (tmpenv);
-  else if (history_filename == nullptr)
-    {
-      /* We include the current directory so that if the user changes
-	 directories the file written will be the same as the one
-	 that was read.  */
-#ifdef __MSDOS__
-      /* No leading dots in file names are allowed on MSDOS.  */
-      const char *fname = "_gdb_history";
-#else
-      const char *fname = ".gdb_history";
-#endif
-
-      gdb::unique_xmalloc_ptr<char> temp (gdb_abspath (fname));
-      history_filename = temp.release ();
-    }
-
-  if (!history_filename_empty ())
-    read_history (history_filename);
+  if (!history_filename.empty ())
+    read_history (history_filename.c_str ());
 }
 
 static void
@@ -2134,21 +2103,20 @@ show_exec_done_display_p (struct ui_file *file, int from_tty,
    Extension languages, for example Python's gdb.parameter API, will read
    the value directory from this variable, so we must ensure that this
    always contains the correct value.  */
-static char *staged_gdb_datadir;
+static std::string staged_gdb_datadir;
 
 /* "set" command for the gdb_datadir configuration variable.  */
 
 static void
 set_gdb_datadir (const char *args, int from_tty, struct cmd_list_element *c)
 {
-  set_gdb_data_directory (staged_gdb_datadir);
+  set_gdb_data_directory (staged_gdb_datadir.c_str ());
 
   /* SET_GDB_DATA_DIRECTORY will resolve relative paths in
      STAGED_GDB_DATADIR, so we now copy the value from GDB_DATADIR
      back into STAGED_GDB_DATADIR so the extension languages can read the
      correct value.  */
-  free (staged_gdb_datadir);
-  staged_gdb_datadir = strdup (gdb_datadir.c_str ());
+  staged_gdb_datadir = gdb_datadir;
 
   gdb::observers::gdb_datadir_changed.notify ();
 }
@@ -2173,12 +2141,13 @@ set_history_filename (const char *args,
   /* We include the current directory so that if the user changes
      directories the file written will be the same as the one
      that was read.  */
-  if (!history_filename_empty () && !IS_ABSOLUTE_PATH (history_filename))
+  if (!history_filename.empty ()
+      && !IS_ABSOLUTE_PATH (history_filename.c_str ()))
     {
-      gdb::unique_xmalloc_ptr<char> temp (gdb_abspath (history_filename));
+      gdb::unique_xmalloc_ptr<char> temp
+	(gdb_abspath (history_filename.c_str ()));
 
-      xfree (history_filename);
-      history_filename = temp.release ();
+      history_filename = temp.get ();
     }
 }
 
@@ -2344,7 +2313,7 @@ When set, GDB uses the specified path to search for data files."),
 			   &setlist,
 			    &showlist);
   /* Prime the initial value for data-directory.  */
-  staged_gdb_datadir = strdup (gdb_datadir.c_str ());
+  staged_gdb_datadir = gdb_datadir;
 
   add_setshow_auto_boolean_cmd ("interactive-mode", class_support,
 				&interactive_mode, _("\
@@ -2430,3 +2399,28 @@ gdb_init ()
   /* Create $_gdb_major and $_gdb_minor convenience variables.  */
   init_gdb_version_vars ();
 }
+
+void _initialize_top ();
+void
+_initialize_top ()
+{
+  /* Determine a default value for the history filename.  */
+  const char *tmpenv = getenv ("GDBHISTFILE");
+  if (tmpenv != nullptr)
+    history_filename = tmpenv;
+  else
+    {
+      /* We include the current directory so that if the user changes
+	 directories the file written will be the same as the one
+	 that was read.  */
+#ifdef __MSDOS__
+      /* No leading dots in file names are allowed on MSDOS.  */
+      const char *fname = "_gdb_history";
+#else
+      const char *fname = ".gdb_history";
+#endif
+
+      gdb::unique_xmalloc_ptr<char> temp (gdb_abspath (fname));
+      history_filename = temp.get ();
+    }
+}
diff --git a/gdb/top.h b/gdb/top.h
index ef88ca024e4..f8a5c39bbdf 100644
--- a/gdb/top.h
+++ b/gdb/top.h
@@ -261,7 +261,7 @@ extern scoped_value_mark prepare_execute_command (void);
 
 /* This function returns a pointer to the string that is used
    by gdb for its command prompt.  */
-extern char *get_prompt (void);
+extern const std::string &get_prompt ();
 
 /* This function returns a pointer to the string that is used
    by gdb for its command prompt.  */
diff --git a/gdb/tracepoint.c b/gdb/tracepoint.c
index 06cf9cad29a..3997d211182 100644
--- a/gdb/tracepoint.c
+++ b/gdb/tracepoint.c
@@ -130,7 +130,7 @@ static traceframe_info_up current_traceframe_info;
 static struct cmd_list_element *tfindlist;
 
 /* List of expressions to collect by default at each tracepoint hit.  */
-char *default_collect;
+std::string default_collect;
 
 static bool disconnected_tracing;
 
@@ -146,15 +146,15 @@ static int trace_buffer_size = -1;
 
 /* Textual notes applying to the current and/or future trace runs.  */
 
-static char *trace_user = NULL;
+static std::string trace_user;
 
 /* Textual notes applying to the current and/or future trace runs.  */
 
-static char *trace_notes = NULL;
+static std::string trace_notes;
 
 /* Textual notes applying to the stopping of a trace.  */
 
-static char *trace_stop_notes = NULL;
+static std::string trace_stop_notes;
 
 /* support routines */
 
@@ -1688,10 +1688,11 @@ start_tracing (const char *notes)
   target_set_trace_buffer_size (trace_buffer_size);
 
   if (!notes)
-    notes = trace_notes;
-  ret = target_set_trace_notes (trace_user, notes, NULL);
+    notes = trace_notes.c_str ();
 
-  if (!ret && (trace_user || notes))
+  ret = target_set_trace_notes (trace_user.c_str (), notes, NULL);
+
+  if (!ret && (!trace_user.empty () || notes))
     warning (_("Target does not support trace user/notes, info ignored"));
 
   /* Now insert traps and begin collecting data.  */
@@ -1764,7 +1765,8 @@ stop_tracing (const char *note)
     }
 
   if (!note)
-    note = trace_stop_notes;
+    note = trace_stop_notes.c_str ();
+
   ret = target_set_trace_notes (NULL, NULL, note);
 
   if (!ret && note)
@@ -2804,10 +2806,10 @@ all_tracepoint_actions (struct breakpoint *t)
      validation is per-tracepoint (local var "xyz" might be valid for
      one tracepoint and not another, etc), we make up the action on
      the fly, and don't cache it.  */
-  if (*default_collect)
+  if (!default_collect.empty ())
     {
       gdb::unique_xmalloc_ptr<char> default_collect_line
-	(xstrprintf ("collect %s", default_collect));
+	(xstrprintf ("collect %s", default_collect.c_str ()));
 
       validate_actionline (default_collect_line.get (), t);
       actions.reset (new struct command_line (simple_control,
@@ -2896,7 +2898,7 @@ set_trace_user (const char *args, int from_tty,
 {
   int ret;
 
-  ret = target_set_trace_notes (trace_user, NULL, NULL);
+  ret = target_set_trace_notes (trace_user.c_str (), NULL, NULL);
 
   if (!ret)
     warning (_("Target does not support trace notes, user ignored"));
@@ -2908,7 +2910,7 @@ set_trace_notes (const char *args, int from_tty,
 {
   int ret;
 
-  ret = target_set_trace_notes (NULL, trace_notes, NULL);
+  ret = target_set_trace_notes (NULL, trace_notes.c_str (), NULL);
 
   if (!ret)
     warning (_("Target does not support trace notes, note ignored"));
@@ -2920,7 +2922,7 @@ set_trace_stop_notes (const char *args, int from_tty,
 {
   int ret;
 
-  ret = target_set_trace_notes (NULL, NULL, trace_stop_notes);
+  ret = target_set_trace_notes (NULL, NULL, trace_stop_notes.c_str ());
 
   if (!ret)
     warning (_("Target does not support trace notes, stop note ignored"));
@@ -4137,7 +4139,6 @@ Tracepoint actions may include collecting of specified data,\n\
 single-stepping, or enabling/disabling other tracepoints,\n\
 depending on target's capabilities."));
 
-  default_collect = xstrdup ("");
   add_setshow_string_cmd ("default-collect", class_trace,
 			  &default_collect, _("\
 Set the list of expressions to collect by default."), _("\
diff --git a/gdb/tracepoint.h b/gdb/tracepoint.h
index b32dd61fdfc..a66226f93f1 100644
--- a/gdb/tracepoint.h
+++ b/gdb/tracepoint.h
@@ -159,7 +159,7 @@ struct trace_status
 
 struct trace_status *current_trace_status (void);
 
-extern char *default_collect;
+extern std::string default_collect;
 
 extern int trace_regblock_size;
 
-- 
2.33.0


^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH v4 3/4] gdb: Have setter and getter callbacks for settings
  2021-09-29 21:50 [PATCH v4 0/4] Refactor cmd_list_element.var Lancelot SIX
  2021-09-29 21:50 ` [PATCH v4 1/4] gdb: Introduce setting construct within cmd_list_element Lancelot SIX
  2021-09-29 21:50 ` [PATCH v4 2/4] gdb: make string-like set show commands use std::string variable Lancelot SIX
@ 2021-09-29 21:50 ` Lancelot SIX
  2021-09-30 12:42   ` Simon Marchi
  2021-10-05 19:10   ` Simon Marchi
  2021-09-29 21:50 ` [PATCH v4 4/4] gdb: Setting setter return a bool to tell if the value changed Lancelot SIX
  2021-09-30 12:44 ` [PATCH v4 0/4] Refactor cmd_list_element.var Simon Marchi
  4 siblings, 2 replies; 18+ messages in thread
From: Lancelot SIX @ 2021-09-29 21:50 UTC (permalink / raw)
  To: gdb-patches; +Cc: Lancelot SIX, Simon Marchi

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 | 355 +++++++++++++++++++++++++++++++++++++++----
 gdb/command.h        | 197 ++++++++++++++++++++++--
 2 files changed, 506 insertions(+), 46 deletions(-)

diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index b3bf6276157..30f5426fac8 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -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 *
@@ -475,6 +479,7 @@ add_set_or_show_cmd (const char *name,
 
   gdb_assert (type == set_cmd || type == show_cmd);
   c->type = type;
+
   c->var.emplace (var_type, arg);
 
   /* This needs to be something besides NULL so that this isn't
@@ -486,9 +491,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 +551,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 +594,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 +644,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 +701,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 +709,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 +748,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 +795,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 +805,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 +846,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 +855,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 +897,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 +963,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 +1014,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 +1063,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 +1100,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 +1150,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);
 }
 
diff --git a/gdb/command.h b/gdb/command.h
index c660bccf662..5ab25c74a9f 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -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,18 +249,42 @@ 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.
 
+     Calling this method is only valid if the current setting is stored in a
+     variable referenced by m_var (i.e. 'storage () == storage::variable').
+
      The template parameter T is the type of the variable used to store the
      setting.
 
@@ -241,13 +295,23 @@ struct setting
     gdb_assert (var_type_uses<T> (m_var_type));
     gdb_assert (m_var != nullptr);
 
-    return *static_cast<T const *> (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<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>
@@ -257,16 +321,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.  */
@@ -550,72 +630,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);
-- 
2.33.0


^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH v4 4/4] gdb: Setting setter return a bool to tell if the value changed
  2021-09-29 21:50 [PATCH v4 0/4] Refactor cmd_list_element.var Lancelot SIX
                   ` (2 preceding siblings ...)
  2021-09-29 21:50 ` [PATCH v4 3/4] gdb: Have setter and getter callbacks for settings Lancelot SIX
@ 2021-09-29 21:50 ` Lancelot SIX
  2021-09-30 12:43   ` Simon Marchi
  2021-09-30 12:44 ` [PATCH v4 0/4] Refactor cmd_list_element.var Simon Marchi
  4 siblings, 1 reply; 18+ messages in thread
From: Lancelot SIX @ 2021-09-29 21:50 UTC (permalink / raw)
  To: gdb-patches; +Cc: Lancelot SIX

GDB can notify observers when a parameter is changed.

To do that, do_set_command (in gdb/cli/cli-setshow.c) compares the new
value against the old one before updating it, and based on that notifies
observers.  This looks like something like:

    int valuechanged = 0;
    switch (cmd->var.type ())
    {
    case var_integer:
      {
        LONGEST new_val = parse_and_eval_long (arg)
        if (new_val != cmd->var.get<int> ())
        {
          cmd->var.get<int> (new_val);
          value_changes = 1;
        }
      }
    case var_uinteger:
    case var_zuinteger:
      {
        unsigned int val
          = parse_cli_var_uinteger (c->var->type (), &arg, true);
        if (c->var->get<unsigned int> () != val)
          {
            c->var->set<unsigned int> (val);
            option_changed = true;
          }
      }
    case...
      /* And so on for all possible var_types.  */
    }

This comparison is done for each possible var_type, which leads to
unnecessary logic duplication.

In this patch I propose to move all those checks in one place within the
setting setter method.  This limits the code duplication and simplifies
the do_set_command implementation.

This patch also changes slightly the way a value change is detected.
Instead of comparing the user provided value against the current value
of the setting, we compare the value of the setting before and after the
set operation.  This is meant to handle edge cases where trying to set
an unrecognized value would be equivalent to a noop (the actual value
remains unchanged).  Doing this requires that the original value needs
to be copied before the update, which can be non trivial for
std::string.

There should be no user visible change introduced by this commit.

Tested on x86_64 GNU/Linux.

[1] https://review.lttng.org/c/binutils-gdb/+/5831/41

Change-Id: If064b9cede3eb56275aacd2b286f74eceb1aed11
---
 gdb/cli/cli-setshow.c | 83 +++++++------------------------------------
 gdb/command.h         |  6 +++-
 2 files changed, 18 insertions(+), 71 deletions(-)

diff --git a/gdb/cli/cli-setshow.c b/gdb/cli/cli-setshow.c
index a027891de05..f06c92731e0 100644
--- a/gdb/cli/cli-setshow.c
+++ b/gdb/cli/cli-setshow.c
@@ -360,26 +360,12 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
 	*q++ = '\0';
 	newobj = (char *) xrealloc (newobj, q - newobj);
 
-	const std::string &cur_val = c->var->get<std::string> ();
-	if (strcmp (cur_val.c_str(),  newobj) != 0)
-	  {
-	    c->var->set<std::string> (std::string (newobj));
-
-	    option_changed = true;
-	  }
+	option_changed = c->var->set<std::string> (std::string (newobj));
 	xfree (newobj);
       }
       break;
     case var_string_noescape:
-      {
-	const std::string &cur_val = c->var->get<std::string> ();
-	if (strcmp (cur_val.c_str (), arg) != 0)
-	  {
-	    c->var->set<std::string> (std::string (arg));
-
-	    option_changed = true;
-	  }
-      }
+      option_changed = c->var->set<std::string> (std::string (arg));
       break;
     case var_filename:
       if (*arg == '\0')
@@ -405,13 +391,8 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
 	else
 	  val = xstrdup ("");
 
-	const std::string &cur_val = c->var->get<std::string> ();
-	if (strcmp (cur_val.c_str (), val) != 0)
-	  {
-	    c->var->set<std::string> (std::string (val));
-
-	    option_changed = true;
-	  }
+	option_changed
+	  = c->var->set<std::string> (std::string (val));
 	xfree (val);
       }
       break;
@@ -421,39 +402,18 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
 
 	if (val < 0)
 	  error (_("\"on\" or \"off\" expected."));
-	if (val != c->var->get<bool> ())
-	  {
-	    c->var->set<bool> (val);
 
-	    option_changed = true;
-	  }
+	option_changed = c->var->set<bool> (val);
       }
       break;
     case var_auto_boolean:
-      {
-	enum auto_boolean val = parse_auto_binary_operation (arg);
-
-	if (c->var->get<enum auto_boolean> () != val)
-	  {
-	    c->var->set<enum auto_boolean> (val);
-
-	    option_changed = true;
-	  }
-      }
+      option_changed = c->var->set<enum auto_boolean> (parse_auto_binary_operation (arg));
       break;
     case var_uinteger:
     case var_zuinteger:
-      {
-	unsigned int val
-	  = parse_cli_var_uinteger (c->var->type (), &arg, true);
-
-	if (c->var->get<unsigned int> () != val)
-	  {
-	    c->var->set<unsigned int> (val);
-
-	    option_changed = true;
-	  }
-      }
+      option_changed
+	= c->var->set<unsigned int> (parse_cli_var_uinteger (c->var->type (),
+							     &arg, true));
       break;
     case var_integer:
     case var_zinteger:
@@ -483,12 +443,7 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
 		 || (c->var->type () == var_zinteger && val > INT_MAX))
 	  error (_("integer %s out of range"), plongest (val));
 
-	if (c->var->get<int> () != val)
-	  {
-	    c->var->set<int> (val);
-
-	    option_changed = true;
-	  }
+	option_changed = c->var->set<int> (val);
       }
       break;
     case var_enum:
@@ -501,24 +456,12 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
 	if (*after != '\0')
 	  error (_("Junk after item \"%.*s\": %s"), len, arg, after);
 
-	if (c->var->get<const char *> () != match)
-	  {
-	    c->var->set<const char *> (match);
-
-	    option_changed = true;
-	  }
+	option_changed = c->var->set<const char *> (match);
       }
       break;
     case var_zuinteger_unlimited:
-      {
-	int val = parse_cli_var_zuinteger_unlimited (&arg, true);
-
-	if (c->var->get<int> () != val)
-	  {
-	    c->var->set<int> (val);
-	    option_changed = true;
-	  }
-      }
+      option_changed = c->var->set<int>
+	(parse_cli_var_zuinteger_unlimited (&arg, true));
       break;
     default:
       error (_("gdb internal error: bad var_type in do_setshow_command"));
diff --git a/gdb/command.h b/gdb/command.h
index 5ab25c74a9f..2d4561318bc 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -315,12 +315,14 @@ struct setting
 
      The var_type of the setting must match T.  */
   template<typename T>
-  void set (const T &v)
+  bool set (const T &v)
   {
     /* Check that the current instance is of one of the supported types for
        this instantiation.  */
     gdb_assert (var_type_uses<T> (m_var_type));
 
+    const T old_value = this->get<T> ();
+
     if (m_var == nullptr)
       {
 	gdb_assert (m_setter != nullptr);
@@ -329,6 +331,8 @@ struct setting
       }
     else
       *static_cast<T *> (m_var) = v;
+
+    return old_value != this->get<T> ();
   }
 
 private:
-- 
2.33.0


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v4 1/4] gdb: Introduce setting construct within cmd_list_element
  2021-09-29 21:50 ` [PATCH v4 1/4] gdb: Introduce setting construct within cmd_list_element Lancelot SIX
@ 2021-09-30 12:12   ` Simon Marchi
  2021-09-30 12:30     ` Simon Marchi
  2021-09-30 12:39   ` Simon Marchi
  1 sibling, 1 reply; 18+ messages in thread
From: Simon Marchi @ 2021-09-30 12:12 UTC (permalink / raw)
  To: Lancelot SIX, gdb-patches

> @@ -667,26 +701,35 @@ get_setshow_command_value_string (const cmd_list_element *c)
>        break;
>      case var_uinteger:
>      case var_zuinteger:
> -      if (c->var_type == var_uinteger
> -	  && *(unsigned int *) c->var == UINT_MAX)
> -	stb.puts ("unlimited");
> -      else
> -	stb.printf ("%u", *(unsigned int *) c->var);
> +      {
> +	unsigned int const value = var.get<unsigned int> ();

As Pedro mentioned, we prefer "const unsigned int" (here and below).

LGTM with that fixed.

Simon

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v4 1/4] gdb: Introduce setting construct within cmd_list_element
  2021-09-30 12:12   ` Simon Marchi
@ 2021-09-30 12:30     ` Simon Marchi
  2021-09-30 23:15       ` Lancelot SIX
  0 siblings, 1 reply; 18+ messages in thread
From: Simon Marchi @ 2021-09-30 12:30 UTC (permalink / raw)
  To: Lancelot SIX, gdb-patches

On 2021-09-30 08:12, Simon Marchi via Gdb-patches wrote:
>> @@ -667,26 +701,35 @@ get_setshow_command_value_string (const cmd_list_element *c)
>>        break;
>>      case var_uinteger:
>>      case var_zuinteger:
>> -      if (c->var_type == var_uinteger
>> -	  && *(unsigned int *) c->var == UINT_MAX)
>> -	stb.puts ("unlimited");
>> -      else
>> -	stb.printf ("%u", *(unsigned int *) c->var);
>> +      {
>> +	unsigned int const value = var.get<unsigned int> ();
> 
> As Pedro mentioned, we prefer "const unsigned int" (here and below).
> 
> LGTM with that fixed.

In fact, I tried to build it and there's a build failure due to a recent
change:

      CXX    bt-utils.o
    In file included from /home/simark/src/binutils-gdb/gdb/../gdbsupport/common-defs.h:201,
                     from /home/simark/src/binutils-gdb/gdb/defs.h:28,
                     from /home/simark/src/binutils-gdb/gdb/bt-utils.c:18:
    /home/simark/src/binutils-gdb/gdb/bt-utils.c: In function ‘void gdb_internal_backtrace_set_cmd(const char*, int, cmd_list_element*)’:
    /home/simark/src/binutils-gdb/gdb/bt-utils.c:32:18: error: ‘struct cmd_list_element’ has no member named ‘var_type’
       32 |   gdb_assert (c->var_type == var_boolean);
          |                  ^~~~~~~~
    /home/simark/src/binutils-gdb/gdb/../gdbsupport/gdb_assert.h:35:13: note: in definition of macro ‘gdb_assert’
       35 |   ((void) ((expr) ? 0 :                                                       \
          |             ^~~~

I think the change below should fix it.

Since it's one of those
settings that reverts the value if the new value is invalid, it could
eventually be changed to use a getter/setter, so that the setter simply
rejects the new value if it's not valid.


From 96df63a390451e0bbdfc74784ea09a3aa360656d Mon Sep 17 00:00:00 2001
From: Simon Marchi <simon.marchi@polymtl.ca>
Date: Thu, 30 Sep 2021 08:26:41 -0400
Subject: [PATCH] fix

Change-Id: Id78157cacf2de9cfacba1f19dea17b0f7b65aa0f
---
 gdb/bt-utils.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/gdb/bt-utils.c b/gdb/bt-utils.c
index 79e6e090d428..2054e68c6e5a 100644
--- a/gdb/bt-utils.c
+++ b/gdb/bt-utils.c
@@ -29,15 +29,15 @@ gdb_internal_backtrace_set_cmd (const char *args, int from_tty,
 				cmd_list_element *c)
 {
   gdb_assert (c->type == set_cmd);
-  gdb_assert (c->var_type == var_boolean);
-  gdb_assert (c->var != nullptr);
+  gdb_assert (c->var->type () == var_boolean);
+  gdb_assert (c->var.has_value ());
 
 #ifndef GDB_PRINT_INTERNAL_BACKTRACE
-  bool *var_ptr = (bool *) c->var;
+  bool val = c->var->get<bool> ();
 
-  if (*var_ptr)
+  if (val)
     {
-      *var_ptr = false;
+      c->var->set<bool> (false);
       error (_("support for this feature is not compiled into GDB"));
     }
 #endif
-- 
2.33.0


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v4 1/4] gdb: Introduce setting construct within cmd_list_element
  2021-09-29 21:50 ` [PATCH v4 1/4] gdb: Introduce setting construct within cmd_list_element Lancelot SIX
  2021-09-30 12:12   ` Simon Marchi
@ 2021-09-30 12:39   ` Simon Marchi
  1 sibling, 0 replies; 18+ messages in thread
From: Simon Marchi @ 2021-09-30 12:39 UTC (permalink / raw)
  To: Lancelot SIX, gdb-patches

Just a nit here:

> +  /* Return the current value.
> +
> +     The template parameter T is the type of the variable used to store the
> +     setting.
> +
> +     The returned value cannot be NULL (this is checked at runtime).*/

The last line of the comment here doesn't seem relevant with the code we
have now.

> +  template<typename T>
> +  const T &get () const
> +  {
> +    gdb_assert (var_type_uses<T> (m_var_type));
> +    gdb_assert (m_var != nullptr);
> +
> +    return *static_cast<T const *> (m_var);
> +  }

Simon

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v4 3/4] gdb: Have setter and getter callbacks for settings
  2021-09-29 21:50 ` [PATCH v4 3/4] gdb: Have setter and getter callbacks for settings Lancelot SIX
@ 2021-09-30 12:42   ` Simon Marchi
  2021-10-05 19:10   ` Simon Marchi
  1 sibling, 0 replies; 18+ messages in thread
From: Simon Marchi @ 2021-09-30 12:42 UTC (permalink / raw)
  To: Lancelot SIX, gdb-patches

Just some nits:

> @@ -475,6 +479,7 @@ add_set_or_show_cmd (const char *name,
>  
>    gdb_assert (type == set_cmd || type == show_cmd);
>    c->type = type;
> +
>    c->var.emplace (var_type, arg);

Spurious new line added.

> @@ -219,18 +249,42 @@ 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.
>  
> +     Calling this method is only valid if the current setting is stored in a
> +     variable referenced by m_var (i.e. 'storage () == storage::variable').

That part of the comment seems false.

Simon

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v4 4/4] gdb: Setting setter return a bool to tell if the value changed
  2021-09-29 21:50 ` [PATCH v4 4/4] gdb: Setting setter return a bool to tell if the value changed Lancelot SIX
@ 2021-09-30 12:43   ` Simon Marchi
  0 siblings, 0 replies; 18+ messages in thread
From: Simon Marchi @ 2021-09-30 12:43 UTC (permalink / raw)
  To: Lancelot SIX, gdb-patches

> diff --git a/gdb/command.h b/gdb/command.h
> index 5ab25c74a9f..2d4561318bc 100644
> --- a/gdb/command.h
> +++ b/gdb/command.h
> @@ -315,12 +315,14 @@ struct setting
>  
>       The var_type of the setting must match T.  */
>    template<typename T>
> -  void set (const T &v)
> +  bool set (const T &v)

Can you just update the comment to document the return value meaning?

LGTM otherwise.

Simon

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v4 0/4] Refactor cmd_list_element.var
  2021-09-29 21:50 [PATCH v4 0/4] Refactor cmd_list_element.var Lancelot SIX
                   ` (3 preceding siblings ...)
  2021-09-29 21:50 ` [PATCH v4 4/4] gdb: Setting setter return a bool to tell if the value changed Lancelot SIX
@ 2021-09-30 12:44 ` Simon Marchi
  2021-10-03 19:22   ` Lancelot SIX
  4 siblings, 1 reply; 18+ messages in thread
From: Simon Marchi @ 2021-09-30 12:44 UTC (permalink / raw)
  To: Lancelot SIX, gdb-patches

On 2021-09-29 17:50, Lancelot SIX via Gdb-patches wrote:
> Hi,
> 
> This is a V4 following
> https://sourceware.org/pipermail/gdb-patches/2021-September/182006.html
> 
> This version addresses the comments from Pedro.  It has also been rebased
> on top of current trunk.
> 
> The entire series have been tested on x86_64-linux-gnu without regression
> observed.
> 
> All feedbacks welcome.
> 
> Lancelot.

This series is OK to push with the minor comments addressed.  Thanks a
lot for picking this up.

Simon

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v4 1/4] gdb: Introduce setting construct within cmd_list_element
  2021-09-30 12:30     ` Simon Marchi
@ 2021-09-30 23:15       ` Lancelot SIX
  2021-10-01  1:16         ` Simon Marchi
  0 siblings, 1 reply; 18+ messages in thread
From: Lancelot SIX @ 2021-09-30 23:15 UTC (permalink / raw)
  To: Simon Marchi; +Cc: gdb-patches

> 
> I think the change below should fix it.

Hi,

Sorry for this. I did rebase just few days ago and let this one slip
through.  I’ll fix this and address your other comments shortly.

> 
> Since it's one of those
> settings that reverts the value if the new value is invalid, it could
> eventually be changed to use a getter/setter, so that the setter simply
> rejects the new value if it's not valid.

I’ll do that in separate commit once this series is pushed if this ok
with you. For the moment I’ll just fix the current compile error
with something similar to what you propose bellow.

Thanks a lot for the review.

Best,
Lancelot.

> 
> 
> From 96df63a390451e0bbdfc74784ea09a3aa360656d Mon Sep 17 00:00:00 2001
> From: Simon Marchi <simon.marchi@polymtl.ca>
> Date: Thu, 30 Sep 2021 08:26:41 -0400
> Subject: [PATCH] fix
> 
> Change-Id: Id78157cacf2de9cfacba1f19dea17b0f7b65aa0f
> ---
>  gdb/bt-utils.c | 10 +++++-----
>  1 file changed, 5 insertions(+), 5 deletions(-)
> 
> diff --git a/gdb/bt-utils.c b/gdb/bt-utils.c
> index 79e6e090d428..2054e68c6e5a 100644
> --- a/gdb/bt-utils.c
> +++ b/gdb/bt-utils.c
> @@ -29,15 +29,15 @@ gdb_internal_backtrace_set_cmd (const char *args, int from_tty,
>  				cmd_list_element *c)
>  {
>    gdb_assert (c->type == set_cmd);
> -  gdb_assert (c->var_type == var_boolean);
> -  gdb_assert (c->var != nullptr);
> +  gdb_assert (c->var->type () == var_boolean);
> +  gdb_assert (c->var.has_value ());
>  
>  #ifndef GDB_PRINT_INTERNAL_BACKTRACE
> -  bool *var_ptr = (bool *) c->var;
> +  bool val = c->var->get<bool> ();
>  
> -  if (*var_ptr)
> +  if (val)
>      {
> -      *var_ptr = false;
> +      c->var->set<bool> (false);
>        error (_("support for this feature is not compiled into GDB"));
>      }
>  #endif
> -- 
> 2.33.0
> 

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v4 1/4] gdb: Introduce setting construct within cmd_list_element
  2021-09-30 23:15       ` Lancelot SIX
@ 2021-10-01  1:16         ` Simon Marchi
  0 siblings, 0 replies; 18+ messages in thread
From: Simon Marchi @ 2021-10-01  1:16 UTC (permalink / raw)
  To: Lancelot SIX; +Cc: gdb-patches

On 2021-09-30 19:15, Lancelot SIX wrote:
> I’ll do that in separate commit once this series is pushed if this ok
> with you. For the moment I’ll just fix the current compile error
> with something similar to what you propose bellow.

Sure, and it's not mandatory either.

Simon

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v4 0/4] Refactor cmd_list_element.var
  2021-09-30 12:44 ` [PATCH v4 0/4] Refactor cmd_list_element.var Simon Marchi
@ 2021-10-03 19:22   ` Lancelot SIX
  0 siblings, 0 replies; 18+ messages in thread
From: Lancelot SIX @ 2021-10-03 19:22 UTC (permalink / raw)
  To: Simon Marchi; +Cc: gdb-patches

> This series is OK to push with the minor comments addressed.  Thanks a
> lot for picking this up.
> 
> Simon

Hi,

After addressing the last comments you made on the last iteration, I
pushed this series to trunk.

Thanks for your time for the reviews!

Best,
Lancelot.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v4 3/4] gdb: Have setter and getter callbacks for settings
  2021-09-29 21:50 ` [PATCH v4 3/4] gdb: Have setter and getter callbacks for settings Lancelot SIX
  2021-09-30 12:42   ` Simon Marchi
@ 2021-10-05 19:10   ` Simon Marchi
  2021-10-05 20:27     ` Lancelot SIX
  1 sibling, 1 reply; 18+ messages in thread
From: Simon Marchi @ 2021-10-05 19:10 UTC (permalink / raw)
  To: Lancelot SIX, gdb-patches

> @@ -241,13 +295,23 @@ struct setting
>      gdb_assert (var_type_uses<T> (m_var_type));
>      gdb_assert (m_var != nullptr);
>
> -    return *static_cast<T const *> (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<T *> (m_var);

While pulling these upstream changes to the ROCm GDB port, I found that
we forgot to remove the `gdb_assert (m_var != nullptr)` above.

I can fix that when I send a patch that actually uses the
getters/setters (which would trigger the assert).

Simon

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v4 3/4] gdb: Have setter and getter callbacks for settings
  2021-10-05 19:10   ` Simon Marchi
@ 2021-10-05 20:27     ` Lancelot SIX
  2021-10-05 20:39       ` Simon Marchi
  0 siblings, 1 reply; 18+ messages in thread
From: Lancelot SIX @ 2021-10-05 20:27 UTC (permalink / raw)
  To: Simon Marchi; +Cc: gdb-patches

On Tue, Oct 05, 2021 at 03:10:55PM -0400, Simon Marchi wrote:
> > @@ -241,13 +295,23 @@ struct setting
> >      gdb_assert (var_type_uses<T> (m_var_type));
> >      gdb_assert (m_var != nullptr);
> >
> > -    return *static_cast<T const *> (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<T *> (m_var);
> 
> While pulling these upstream changes to the ROCm GDB port, I found that
> we forgot to remove the `gdb_assert (m_var != nullptr)` above.
> 
> I can fix that when I send a patch that actually uses the
> getters/setters (which would trigger the assert).
> 
> Simon

Hi,

This is odd, I was sure I did fix this omission, but obviously not.
Thanks for spotting this, and my apologies for the inconvenience.

Here is a (trivial) patch that fixes that.

Best,
Lancelot.

---
From 4c5b728046770ef923323162ac48246b947267d2 Mon Sep 17 00:00:00 2001
From: Lancelot SIX <lsix@lancelotsix.com>
Date: Tue, 5 Oct 2021 19:55:19 +0000
Subject: [PATCH] gdb: Remove deprecated assertion in setting::get

The commit 702991711a91bd47b209289562843a11e7009396 (gdb: Have setter
and getter callbacks for settings) makes it possible for a setting not
to be backed by a memory buffer but use callback functions instead to
retrieve or set the setting's value.

An assertion was not properly updated to take into account that the
m_var member (which points to a memory buffer, if used) might be nullptr
if the setting uses callback functions.  If the setting is backed by a
memory buffer, the m_var has to be non nullptr, which is already checked
before the pointer is dereferenced.

This commit removes this assertion as it is not valid anymore.
---
 gdb/command.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/gdb/command.h b/gdb/command.h
index 7c226f193b8..0049ab6ff9e 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -288,7 +288,6 @@ struct setting
   const T &get () const
   {
     gdb_assert (var_type_uses<T> (m_var_type));
-    gdb_assert (m_var != nullptr);
 
     if (m_var == nullptr)
       {
-- 
2.30.2


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v4 3/4] gdb: Have setter and getter callbacks for settings
  2021-10-05 20:27     ` Lancelot SIX
@ 2021-10-05 20:39       ` Simon Marchi
  2021-10-05 22:21         ` Lancelot SIX
  0 siblings, 1 reply; 18+ messages in thread
From: Simon Marchi @ 2021-10-05 20:39 UTC (permalink / raw)
  To: Lancelot SIX; +Cc: gdb-patches

On 2021-10-05 4:27 p.m., Lancelot SIX wrote:
> On Tue, Oct 05, 2021 at 03:10:55PM -0400, Simon Marchi wrote:
>>> @@ -241,13 +295,23 @@ struct setting
>>>      gdb_assert (var_type_uses<T> (m_var_type));
>>>      gdb_assert (m_var != nullptr);
>>>
>>> -    return *static_cast<T const *> (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<T *> (m_var);
>>
>> While pulling these upstream changes to the ROCm GDB port, I found that
>> we forgot to remove the `gdb_assert (m_var != nullptr)` above.
>>
>> I can fix that when I send a patch that actually uses the
>> getters/setters (which would trigger the assert).
>>
>> Simon
> 
> Hi,
> 
> This is odd, I was sure I did fix this omission, but obviously not.
> Thanks for spotting this, and my apologies for the inconvenience.
> 
> Here is a (trivial) patch that fixes that.
> 
> Best,
> Lancelot.
> 
> ---
> From 4c5b728046770ef923323162ac48246b947267d2 Mon Sep 17 00:00:00 2001
> From: Lancelot SIX <lsix@lancelotsix.com>
> Date: Tue, 5 Oct 2021 19:55:19 +0000
> Subject: [PATCH] gdb: Remove deprecated assertion in setting::get
> 
> The commit 702991711a91bd47b209289562843a11e7009396 (gdb: Have setter
> and getter callbacks for settings) makes it possible for a setting not
> to be backed by a memory buffer but use callback functions instead to
> retrieve or set the setting's value.
> 
> An assertion was not properly updated to take into account that the
> m_var member (which points to a memory buffer, if used) might be nullptr
> if the setting uses callback functions.  If the setting is backed by a
> memory buffer, the m_var has to be non nullptr, which is already checked
> before the pointer is dereferenced.
> 
> This commit removes this assertion as it is not valid anymore.
> ---
>  gdb/command.h | 1 -
>  1 file changed, 1 deletion(-)
> 
> diff --git a/gdb/command.h b/gdb/command.h
> index 7c226f193b8..0049ab6ff9e 100644
> --- a/gdb/command.h
> +++ b/gdb/command.h
> @@ -288,7 +288,6 @@ struct setting
>    const T &get () const
>    {
>      gdb_assert (var_type_uses<T> (m_var_type));
> -    gdb_assert (m_var != nullptr);
>  
>      if (m_var == nullptr)
>        {
> -- 
> 2.30.2
> 

Well, since you have the patch already, go ahead and push it.

Thanks,

Simon

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v4 3/4] gdb: Have setter and getter callbacks for settings
  2021-10-05 20:39       ` Simon Marchi
@ 2021-10-05 22:21         ` Lancelot SIX
  0 siblings, 0 replies; 18+ messages in thread
From: Lancelot SIX @ 2021-10-05 22:21 UTC (permalink / raw)
  To: Simon Marchi; +Cc: gdb-patches

> 
> Well, since you have the patch already, go ahead and push it.
> 
> Thanks,
> 
> Simon

Hi,

I have pushed it as 1461d3712b921466015ab877b6e08ac27456a6a6.

Best,
Lancelot.

^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2021-10-05 22:21 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-09-29 21:50 [PATCH v4 0/4] Refactor cmd_list_element.var Lancelot SIX
2021-09-29 21:50 ` [PATCH v4 1/4] gdb: Introduce setting construct within cmd_list_element Lancelot SIX
2021-09-30 12:12   ` Simon Marchi
2021-09-30 12:30     ` Simon Marchi
2021-09-30 23:15       ` Lancelot SIX
2021-10-01  1:16         ` Simon Marchi
2021-09-30 12:39   ` Simon Marchi
2021-09-29 21:50 ` [PATCH v4 2/4] gdb: make string-like set show commands use std::string variable Lancelot SIX
2021-09-29 21:50 ` [PATCH v4 3/4] gdb: Have setter and getter callbacks for settings Lancelot SIX
2021-09-30 12:42   ` Simon Marchi
2021-10-05 19:10   ` Simon Marchi
2021-10-05 20:27     ` Lancelot SIX
2021-10-05 20:39       ` Simon Marchi
2021-10-05 22:21         ` Lancelot SIX
2021-09-29 21:50 ` [PATCH v4 4/4] gdb: Setting setter return a bool to tell if the value changed Lancelot SIX
2021-09-30 12:43   ` Simon Marchi
2021-09-30 12:44 ` [PATCH v4 0/4] Refactor cmd_list_element.var Simon Marchi
2021-10-03 19:22   ` Lancelot SIX

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).