public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 00/16] Bunch of commands related cleanups
@ 2021-07-14  4:55 Simon Marchi
  2021-07-14  4:55 ` [PATCH 01/16] gdb/testsuite: split gdb.python/py-parameter.exp in procs Simon Marchi
                   ` (15 more replies)
  0 siblings, 16 replies; 50+ messages in thread
From: Simon Marchi @ 2021-07-14  4:55 UTC (permalink / raw)
  To: gdb-patches

While making a patch for bug 28085 [1], I put my hands in the command
code, and noticed a lot of potential cleanups to make the code simpler
more robust.  My patch queue started to get a bit long, so here's a
mixed bag of patches that I think are improvements on their own right.

The last patch of the series is the biggest and most risky one, it makes
string settings use an std::string variable, instead of a `char *`.
It's quite a big change, but I think it makes things easier going
forward.

Other than that, there are some cleanups in tests, removal of unneeded
fields / declarations, adding of C++-y getters / setters, etc.

This was regtested on Ubuntu 20.04 with the unix and
native-extended-gdbserver boards.

[1] https://sourceware.org/bugzilla/show_bug.cgi?id=28085

Simon Marchi (16):
  gdb/testsuite: split gdb.python/py-parameter.exp in procs
  gdb.base/setshow.exp: use save_vars to save/restore gdb_prompt
  gdb.base/setshow.exp: split in procs
  gdb.base/setshow.exp: fix duplicate test name
  gdb: un-share set_inferior_cwd declaration
  gdb: remove inferior::{argc,argv}
  gdb: add setter/getter for inferior arguments
  gdb: add setter/getter for inferior cwd
  gdb: make inferior::m_args an std::string
  gdb: make inferior::m_cwd an std::string
  gdb: make inferior::m_terminal an std::string
  gdb: rename cfunc to simple_func
  gdb: remove cmd_list_element::function::sfunc
  gdb/testsuite: test get/set value of unregistered Guile parameter
  gdb: make cmd_list_element var an optional union
  gdb: make string-like set show commands use std::string variable

 gdb/auto-load.c                           |  48 +-
 gdb/breakpoint.c                          |  28 +-
 gdb/breakpoint.h                          |   2 +-
 gdb/build-id.c                            |   4 +-
 gdb/cli/cli-cmds.c                        |  59 +-
 gdb/cli/cli-decode.c                      | 223 ++++---
 gdb/cli/cli-decode.h                      |  57 +-
 gdb/cli/cli-dump.c                        |   2 +-
 gdb/cli/cli-logging.c                     |  23 +-
 gdb/cli/cli-option.c                      |   9 +-
 gdb/cli/cli-option.h                      |   4 +-
 gdb/cli/cli-setshow.c                     | 101 ++-
 gdb/command.h                             |  79 +--
 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/elf-none-tdep.c                       |   6 +-
 gdb/event-top.c                           |  12 +-
 gdb/fbsd-tdep.c                           |   6 +-
 gdb/fork-child.c                          |   7 +-
 gdb/guile/scm-cmd.c                       |   3 +-
 gdb/guile/scm-param.c                     | 168 ++++-
 gdb/infcmd.c                              | 103 +--
 gdb/inferior.c                            |  11 +-
 gdb/inferior.h                            |  78 ++-
 gdb/inflow.c                              |  46 +-
 gdb/linux-tdep.c                          |   7 +-
 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                       |  27 +-
 gdb/nat/fork-inferior.c                   |  15 +-
 gdb/proc-api.c                            |   5 +-
 gdb/procfs.c                              |  10 +-
 gdb/python/py-cmd.c                       |   3 +-
 gdb/python/py-param.c                     |  48 +-
 gdb/python/python-internal.h              |   2 +-
 gdb/python/python.c                       |  32 +-
 gdb/remote-sim.c                          |   7 +-
 gdb/remote.c                              |  21 +-
 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/target.c                              |   4 +-
 gdb/terminal.h                            |   2 +-
 gdb/testsuite/gdb.base/setshow.exp        | 734 +++++++++++++---------
 gdb/testsuite/gdb.guile/scm-parameter.exp |   8 +
 gdb/testsuite/gdb.python/py-parameter.exp | 237 ++++---
 gdb/top.c                                 | 112 ++--
 gdb/top.h                                 |   2 +-
 gdb/tracepoint.c                          |  47 +-
 gdb/tracepoint.h                          |   2 +-
 gdb/tui/tui-layout.c                      |   3 +-
 gdb/windows-nat.c                         |   2 +-
 gdbserver/inferiors.cc                    |  18 +-
 gdbserver/inferiors.h                     |   4 +
 gdbserver/server.cc                       |   6 +-
 gdbsupport/common-inferior.h              |   9 +-
 69 files changed, 1559 insertions(+), 1260 deletions(-)

-- 
2.32.0


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

* [PATCH 01/16] gdb/testsuite: split gdb.python/py-parameter.exp in procs
  2021-07-14  4:55 [PATCH 00/16] Bunch of commands related cleanups Simon Marchi
@ 2021-07-14  4:55 ` Simon Marchi
  2021-07-14  4:55 ` [PATCH 02/16] gdb.base/setshow.exp: use save_vars to save/restore gdb_prompt Simon Marchi
                   ` (14 subsequent siblings)
  15 siblings, 0 replies; 50+ messages in thread
From: Simon Marchi @ 2021-07-14  4:55 UTC (permalink / raw)
  To: gdb-patches

Split the file into multiple independent test procs, where each proc
starts with a fresh GDB.  I find it easier to understand what a test is
doing when each part of the test is isolated and self-contained.  It
makes it easier to comment out some parts of the test while working /
debugging a specific part.  It also makes it easier to add new things
(which a subsequent patch will do) without fear of impacting another part
of the test.

Change-Id: I8b4d52ac82b1492d79b679e13914ed177d8a836d
---
 gdb/testsuite/gdb.python/py-parameter.exp | 237 +++++++++++++---------
 1 file changed, 137 insertions(+), 100 deletions(-)

diff --git a/gdb/testsuite/gdb.python/py-parameter.exp b/gdb/testsuite/gdb.python/py-parameter.exp
index 84ad70ee20ef..7a51eeb640fc 100644
--- a/gdb/testsuite/gdb.python/py-parameter.exp
+++ b/gdb/testsuite/gdb.python/py-parameter.exp
@@ -19,78 +19,84 @@
 load_lib gdb-python.exp
 
 # Start with a fresh gdb.
-gdb_exit
-gdb_start
-gdb_reinitialize_dir $srcdir/$subdir
+clean_restart
 
 # Skip all tests if Python scripting is not enabled.
 if { [skip_python_tests] } { continue }
 
-# We use "." here instead of ":" so that this works on win32 too.
-if { [is_remote host] } {
-    # Don't match $srcdir/$subdir because proc gdb_reinitialize_dir
-    # doesn't set search directories on remote host.
-    set directories ".*\\\$cdir.\\\$cwd"
-} else {
-    set escaped_directory [string_to_regexp "$srcdir/$subdir"]
-    set directories "$escaped_directory.\\\$cdir.\\\$cwd"
-}
-gdb_test "python print (gdb.parameter ('directories'))" $directories
-
-# Check we can correctly read the data-directory parameter.  First,
-# grab the value as read directly from the GDB CLI.
-set dd ""
-gdb_test_multiple "show data-directory" \
-    "find the initial data-directory value" {
-	-re -wrap "GDB's data directory is \"(\[^\r\n\]+)\"\\." {
-	    set dd $expect_out(1,string)
-	    pass $gdb_test_name
-	}
+proc_with_prefix test_directories { } {
+    # We use "." here instead of ":" so that this works on win32 too.
+    if { [is_remote host] } {
+	# Don't match $srcdir/$subdir because proc gdb_reinitialize_dir
+	# doesn't set search directories on remote host.
+	set directories ".*\\\$cdir.\\\$cwd"
+    } else {
+	set escaped_directory [string_to_regexp "$::srcdir/$::subdir"]
+	set directories "$escaped_directory.\\\$cdir.\\\$cwd"
     }
+    gdb_test "python print (gdb.parameter ('directories'))" $directories
+}
 
-# Now print the data-directory from Python.
-gdb_test "python print (gdb.parameter ('data-directory'))" $dd
-
-# Next change the data-directory to a relative path.  Internally GDB
-# will resolve this to an absolute path, which Python should then see.
-#
-# GDB is currently running in '...../build/gdb/testsuite/' and the
-# test output is being written to:
-#   ...../build/gdb/testsuite/outputs/gdb.python/py-parameter/
-#
-# So create the relative path './outputs/gdb.python/py-parameter/' and
-# set the data-directory to that, we should then see the absolute path.
-
-set abs_path_to_output_dir [standard_output_file ""]
-set abs_path_to_cwd $objdir
-set rel_path_to_output_dir \
-    [file join "." [string replace ${abs_path_to_output_dir} 0 \
-			[string length ${abs_path_to_cwd}] ""]]
-gdb_test_no_output "set data-directory ${rel_path_to_output_dir}"
-
-gdb_test "python print (gdb.parameter ('data-directory'))" \
-    ${abs_path_to_output_dir} \
-    "python sees absolute version of data-directory path"
-
-# While we're here, check we see the correct path at GDB's CLI.
-gdb_test "show data-directory" \
-    "GDB's data directory is \"${abs_path_to_output_dir}\"\\." \
-    "check modified data-directory at the CLI"
-
-# Now lets set the data-directory back to what it was initially.
-gdb_test_no_output "set data-directory ${dd}" \
-    "set data-directory back to its original value"
-
-# And check we see the restored value at CLI and from Python.
-gdb_test "show data-directory" \
-    "GDB's data directory is \"${dd}\"\\." \
-    "check original data-directory was restored at the CLI"
+proc_with_prefix test_data_directory { } {
+    clean_restart
+
+    # Check we can correctly read the data-directory parameter.  First,
+    # grab the value as read directly from the GDB CLI.
+    set dd ""
+    gdb_test_multiple "show data-directory" \
+	"find the initial data-directory value" {
+	    -re -wrap "GDB's data directory is \"(\[^\r\n\]+)\"\\." {
+		set dd $expect_out(1,string)
+		pass $gdb_test_name
+	    }
+	}
 
-gdb_test "python print (gdb.parameter ('data-directory'))" ${dd} \
-    "python sees restored data-directory value"
+    # Now print the data-directory from Python.
+    gdb_test "python print (gdb.parameter ('data-directory'))" $dd
+
+    # Next change the data-directory to a relative path.  Internally GDB
+    # will resolve this to an absolute path, which Python should then see.
+    #
+    # GDB is currently running in '...../build/gdb/testsuite/' and the
+    # test output is being written to:
+    #   ...../build/gdb/testsuite/outputs/gdb.python/py-parameter/
+    #
+    # So create the relative path './outputs/gdb.python/py-parameter/' and
+    # set the data-directory to that, we should then see the absolute path.
+
+    set abs_path_to_output_dir [standard_output_file ""]
+    set abs_path_to_cwd $::objdir
+    set rel_path_to_output_dir \
+	[file join "." [string replace ${abs_path_to_output_dir} 0 \
+			    [string length ${abs_path_to_cwd}] ""]]
+    gdb_test_no_output "set data-directory ${rel_path_to_output_dir}"
+
+    gdb_test "python print (gdb.parameter ('data-directory'))" \
+	${abs_path_to_output_dir} \
+	"python sees absolute version of data-directory path"
+
+    # While we're here, check we see the correct path at GDB's CLI.
+    gdb_test "show data-directory" \
+	"GDB's data directory is \"${abs_path_to_output_dir}\"\\." \
+	"check modified data-directory at the CLI"
+
+    # Now lets set the data-directory back to what it was initially.
+    gdb_test_no_output "set data-directory ${dd}" \
+	"set data-directory back to its original value"
+
+    # And check we see the restored value at CLI and from Python.
+    gdb_test "show data-directory" \
+	"GDB's data directory is \"${dd}\"\\." \
+	"check original data-directory was restored at the CLI"
+
+    gdb_test "python print (gdb.parameter ('data-directory'))" ${dd} \
+	"python sees restored data-directory value"
+}
 
 # Test a simple boolean parameter.
-with_test_prefix "boolean parameter" {
+proc_with_prefix test_boolean_parameter { } {
+    clean_restart
+
     gdb_test_multiline "Simple gdb booleanparameter" \
 	"python" "" \
 	"class TestParam (gdb.Parameter):" "" \
@@ -130,7 +136,9 @@ with_test_prefix "boolean parameter" {
 }
 
 # Test an enum parameter.
-with_test_prefix "enum parameter" {
+proc_with_prefix test_enum_parameter { } {
+    clean_restart
+
     gdb_test_multiline "enum gdb parameter" \
 	"python" "" \
 	"class TestEnumParam (gdb.Parameter):" "" \
@@ -163,7 +171,9 @@ with_test_prefix "enum parameter" {
 }
 
 # Test a file parameter.
-with_test_prefix "file parameter" {
+proc_with_prefix test_file_parameter { } {
+    clean_restart
+
     gdb_test_multiline "file gdb parameter" \
 	"python" "" \
 	"class TestFileParam (gdb.Parameter):" "" \
@@ -195,7 +205,9 @@ with_test_prefix "file parameter" {
 }
 
 # Test a parameter that is not documented.
-with_test_prefix "undocumented parameter" {
+proc_with_prefix test_undocumented_parameter { } {
+    clean_restart
+
     gdb_test_multiline "Simple gdb booleanparameter" \
 	"python" "" \
 	"class TestUndocParam (gdb.Parameter):" "" \
@@ -230,7 +242,9 @@ with_test_prefix "undocumented parameter" {
 }
 
 # Test a parameter that is not documented in any way..
-with_test_prefix "really undocumented parameter" {
+proc_with_prefix test_really_undocumented_parameter { } {
+    clean_restart
+
     gdb_test_multiline "Simple gdb booleanparameter" \
 	"python" "" \
 	"class TestNodocParam (gdb.Parameter):" "" \
@@ -258,7 +272,9 @@ with_test_prefix "really undocumented parameter" {
 }
 
 # Test deprecated API. Do not use in your own implementations.
-with_test_prefix "deprecated API parameter" {
+proc_with_prefix test_deprecated_api_parameter { } {
+    clean_restart
+
     gdb_test_multiline "Simple gdb booleanparameter" \
 	"python" "" \
 	"class TestParam (gdb.Parameter):" "" \
@@ -289,41 +305,62 @@ with_test_prefix "deprecated API parameter" {
 	"test general help"
 }
 
-foreach kind {PARAM_ZUINTEGER PARAM_ZUINTEGER_UNLIMITED} {
-    gdb_test_multiline "Simple gdb $kind" \
+proc_with_prefix test_integer_parameter { } {
+    foreach_with_prefix kind {PARAM_ZUINTEGER PARAM_ZUINTEGER_UNLIMITED} {
+	clean_restart
+
+	gdb_test_multiline "create parameter" \
+	    "python" "" \
+	    "class TestNodocParam (gdb.Parameter):" "" \
+	    "   def __init__ (self, name):" "" \
+	    "      super (TestNodocParam, self).__init__ (name, gdb.COMMAND_DATA, gdb.$kind)" "" \
+	    "      self.value = 0" "" \
+	    "test_param_$kind = TestNodocParam ('test-$kind')" "" \
+	    "end"
+
+	gdb_test "python print(gdb.parameter('test-$kind'))" "0"
+
+	gdb_test "python test_param_$kind.value = -5" "RuntimeError: Range exceeded.*"
+
+	if {$kind == "PARAM_ZUINTEGER"} {
+	    gdb_test "python test_param_$kind.value = -1" "RuntimeError: Range exceeded.*"
+	} elseif {$kind == "PARAM_ZUINTEGER_UNLIMITED"} {
+	    gdb_test_no_output "python test_param_$kind.value = -1" \
+		"check that PARAM_ZUINTEGER value can be set to -1"
+	    gdb_test "python print(gdb.parameter('test-$kind'))" "-1" \
+		"check that PARAM_ZUINTEGER value is -1 after setting"
+	} else {
+	    error "invalid kind: $kind"
+	}
+    }
+}
+
+proc_with_prefix test_throwing_parameter { } {
+    clean_restart
+
+    gdb_test_multiline "Throwing gdb parameter" \
 	"python" "" \
-	"class TestNodocParam (gdb.Parameter):" "" \
+	"class TestThrowParam (gdb.Parameter):" "" \
 	"   def __init__ (self, name):" "" \
-	"      super (TestNodocParam, self).__init__ (name, gdb.COMMAND_DATA, gdb.$kind)" "" \
-	"      self.value = 0" "" \
-	"test_param_$kind = TestNodocParam ('test-$kind')" "" \
+	"      super (TestThrowParam, self).__init__ (name, gdb.COMMAND_DATA, gdb.PARAM_STRING)" "" \
+	"      self.value = True" "" \
+	"   def get_set_string (self):" "" \
+	"      raise gdb.GdbError('Ordinary gdb error')" "" \
+	"test_throw_param = TestThrowParam ('print test-throw-param')" ""\
 	"end"
 
-    gdb_test "python print(gdb.parameter('test-$kind'))" "0"
-
-    gdb_test "python test_param_$kind.value = -5" "RuntimeError: Range exceeded.*"
-
-    if {$kind == "PARAM_ZUINTEGER"} {
-	gdb_test "python test_param_$kind.value = -1" "RuntimeError: Range exceeded.*"
-    } else {
-	gdb_test_no_output "python test_param_$kind.value = -1" \
-	    "check that PARAM_ZUINTEGER value can be set to -1"
-	gdb_test "python print(gdb.parameter('test-$kind'))" "-1" \
-	    "check that PARAM_ZUINTEGER value is -1 after setting"
-    }
+    gdb_test "set print test-throw-param whoops" \
+	"Ordinary gdb error" \
+	"gdb.GdbError does not show Python stack"
 }
 
-gdb_test_multiline "Throwing gdb parameter" \
-    "python" "" \
-    "class TestThrowParam (gdb.Parameter):" "" \
-    "   def __init__ (self, name):" "" \
-    "      super (TestThrowParam, self).__init__ (name, gdb.COMMAND_DATA, gdb.PARAM_STRING)" "" \
-    "      self.value = True" "" \
-    "   def get_set_string (self):" "" \
-    "      raise gdb.GdbError('Ordinary gdb error')" "" \
-    "test_throw_param = TestThrowParam ('print test-throw-param')" ""\
-    "end"
-
-gdb_test "set print test-throw-param whoops" \
-    "Ordinary gdb error" \
-    "gdb.GdbError does not show Python stack"
+test_directories
+test_data_directory
+test_boolean_parameter
+test_enum_parameter
+test_file_parameter
+test_undocumented_parameter
+test_really_undocumented_parameter
+test_deprecated_api_parameter
+test_integer_parameter
+test_throwing_parameter
-- 
2.32.0


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

* [PATCH 02/16] gdb.base/setshow.exp: use save_vars to save/restore gdb_prompt
  2021-07-14  4:55 [PATCH 00/16] Bunch of commands related cleanups Simon Marchi
  2021-07-14  4:55 ` [PATCH 01/16] gdb/testsuite: split gdb.python/py-parameter.exp in procs Simon Marchi
@ 2021-07-14  4:55 ` Simon Marchi
  2021-07-14  4:55 ` [PATCH 03/16] gdb.base/setshow.exp: split in procs Simon Marchi
                   ` (13 subsequent siblings)
  15 siblings, 0 replies; 50+ messages in thread
From: Simon Marchi @ 2021-07-14  4:55 UTC (permalink / raw)
  To: gdb-patches

Using save_vars is a bit better than what we have now, as it ensures the
variable gets restored if the code within it throws an error.

Change-Id: I3bd6836e5b7efb61b078acadff1a1c8182c19a27
---
 gdb/testsuite/gdb.base/setshow.exp | 33 +++++++++++++++---------------
 1 file changed, 16 insertions(+), 17 deletions(-)

diff --git a/gdb/testsuite/gdb.base/setshow.exp b/gdb/testsuite/gdb.base/setshow.exp
index 5404b5239671..7b273b3902a5 100644
--- a/gdb/testsuite/gdb.base/setshow.exp
+++ b/gdb/testsuite/gdb.base/setshow.exp
@@ -48,31 +48,30 @@ gdb_test "show annotate" "Annotation_level is 0..*"  "default annotation_level i
 #test set annotate 2
 
 # Here we need to fiddle with prompts.
-set old_gdb_prompt $gdb_prompt
-set gdb_prompt "\r\n\032\032pre-prompt\r\n$gdb_prompt \r\n\032\032prompt\r\n"
+save_vars { gdb_prompt } {
+    set old_gdb_prompt $gdb_prompt
+    set gdb_prompt "\r\n\032\032pre-prompt\r\n$gdb_prompt \r\n\032\032prompt\r\n"
 
-gdb_test_multiple "set annotate 2" "set annotate 2" {
-    -re "\r\n$gdb_prompt$" {
-	pass "set annotate 2"
+    gdb_test_multiple "set annotate 2" "set annotate 2" {
+	-re "\r\n$gdb_prompt$" {
+	    pass "set annotate 2"
+	}
     }
-}
 
-gdb_test_multiple "show annotate" "show annotate 2" {
-    -re ".*\032\032post-prompt.*Annotation_level is 2..*\032\032pre-prompt.*$old_gdb_prompt .*\032\032prompt.*$" {
-	pass "show annotate 2"
+    gdb_test_multiple "show annotate" "show annotate 2" {
+	-re ".*\032\032post-prompt.*Annotation_level is 2..*\032\032pre-prompt.*$old_gdb_prompt .*\032\032prompt.*$" {
+	    pass "show annotate 2"
+	}
     }
-}
 
-#test annotation_level 2
-gdb_test_multiple "info line 1" "annotation_level 2" {
-    -re ".*\032\032post-prompt.*Line 1 of .* is at address .* but contains no code.*:1:0:beg:0x.*\032\032pre-prompt.*$old_gdb_prompt .*\032\032prompt.*$" {
-	pass "annotation_level 2" 
+    #test annotation_level 2
+    gdb_test_multiple "info line 1" "annotation_level 2" {
+	-re ".*\032\032post-prompt.*Line 1 of .* is at address .* but contains no code.*:1:0:beg:0x.*\032\032pre-prompt.*$old_gdb_prompt .*\032\032prompt.*$" {
+	    pass "annotation_level 2"
+	}
     }
 }
 
-# Restore the original prompt for the rest of the testsuite.
-set gdb_prompt $old_gdb_prompt
-
 #test set annotate 1
 gdb_test "set annotate 1" ".*post-prompt.*"
 gdb_test "show annotate" "Annotation_level is 1..*"  "show annotate (1)" 
-- 
2.32.0


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

* [PATCH 03/16] gdb.base/setshow.exp: split in procs
  2021-07-14  4:55 [PATCH 00/16] Bunch of commands related cleanups Simon Marchi
  2021-07-14  4:55 ` [PATCH 01/16] gdb/testsuite: split gdb.python/py-parameter.exp in procs Simon Marchi
  2021-07-14  4:55 ` [PATCH 02/16] gdb.base/setshow.exp: use save_vars to save/restore gdb_prompt Simon Marchi
@ 2021-07-14  4:55 ` Simon Marchi
  2021-07-14  4:55 ` [PATCH 04/16] gdb.base/setshow.exp: fix duplicate test name Simon Marchi
                   ` (12 subsequent siblings)
  15 siblings, 0 replies; 50+ messages in thread
From: Simon Marchi @ 2021-07-14  4:55 UTC (permalink / raw)
  To: gdb-patches

Split in multiple procs, one per topic, and start with a fresh GDB in
each.  I find it easier to work on a test with multiple smaller
independent test procedures.  For example, it's possible to comment all
but one when working on one.  It's also easier to add things without
having to think about the impact on existing tests, and vice-versa.

Change-Id: I19691eed8f9bcb975b2eeff7577cac66251bcbe2
---
 gdb/testsuite/gdb.base/setshow.exp | 729 +++++++++++++++++------------
 1 file changed, 435 insertions(+), 294 deletions(-)

diff --git a/gdb/testsuite/gdb.base/setshow.exp b/gdb/testsuite/gdb.base/setshow.exp
index 7b273b3902a5..bfc689b184e5 100644
--- a/gdb/testsuite/gdb.base/setshow.exp
+++ b/gdb/testsuite/gdb.base/setshow.exp
@@ -15,6 +15,10 @@
 
 # This file was written by Michael Snyder (msnyder@cygnus.com)
 
+# Test gdb set and show commands.  The goal here is to verify that parameters
+# are set and shown correctly, not test whether setting the parameter has the
+# desired effect.
+
 
 standard_testfile .c
 
@@ -23,280 +27,361 @@ if { [gdb_compile ${srcdir}/${subdir}/${srcfile} ${binfile} executable {debug}]
      return -1
 }
 
-# Start with a fresh gdb
+proc_with_prefix test_setshow_annotate {} {
+    # Start with a fresh gdb
+    clean_restart $::binfile
 
-clean_restart ${binfile}
+    if ![runto_main] then {
+	fail "can't run to main"
+	return
+    }
 
-# make sure $pc is sane, in case we're talking to a board.
-if ![runto_main] then {
-    fail "can't run to main"
-    return 0
-}
+    #test default annotation_level is 0
+    gdb_test "show annotate" "Annotation_level is 0..*"  "default annotation_level is zero"
 
-#
-# Test gdb set and show commands.
-# Add tests here for show and set that don't fit neatly elsewhere.
-# FIXME: many rudimentary tests for set and show commands have been
-#        added below, but most do nothing more than check that a 
-#        variable has been set successfully, ie. they do not test
-#        whether setting the variable has had the desired effect.
-#
+    #test set annotate 2
 
-#test default annotation_level is 0
-gdb_test "show annotate" "Annotation_level is 0..*"  "default annotation_level is zero"
+    # Here we need to fiddle with prompts.
+    save_vars { ::gdb_prompt } {
+	set old_gdb_prompt $::gdb_prompt
+	set ::gdb_prompt "\r\n\032\032pre-prompt\r\n$::gdb_prompt \r\n\032\032prompt\r\n"
 
-#test set annotate 2
+	gdb_test_multiple "set annotate 2" "set annotate 2" {
+	    -re "\r\n$::gdb_prompt$" {
+		pass "set annotate 2"
+	    }
+	}
 
-# Here we need to fiddle with prompts.
-save_vars { gdb_prompt } {
-    set old_gdb_prompt $gdb_prompt
-    set gdb_prompt "\r\n\032\032pre-prompt\r\n$gdb_prompt \r\n\032\032prompt\r\n"
+	gdb_test_multiple "show annotate" "show annotate 2" {
+	    -re ".*\032\032post-prompt.*Annotation_level is 2..*\032\032pre-prompt.*$old_gdb_prompt .*\032\032prompt.*$" {
+		pass "show annotate 2"
+	    }
+	}
 
-    gdb_test_multiple "set annotate 2" "set annotate 2" {
-	-re "\r\n$gdb_prompt$" {
-	    pass "set annotate 2"
+	#test annotation_level 2
+	gdb_test_multiple "info line 1" "annotation_level 2" {
+	    -re ".*\032\032post-prompt.*Line 1 of .* is at address .* but contains no code.*:1:0:beg:0x.*\032\032pre-prompt.*$old_gdb_prompt .*\032\032prompt.*$" {
+		pass "annotation_level 2"
+	    }
 	}
     }
 
-    gdb_test_multiple "show annotate" "show annotate 2" {
-	-re ".*\032\032post-prompt.*Annotation_level is 2..*\032\032pre-prompt.*$old_gdb_prompt .*\032\032prompt.*$" {
-	    pass "show annotate 2"
+    #test set annotate 1
+    gdb_test "set annotate 1" ".*post-prompt.*"
+    gdb_test "show annotate" "Annotation_level is 1..*"  "show annotate (1)"
+    #test annotation_level 1
+    gdb_test "info line 1" "Line 1 of .* is at address .* but contains no code.*:1:0:beg:0x.*"  "annotation_level 1"
+    #test set annotate 0
+    gdb_test_no_output "set annotate 0" "set annotate 0"
+    gdb_test "show annotate" "Annotation_level is 0..*"  "show annotate (0)"
+    #test annotation_level 0
+    gdb_test "info line 1" "Line 1 of .* is at address .* but contains no code.*"  "annotation_level 0"
+}
+
+proc_with_prefix test_setshow_args {} {
+    clean_restart $::binfile
+
+    if ![runto_main] then {
+	fail "can't run to main"
+	return
+    }
+
+    gdb_test "show args" "Argument list to give program being debugged when it is started is \"\"\."
+    gdb_test "p \$_gdb_setting_str(\"args\")" " = \"\""
+
+    gdb_test_no_output "set args ~"
+    gdb_test "show args" "Argument list to give program being debugged when it is started is \"~\"..*" \
+	"show args ~"
+
+    #test set args
+    gdb_test_no_output "set args foo bar blup baz bubble" "set args"
+
+    #test show args
+    gdb_test "show args" "Argument list to give program being debugged when it is started is \"foo bar blup baz bubble\"..*"
+    gdb_test "p \$_gdb_setting(\"args\")" " = \"foo bar blup baz bubble\"" \
+	"_gdb_setting args"
+
+    # Don't test if we can't pass args or if we're using a stub.
+    if { !$::use_gdb_stub && ![target_info exists noargs] } {
+	#test passing args
+	gdb_test "cont" "Continuing.*" "continuing"
+	delete_breakpoints
+	gdb_test "run" "Starting program:.*foo bar blup baz bubble.*" "passing args"
+    }
+}
+
+proc_with_prefix test_setshow_check {} {
+    clean_restart
+
+    #test set check range on
+    # Note: the below might produce a warning, so match anything.
+    gdb_test "set check range on" ""
+
+    gdb_test "p \$_gdb_setting(\"check range\")" " = \"on\"" \
+	"_gdb_setting check range on"
+
+    #test show check range on
+    gdb_test "show check range" "Range checking is \"on\"\..*" "show check range (on)"
+
+    #test set check range off with trailing space
+    gdb_test_no_output "set check range off " "set check range off"
+
+    #test show check range off
+    gdb_test "show check range" "Range checking is \"off\"\..*" "show check range (off)"
+    gdb_test "p \$_gdb_setting(\"check range\")" " = \"off\"" \
+	"_gdb_setting check range off"
+
+    #test set check range auto
+    gdb_test_no_output "set check range auto" "set check range auto"
+
+    #test show check range auto
+    gdb_test "show check range" "Range checking is \"auto; currently .*" "show check range (auto)"
+    gdb_test "p \$_gdb_setting(\"check range\")" " = \"auto\"" \
+	"_gdb_setting check range auto"
+
+    # Test set check type on
+    gdb_test_no_output "set check type on"
+
+    # Test show check type on
+    gdb_test "show check type" "Strict type checking is on\..*" \
+	"show check type (on)"
+    gdb_test "p \$_gdb_setting_str(\"check type\")" " = \"on\"" \
+	"_gdb_setting_str check type on"
+    gdb_test "p \$_gdb_setting(\"check type\")" " = 1" \
+	"_gdb_setting check type on 1"
+
+    # Test set check type off with trailing space
+    gdb_test_no_output "set check type off " "set check type off"
+    gdb_test "p \$_gdb_setting_str(\"check type\")" " = \"off\"" \
+	"_gdb_setting_str check type off"
+    gdb_test "p \$_gdb_setting(\"check type\")" " = 0" \
+	"_gdb_setting check type off 0"
+
+    # Test show check type off
+    gdb_test "show check type" "Strict type checking is off\..*" \
+	"show check type (off)"
+}
+
+proc_with_prefix test_setshow_breakpoint_pending {} {
+    #test set breakpoint pending
+
+    #test set breakpoint pending on
+    gdb_test_no_output "set breakpoint pending on"
+    gdb_test "p \$_gdb_setting_str(\"breakpoint pending\")" " = \"on\"" \
+	"_gdb_setting_str breakpoint pending on"
+    gdb_test "p \$_gdb_setting(\"breakpoint pending\")" " = 1" \
+	"_gdb_setting breakpoint pending 1"
+
+    #test show breakpoint pending on
+    gdb_test "show breakpoint pending" " is on\..*" "show breakpoint pending on"
+
+    #test show breakpoint pending off
+    gdb_test_no_output "set breakpoint pending off"
+    gdb_test "show breakpoint pending" " is off\..*" "show breakpoint pending off"
+    gdb_test "p \$_gdb_setting_str(\"breakpoint pending\")" " = \"off\"" \
+	"_gdb_setting_str breakpoint pending off"
+    gdb_test "p \$_gdb_setting(\"breakpoint pending\")" " = 0" \
+	"_gdb_setting breakpoint pending 0"
+
+    #test set breakpoint pending auto
+    gdb_test_no_output "set breakpoint pending auto"
+
+    #test show breakpoint pending auto
+    gdb_test "show breakpoint pending" " is auto.*" "show breakpoint pending auto"
+    gdb_test "p \$_gdb_setting_str(\"breakpoint pending\")" " = \"auto\"" \
+	"_gdb_setting_str breakpoint pending auto"
+    gdb_test "p \$_gdb_setting(\"breakpoint pending\")" " = -1" \
+	"_gdb_setting breakpoint pending -1"
+}
+
+proc_with_prefix test_setshow_complaints {} {
+    clean_restart
+
+    #test set complaints 100
+    gdb_test_no_output "set complaints 100" "set complaints 100"
+    #test show complaints 100
+    gdb_test "show complaints" "Max number of complaints about incorrect symbols is 100..*" "show complaints (100)"
+    #test set complaints 0
+    gdb_test_no_output "set complaints 0" "set complaints 0"
+    #test show complaints 0
+    gdb_test "show complaints" "Max number of complaints about incorrect symbols is 0..*" "show complaints (0)"
+}
+
+proc_with_prefix test_setshow_confirm {} {
+    clean_restart
+
+    #test set confirm off
+    gdb_test_no_output "set confirm off" "set confirm off"
+    #test show confirm off
+    gdb_test "show confirm" "Whether to confirm potentially dangerous operations is off..*" "show confirm (off)"
+    #test set confirm on
+    gdb_test_no_output "set confirm on" "set confirm on"
+    #test show confirm on
+    gdb_test "show confirm" "Whether to confirm potentially dangerous operations is on..*" "show confirm (on)"
+}
+
+proc_with_prefix test_setshow_editing {} {
+    clean_restart
+
+    #test set editing off
+    gdb_test_no_output "set editing off" "set editing off"
+
+    #test show editing off
+    gdb_test "show editing" "Editing of command lines as they are typed is off..*" "show editing (off)"
+
+    #test set editing on
+    #gdb_test_no_output "set editing on" "set editing on"
+
+    #test show editing on
+    #gdb_test "show editing" "Editing of command lines as they are typed is on..*" "show editing (on)"
+}
+
+proc_with_prefix test_setshow_environment {} {
+    clean_restart
+
+    #test set environment FOOBARBAZ
+    gdb_test_no_output "set environment FOOBARBAZ = grbxgrbxgrbx" \
+	"set environment FOOBARBAZ"
+
+    #test show environment FOOBARBAZ
+    gdb_test "show environment FOOBARBAZ" "FOOBARBAZ = grbxgrbxgrbx.*"
+}
+
+proc_with_prefix test_setshow_height {} {
+    clean_restart
+
+    #test set height 100
+    gdb_test_no_output "set height 100" "set height 100"
+
+    #test show height 100
+    gdb_test "show height" "Number of lines gdb thinks are in a page is 100..*"
+    gdb_test "p \$_gdb_setting_str(\"height\")" " = \"100\"" \
+	"_gdb_setting_str height 100"
+    gdb_test "p \$_gdb_setting(\"height\")" " = 100" \
+	"_gdb_setting height 100"
+
+    # Back to infinite height to avoid pagers.  While at it, check that
+    # literal "unlimited" works just as well as 0.
+    gdb_test_no_output "set height unlimited"
+    gdb_test "p \$_gdb_setting_str(\"height\")" " = \"unlimited\"" \
+	"_gdb_setting_str height unlimited"
+    gdb_test "p \$_gdb_setting(\"height\")" " = 0" \
+	"_gdb_setting height unlimited"
+}
+
+proc_with_prefix test_setshow_history {} {
+    clean_restart
+
+    #test set history expansion on
+    gdb_test_no_output "set history expansion on" "set history expansion on"
+
+    #test show history expansion on
+    gdb_test "show history expansion on" "History expansion on command input is on.*" "show history expansion"
+
+    #get home directory path
+    set HOME ""
+    set test "show environment HOME"
+    gdb_test_multiple $test $test {
+	-re "\nHOME = (\[^\r\n\]*)\[\r\n\]+$::gdb_prompt $" {
+	    set HOME $expect_out(1,string)
+	    pass $test
 	}
     }
 
-    #test annotation_level 2
-    gdb_test_multiple "info line 1" "annotation_level 2" {
-	-re ".*\032\032post-prompt.*Line 1 of .* is at address .* but contains no code.*:1:0:beg:0x.*\032\032pre-prompt.*$old_gdb_prompt .*\032\032prompt.*$" {
-	    pass "annotation_level 2"
+    #test set history filename ~/foobar.baz
+    gdb_test_no_output "set history filename ~/foobar.baz" \
+	"set history filename ~/foobar.baz"
+
+    #test show history filename ~/foobar.baz
+    gdb_test "show history filename" \
+	"The filename in which to record the command history is \"[string_to_regexp $HOME]/foobar.baz\"..*" \
+	"show history filename (~/foobar.baz)"
+    gdb_test "p \$_gdb_setting(\"history filename\")" \
+	" = \"[string_to_regexp $HOME]/foobar.baz\"..*" \
+	"_gdb_setting history filename"
+    gdb_test "p \$_gdb_setting_str(\"history filename\")" \
+	" = \"[string_to_regexp $HOME]/foobar.baz\"..*" \
+	"_gdb_setting_str history filename"
+
+    #get current working directory
+    set PWD ""
+    set test "show working directory"
+    gdb_test_multiple "pwd" $test {
+	-re "\nWorking directory (\[^\r\n\]*)\\.\[\r\n\]+$::gdb_prompt $" {
+	    set PWD $expect_out(1,string)
+	    pass $test
 	}
     }
+
+    #test set history filename foobar.baz
+    gdb_test_no_output "set history filename foobar.baz" \
+	"set history filename foobar.baz"
+
+    #test show history filename foobar.baz
+    gdb_test "show history filename" \
+	"The filename in which to record the command history is \"[string_to_regexp $PWD]/foobar.baz\"..*" \
+	"show history filename (current_directory/foobar.baz)"
+
+    #test set history save on
+    gdb_test_no_output "set history save on" "set history save on"
+
+    #test show history save on
+    gdb_test "show history save" "Saving of the history record on exit is on..*"  "show history save (on)"
+
+    #history saving should stay disabled
+    gdb_test_no_output "set history save off" "set history save off"
+
+    #test set history size 100
+    gdb_test_no_output "set history size 100" "set history size 100"
+
+    #test show history size 100
+    gdb_test "show history size" "The size of the command history is 100..*" "show history size (100)"
 }
 
-#test set annotate 1
-gdb_test "set annotate 1" ".*post-prompt.*"
-gdb_test "show annotate" "Annotation_level is 1..*"  "show annotate (1)" 
-#test annotation_level 1
-gdb_test "info line 1" "Line 1 of .* is at address .* but contains no code.*:1:0:beg:0x.*"  "annotation_level 1" 
-#test set annotate 0
-gdb_test_no_output "set annotate 0" "set annotate 0" 
-gdb_test "show annotate" "Annotation_level is 0..*"  "show annotate (0)" 
-#test annotation_level 0
-gdb_test "info line 1" "Line 1 of .* is at address .* but contains no code.*"  "annotation_level 0" 
-
-gdb_test "show args" "Argument list to give program being debugged when it is started is \"\"\."
-gdb_test "p \$_gdb_setting_str(\"args\")" " = \"\""
-
-gdb_test_no_output "set args ~"
-gdb_test "show args" "Argument list to give program being debugged when it is started is \"~\"..*" \
-    "show args ~"
-#test set args
-gdb_test_no_output "set args foo bar blup baz bubble" "set args" 
-#test show args
-gdb_test "show args" "Argument list to give program being debugged when it is started is \"foo bar blup baz bubble\"..*"
-gdb_test "p \$_gdb_setting(\"args\")" " = \"foo bar blup baz bubble\"" \
-    "_gdb_setting args"
-
-
-# Don't test if we can't pass args or if we're using a stub.
-if { !$use_gdb_stub && ![target_info exists noargs] } {
-    #test passing args
-    gdb_test "cont" "Continuing.*" "continuing"
-    delete_breakpoints
-    gdb_test "run" "Starting program:.*foo bar blup baz bubble.*" "passing args"
+proc_with_prefix test_setshow_language {} {
+    clean_restart
+
+    #test set language asm
+    gdb_test_no_output "set language asm"
+
+    #test show language asm
+    gdb_test "show language" "The current source language is \"asm\"..*" "show language (asm)"
+
+    #test set language rust, with a trailing space
+    gdb_test_no_output "set language rust " "set language rust"
+
+    #test show language rust
+    gdb_test "show language" "The current source language is \"rust\"..*" "show language (rust)"
+
+    #test completion for set language.
+    gdb_test "complete set language min" "set language minimal" \
+      "complete set language minimal"
+
+    #test set language auto
+    gdb_test_no_output "set language auto" "set language auto"
+
+    #test show language
+    gdb_test "show language" "The current source language is \"auto.*\"..*" "show language (auto)"
 }
-#test set check range on
-# Note: the below might produce a warning, so match anything. 
-gdb_test "set check range on" ""
-
-gdb_test "p \$_gdb_setting(\"check range\")" " = \"on\"" \
-    "_gdb_setting check range on"
-#test show check range on
-gdb_test "show check range" "Range checking is \"on\"\..*" "show check range (on)" 
-#test set check range off with trailing space
-gdb_test_no_output "set check range off " "set check range off" 
-#test show check range off
-gdb_test "show check range" "Range checking is \"off\"\..*" "show check range (off)" 
-gdb_test "p \$_gdb_setting(\"check range\")" " = \"off\"" \
-    "_gdb_setting check range off"
-#test set check range auto
-gdb_test_no_output "set check range auto" "set check range auto" 
-#test show check range auto
-gdb_test "show check range" "Range checking is \"auto; currently .*" "show check range (auto)"
-gdb_test "p \$_gdb_setting(\"check range\")" " = \"auto\"" \
-    "_gdb_setting check range auto"
-
-# Test set check type on
-gdb_test_no_output "set check type on"
-
-# Test show check type on
-gdb_test "show check type" "Strict type checking is on\..*" \
-    "show check type (on)"
-gdb_test "p \$_gdb_setting_str(\"check type\")" " = \"on\"" \
-    "_gdb_setting_str check type on"
-gdb_test "p \$_gdb_setting(\"check type\")" " = 1" \
-    "_gdb_setting check type on 1"
-
-# Test set check type off with trailing space
-gdb_test_no_output "set check type off " "set check type off"
-gdb_test "p \$_gdb_setting_str(\"check type\")" " = \"off\"" \
-    "_gdb_setting_str check type off"
-gdb_test "p \$_gdb_setting(\"check type\")" " = 0" \
-    "_gdb_setting check type off 0"
-
-# Test show check type off
-gdb_test "show check type" "Strict type checking is off\..*" \
-    "show check type (off)"
-
-#test set breakpoint pending
-
-#test set breakpoint pending on
-gdb_test_no_output "set breakpoint pending on"
-gdb_test "p \$_gdb_setting_str(\"breakpoint pending\")" " = \"on\"" \
-    "_gdb_setting_str breakpoint pending on"
-gdb_test "p \$_gdb_setting(\"breakpoint pending\")" " = 1" \
-    "_gdb_setting breakpoint pending 1"
-
-#test show breakpoint pending on
-gdb_test "show breakpoint pending" " is on\..*" "show breakpoint pending on"
-
-#test show breakpoint pending off
-gdb_test_no_output "set breakpoint pending off"
-gdb_test "show breakpoint pending" " is off\..*" "show breakpoint pending off"
-gdb_test "p \$_gdb_setting_str(\"breakpoint pending\")" " = \"off\"" \
-    "_gdb_setting_str breakpoint pending off"
-gdb_test "p \$_gdb_setting(\"breakpoint pending\")" " = 0" \
-    "_gdb_setting breakpoint pending 0"
-
-#test set breakpoint pending auto
-gdb_test_no_output "set breakpoint pending auto"
-
-#test show breakpoint pending auto
-gdb_test "show breakpoint pending" " is auto.*" "show breakpoint pending auto"
-gdb_test "p \$_gdb_setting_str(\"breakpoint pending\")" " = \"auto\"" \
-    "_gdb_setting_str breakpoint pending auto"
-gdb_test "p \$_gdb_setting(\"breakpoint pending\")" " = -1" \
-    "_gdb_setting breakpoint pending -1"
-
-
-
-#test set complaints 100
-gdb_test_no_output "set complaints 100" "set complaints 100" 
-#test show complaints 100
-gdb_test "show complaints" "Max number of complaints about incorrect symbols is 100..*" "show complaints (100)" 
-#test set complaints 0
-gdb_test_no_output "set complaints 0" "set complaints 0" 
-#test show complaints 0
-gdb_test "show complaints" "Max number of complaints about incorrect symbols is 0..*" "show complaints (0)" 
-#test set confirm off
-gdb_test_no_output "set confirm off" "set confirm off" 
-#test show confirm off
-gdb_test "show confirm" "Whether to confirm potentially dangerous operations is off..*" "show confirm (off)" 
-#test set confirm on
-gdb_test_no_output "set confirm on" "set confirm on" 
-#test show confirm on
-gdb_test "show confirm" "Whether to confirm potentially dangerous operations is on..*" "show confirm (on)" 
-#test set editing off
-gdb_test_no_output "set editing off" "set editing off" 
-#test show editing off
-gdb_test "show editing" "Editing of command lines as they are typed is off..*" "show editing (off)" 
-#test set editing on
-#gdb_test_no_output "set editing on" "set editing on" 
-#test show editing on
-#gdb_test "show editing" "Editing of command lines as they are typed is on..*" "show editing (on)" 
-#test set environment FOOBARBAZ
-gdb_test_no_output "set environment FOOBARBAZ = grbxgrbxgrbx" \
-    "set environment FOOBARBAZ" 
-#test show environment FOOBARBAZ
-gdb_test "show environment FOOBARBAZ" "FOOBARBAZ = grbxgrbxgrbx.*"
-#test set height 100
-gdb_test_no_output "set height 100" "set height 100" 
-#test show height 100
-gdb_test "show height" "Number of lines gdb thinks are in a page is 100..*"
-gdb_test "p \$_gdb_setting_str(\"height\")" " = \"100\"" \
-    "_gdb_setting_str height 100"
-gdb_test "p \$_gdb_setting(\"height\")" " = 100" \
-    "_gdb_setting height 100"
-# Back to infinite height to avoid pagers.  While at it, check that
-# literal "unlimited" works just as well as 0.
-gdb_test_no_output "set height unlimited"
-gdb_test "p \$_gdb_setting_str(\"height\")" " = \"unlimited\"" \
-    "_gdb_setting_str height unlimited"
-gdb_test "p \$_gdb_setting(\"height\")" " = 0" \
-    "_gdb_setting height unlimited"
-#test set history expansion on
-gdb_test_no_output "set history expansion on" "set history expansion on" 
-#test show history expansion on
-gdb_test "show history expansion on" "History expansion on command input is on.*" "show history expansion" 
-#get home directory path
-set HOME ""
-set test "show environment HOME"
-gdb_test_multiple $test $test {
-    -re "\nHOME = (\[^\r\n\]*)\[\r\n\]+$gdb_prompt $" {
-        set HOME $expect_out(1,string)
-        pass $test
-    }
+
+proc_with_prefix test_setshow_listsize {} {
+    clean_restart
+
+    #test set listsize 100
+    gdb_test_no_output "set listsize 100" "set listsize 100"
+
+    #test show listsize 100
+    gdb_test "show listsize" "Number of source lines gdb will list by default is 100..*" "show listsize (100)"
 }
-#test set history filename ~/foobar.baz
-gdb_test_no_output "set history filename ~/foobar.baz" \
-    "set history filename ~/foobar.baz"
-#test show history filename ~/foobar.baz
-gdb_test "show history filename" \
-    "The filename in which to record the command history is \"[string_to_regexp $HOME]/foobar.baz\"..*" \
-    "show history filename (~/foobar.baz)"
-gdb_test "p \$_gdb_setting(\"history filename\")" \
-    " = \"[string_to_regexp $HOME]/foobar.baz\"..*" \
-    "_gdb_setting history filename"
-gdb_test "p \$_gdb_setting_str(\"history filename\")" \
-    " = \"[string_to_regexp $HOME]/foobar.baz\"..*" \
-    "_gdb_setting_str history filename"
-#get current working directory
-set PWD ""
-set test "show working directory"
-gdb_test_multiple "pwd" $test {
-    -re "\nWorking directory (\[^\r\n\]*)\\.\[\r\n\]+$gdb_prompt $" {
-        set PWD $expect_out(1,string)
-        pass $test
+
+proc_with_prefix test_setshow_prompt {} {
+    clean_restart
+
+    if [board_info target exists gdb_prompt] {
+	return
     }
-}
-#test set history filename foobar.baz
-gdb_test_no_output "set history filename foobar.baz" \
-    "set history filename foobar.baz"
-#test show history filename foobar.baz
-gdb_test "show history filename" \
-    "The filename in which to record the command history is \"[string_to_regexp $PWD]/foobar.baz\"..*" \
-    "show history filename (current_directory/foobar.baz)"
-#test set history save on
-gdb_test_no_output "set history save on" "set history save on" 
-#test show history save on
-gdb_test "show history save" "Saving of the history record on exit is on..*"  "show history save (on)" 
-#history saving should stay disabled
-gdb_test_no_output "set history save off" "set history save off" 
-#test set history size 100
-gdb_test_no_output "set history size 100" "set history size 100" 
-#test show history size 100
-gdb_test "show history size" "The size of the command history is 100..*" "show history size (100)" 
-#test set language asm
-gdb_test_no_output "set language asm"
-#test show language asm
-gdb_test "show language" "The current source language is \"asm\"..*" "show language (asm)" 
-#test set language rust, with a trailing space
-gdb_test_no_output "set language rust " "set language rust"
-#test show language rust
-gdb_test "show language" "The current source language is \"rust\"..*" "show language (rust)" 
-#test completion for set language.
-gdb_test "complete set language min" "set language minimal" \
-  "complete set language minimal"
-#test set language auto
-gdb_test_no_output "set language auto" "set language auto" 
-#test show language
-gdb_test "show language" "The current source language is \"auto.*\"..*" "show language (auto)" 
-#test set listsize 100
-gdb_test_no_output "set listsize 100" "set listsize 100" 
-#test show listsize 100
-gdb_test "show listsize" "Number of source lines gdb will list by default is 100..*" "show listsize (100)" 
-
-if ![board_info target exists gdb_prompt] {
-    #test set prompt (FooBarBaz) 
+
+    #test set prompt (FooBarBaz)
     set newprompt "\\(FooBarBaz\\)"
 
     gdb_test_multiple "set prompt (FooBarBaz) " "set prompt FooBarBaz" {
@@ -305,57 +390,113 @@ if ![board_info target exists gdb_prompt] {
 	}
     }
 
-    #test show prompt (FooBarBaz) 
-
+    #test show prompt (FooBarBaz)
     gdb_test_multiple "show prompt" "show prompt FooBarBaz" {
 	-re "Gdb's prompt is \"$newprompt \"..* $" {
 	    pass "show prompt FooBarBaz"
 	}
     }
 
-    #test set prompt (gdb) 
-
+    #test set prompt (gdb)
     gdb_test_multiple "set prompt (gdb) " "set prompt gdb" {
-	-re "\[\r\n\]$gdb_prompt $" {
+	-re "\[\r\n\]$::gdb_prompt $" {
 	    pass "set prompt gdb"
 	}
     }
 }
 
-#test set radix 11
-gdb_test "set radix 11" "Unsupported output radix ``decimal 11''; output radix unchanged..*"
-#test set radix 16
-gdb_test "set radix 16" "Input and output radices now set to decimal 16, hex 10, octal 20..*"
-#test show radix 16
-gdb_test "show radix" "Input and output radices set to decimal 16, hex 10, octal 20..*"  "show radix (16)" 
-#test set radix 10
-gdb_test "set radix" "Input and output radices now set to decimal 10, hex a, octal 12..*"  "set radix 10" 
-#test show radix 10
-gdb_test "show radix" "Input and output radices set to decimal 10, hex a, octal 12..*" "show radix (10)" 
-#test set width 90
-gdb_test_no_output "set width 90" "set width 90"
-#test show width 90
-gdb_test "show width" "Number of characters gdb thinks are in a line is 90..*" "show width (90)" 
-#test set write on
-# This is only supported on targets which use exec.o.
-gdb_test_no_output "set write on" "set write on" 
-#test show write on
-# This is only supported on targets which use exec.o.
-gdb_test "show write" "Writing into executable and core files is on..*" "show write (on)" 
-#test show user
-gdb_test_no_output "show user" "show user" 
-#test set verbose on
-gdb_test_no_output "set verbose on" "set verbose on" 
-#test show verbose on
-gdb_test "show verbose" "Verbose printing of informational messages is on..*" "show verbose (on)" 
-#test set verbose off
-gdb_test_no_output "set verbose off" "set verbose off" 
-#test show verbose off
-gdb_test "show verbose" "Verbosity is off..*" "show verbose (off)" 
-#test argument must be preceded by space
-foreach x {"history file" "solib-search-path" "data-directory"} {
-    foreach y {"/home/" "~/home" "=home"} {
-        gdb_test "set $x$y" "Argument must be preceded by space." \
-            "$x is not set to $y"
+proc_with_prefix test_setshow_radix {} {
+    clean_restart
+
+    #test set radix 11
+    gdb_test "set radix 11" "Unsupported output radix ``decimal 11''; output radix unchanged..*"
+
+    #test set radix 16
+    gdb_test "set radix 16" "Input and output radices now set to decimal 16, hex 10, octal 20..*"
+
+    #test show radix 16
+    gdb_test "show radix" "Input and output radices set to decimal 16, hex 10, octal 20..*"  "show radix (16)"
+
+    #test set radix 10
+    gdb_test "set radix" "Input and output radices now set to decimal 10, hex a, octal 12..*"  "set radix 10"
+
+    #test show radix 10
+    gdb_test "show radix" "Input and output radices set to decimal 10, hex a, octal 12..*" "show radix (10)"
+}
+
+proc_with_prefix test_setshow_width {} {
+    clean_restart
+
+    #test set width 90
+    gdb_test_no_output "set width 90" "set width 90"
+
+    #test show width 90
+    gdb_test "show width" "Number of characters gdb thinks are in a line is 90..*" "show width (90)"
+}
+
+proc_with_prefix test_setshow_write {} {
+    clean_restart
+
+    #test set write on
+    # This is only supported on targets which use exec.o.
+    gdb_test_no_output "set write on" "set write on"
+
+    #test show write on
+    # This is only supported on targets which use exec.o.
+    gdb_test "show write" "Writing into executable and core files is on..*" "show write (on)"
+}
+
+proc_with_prefix test_show_user {} {
+    clean_restart
+
+    #test show user
+    gdb_test_no_output "show user" "show user"
+}
+
+proc_with_prefix test_setshow_verbose {} {
+    clean_restart
+
+    #test set verbose on
+    gdb_test_no_output "set verbose on" "set verbose on"
+
+    #test show verbose on
+    gdb_test "show verbose" "Verbose printing of informational messages is on..*" "show verbose (on)"
+
+    #test set verbose off
+    gdb_test_no_output "set verbose off" "set verbose off"
+
+    #test show verbose off
+    gdb_test "show verbose" "Verbosity is off..*" "show verbose (off)"
+}
+
+proc_with_prefix test_argument_preceded_by_space {} {
+    clean_restart
+
+    #test argument must be preceded by space
+    foreach x {"history file" "solib-search-path" "data-directory"} {
+	foreach y {"/home/" "~/home" "=home"} {
+	    gdb_test "set $x$y" "Argument must be preceded by space." \
+		"$x is not set to $y"
+	}
     }
 }
+
+test_setshow_annotate
+test_setshow_args
+test_setshow_check
+test_setshow_breakpoint_pending
+test_setshow_complaints
+test_setshow_confirm
+test_setshow_editing
+test_setshow_environment
+test_setshow_height
+test_setshow_history
+test_setshow_language
+test_setshow_listsize
+test_setshow_prompt
+test_setshow_radix
+test_setshow_width
+test_setshow_write
+test_show_user
+test_setshow_verbose
+test_argument_preceded_by_space
-- 
2.32.0


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

* [PATCH 04/16] gdb.base/setshow.exp: fix duplicate test name
  2021-07-14  4:55 [PATCH 00/16] Bunch of commands related cleanups Simon Marchi
                   ` (2 preceding siblings ...)
  2021-07-14  4:55 ` [PATCH 03/16] gdb.base/setshow.exp: split in procs Simon Marchi
@ 2021-07-14  4:55 ` Simon Marchi
  2021-07-14  4:55 ` [PATCH 05/16] gdb: un-share set_inferior_cwd declaration Simon Marchi
                   ` (11 subsequent siblings)
  15 siblings, 0 replies; 50+ messages in thread
From: Simon Marchi @ 2021-07-14  4:55 UTC (permalink / raw)
  To: gdb-patches

Fix:

    DUPLICATE: gdb.base/setshow.exp: test_setshow_args: show args

by giving some explicit test names.

Change-Id: I2a738d3d3675ab9b45929e71f5aee0ea6bf92072
---
 gdb/testsuite/gdb.base/setshow.exp | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/gdb/testsuite/gdb.base/setshow.exp b/gdb/testsuite/gdb.base/setshow.exp
index bfc689b184e5..6e09361c9d70 100644
--- a/gdb/testsuite/gdb.base/setshow.exp
+++ b/gdb/testsuite/gdb.base/setshow.exp
@@ -86,7 +86,8 @@ proc_with_prefix test_setshow_args {} {
 	return
     }
 
-    gdb_test "show args" "Argument list to give program being debugged when it is started is \"\"\."
+    gdb_test "show args" "Argument list to give program being debugged when it is started is \"\"\." \
+	"show args empty"
     gdb_test "p \$_gdb_setting_str(\"args\")" " = \"\""
 
     gdb_test_no_output "set args ~"
@@ -97,7 +98,8 @@ proc_with_prefix test_setshow_args {} {
     gdb_test_no_output "set args foo bar blup baz bubble" "set args"
 
     #test show args
-    gdb_test "show args" "Argument list to give program being debugged when it is started is \"foo bar blup baz bubble\"..*"
+    gdb_test "show args" "Argument list to give program being debugged when it is started is \"foo bar blup baz bubble\"..*" \
+	"show args multiple words"
     gdb_test "p \$_gdb_setting(\"args\")" " = \"foo bar blup baz bubble\"" \
 	"_gdb_setting args"
 
-- 
2.32.0


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

* [PATCH 05/16] gdb: un-share set_inferior_cwd declaration
  2021-07-14  4:55 [PATCH 00/16] Bunch of commands related cleanups Simon Marchi
                   ` (3 preceding siblings ...)
  2021-07-14  4:55 ` [PATCH 04/16] gdb.base/setshow.exp: fix duplicate test name Simon Marchi
@ 2021-07-14  4:55 ` Simon Marchi
  2021-07-14  4:55 ` [PATCH 06/16] gdb: remove inferior::{argc,argv} Simon Marchi
                   ` (10 subsequent siblings)
  15 siblings, 0 replies; 50+ messages in thread
From: Simon Marchi @ 2021-07-14  4:55 UTC (permalink / raw)
  To: gdb-patches

The declaration of set_inferior_cwd is currently shared between gdb and
gdbserver, in gdbsupport/common-inferior.h.  It doesn't need to be, as
set_inferior_cwd is not called from common code.  Only get_inferior_cwd
needs to.

The motivation for this is that a future patch will change the prototype
of set_inferior_cwd in gdb, and I don't want to change it for gdbserver
unnecessarily.  I see this as a good cleanup in any case, to reduce to
just the essential what is shared between GDB and GDBserver.

Change-Id: I3127d27d078f0503ebf5ccc6fddf14f212426a73
---
 gdb/infcmd.c                 | 5 +++--
 gdbserver/inferiors.cc       | 2 +-
 gdbserver/inferiors.h        | 4 ++++
 gdbsupport/common-inferior.h | 4 ----
 4 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 0a5edef69824..a7b520cdd169 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -184,9 +184,10 @@ show_args_command (struct ui_file *file, int from_tty,
   deprecated_show_value_hack (file, from_tty, c, get_inferior_args ());
 }
 
-/* See gdbsupport/common-inferior.h.  */
+/* Set the inferior current working directory.  If CWD is NULL, unset
+   the directory.  */
 
-void
+static void
 set_inferior_cwd (const char *cwd)
 {
   struct inferior *inf = current_inferior ();
diff --git a/gdbserver/inferiors.cc b/gdbserver/inferiors.cc
index 3750090c0a14..0a09de79071d 100644
--- a/gdbserver/inferiors.cc
+++ b/gdbserver/inferiors.cc
@@ -241,7 +241,7 @@ get_inferior_cwd ()
   return current_inferior_cwd;
 }
 
-/* See gdbsupport/common-inferior.h.  */
+/* See inferiors.h.  */
 
 void
 set_inferior_cwd (const char *cwd)
diff --git a/gdbserver/inferiors.h b/gdbserver/inferiors.h
index 7754f745cd7d..3b8959a28cf7 100644
--- a/gdbserver/inferiors.h
+++ b/gdbserver/inferiors.h
@@ -154,4 +154,8 @@ void *thread_target_data (struct thread_info *);
 struct regcache *thread_regcache_data (struct thread_info *);
 void set_thread_regcache_data (struct thread_info *, struct regcache *);
 
+/* Set the inferior current working directory.  If CWD is NULL, unset
+   the directory.  */
+void set_inferior_cwd (const char *cwd);
+
 #endif /* GDBSERVER_INFERIORS_H */
diff --git a/gdbsupport/common-inferior.h b/gdbsupport/common-inferior.h
index 3f1b60fd9629..5e1221277b87 100644
--- a/gdbsupport/common-inferior.h
+++ b/gdbsupport/common-inferior.h
@@ -36,10 +36,6 @@ extern const char *get_exec_file (int err);
    been set, then return NULL.  */
 extern const char *get_inferior_cwd ();
 
-/* Set the inferior current working directory.  If CWD is NULL, unset
-   the directory.  */
-extern void set_inferior_cwd (const char *cwd);
-
 /* Whether to start up the debuggee under a shell.
 
    If startup-with-shell is set, GDB's "run" will attempt to start up
-- 
2.32.0


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

* [PATCH 06/16] gdb: remove inferior::{argc,argv}
  2021-07-14  4:55 [PATCH 00/16] Bunch of commands related cleanups Simon Marchi
                   ` (4 preceding siblings ...)
  2021-07-14  4:55 ` [PATCH 05/16] gdb: un-share set_inferior_cwd declaration Simon Marchi
@ 2021-07-14  4:55 ` Simon Marchi
  2021-07-14  4:55 ` [PATCH 07/16] gdb: add setter/getter for inferior arguments Simon Marchi
                   ` (9 subsequent siblings)
  15 siblings, 0 replies; 50+ messages in thread
From: Simon Marchi @ 2021-07-14  4:55 UTC (permalink / raw)
  To: gdb-patches

There are currently two states that the inferior args can be stored.
The main one is the `args` field, where they are stored as a single
string.  The other one is the `argc`/`argv` fields.

This last one is only used for arguments passed in GDB's
command line.  And the only outcome is that when get_inferior_args is
called, `argc`/`argv` are serialized into `args`.  So really,
`argc`/`argv` is just a staging area before moving the arguments in
`args`.

Simplify this by only keeping the `args` field.  Change
set_inferior_args_vector to immediately serialize the arguments into
`args`, work that would be done in get_inferior_args later anyway.

The only time where this work would be "wasted" is when the user passes
some arguments on the command line, but does not end up running the
program.  But that just seems unlikely.  And it's not that much work.

Change-Id: Ica0b9859397c095f6530350c8fb3c36905f2044a
---
 gdb/infcmd.c   | 24 +++++-------------------
 gdb/inferior.h |  9 ---------
 2 files changed, 5 insertions(+), 28 deletions(-)

diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index a7b520cdd169..05115958a8f8 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -127,16 +127,8 @@ show_inferior_tty_command (struct ui_file *file, int from_tty,
 const char *
 get_inferior_args (void)
 {
-  if (current_inferior ()->argc != 0)
-    {
-      gdb::array_view<char * const> args (current_inferior ()->argv,
-					  current_inferior ()->argc);
-      std::string n = construct_inferior_arguments (args);
-      set_inferior_args (n.c_str ());
-    }
-
-  if (current_inferior ()->args == NULL)
-    current_inferior ()->args = make_unique_xstrdup ("");
+  if (current_inferior ()->args == nullptr)
+    return "";
 
   return current_inferior ()->args.get ();
 }
@@ -151,16 +143,14 @@ set_inferior_args (const char *newargs)
     current_inferior ()->args = make_unique_xstrdup (newargs);
   else
     current_inferior ()->args.reset ();
-
-  current_inferior ()->argc = 0;
-  current_inferior ()->argv = 0;
 }
 
 void
 set_inferior_args_vector (int argc, char **argv)
 {
-  current_inferior ()->argc = argc;
-  current_inferior ()->argv = argv;
+  gdb::array_view<char * const> args (argv, argc);
+  std::string n = construct_inferior_arguments (args);
+  set_inferior_args (n.c_str ());
 }
 
 /* Notice when `set args' is run.  */
@@ -490,15 +480,11 @@ run_command_1 (const char *args, int from_tty, enum run_how run_how)
       if (exec_file)
 	uiout->field_string ("execfile", exec_file);
       uiout->spaces (1);
-      /* We call get_inferior_args() because we might need to compute
-	 the value now.  */
       uiout->field_string ("infargs", get_inferior_args ());
       uiout->text ("\n");
       uiout->flush ();
     }
 
-  /* We call get_inferior_args() because we might need to compute
-     the value now.  */
   run_target->create_inferior (exec_file,
 			       std::string (get_inferior_args ()),
 			       current_inferior ()->environment.envp (),
diff --git a/gdb/inferior.h b/gdb/inferior.h
index 6662a3bde463..af03887d63b3 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -478,15 +478,6 @@ class inferior : public refcounted_object,
   /* The arguments string to use when running.  */
   gdb::unique_xmalloc_ptr<char> args;
 
-  /* The size of elements in argv.  */
-  int argc = 0;
-
-  /* The vector version of arguments.  If ARGC is nonzero,
-     then we must compute ARGS from this (via the target).
-     This is always coming from main's argv and therefore
-     should never be freed.  */
-  char **argv = NULL;
-
   /* The current working directory that will be used when starting
      this inferior.  */
   gdb::unique_xmalloc_ptr<char> cwd;
-- 
2.32.0


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

* [PATCH 07/16] gdb: add setter/getter for inferior arguments
  2021-07-14  4:55 [PATCH 00/16] Bunch of commands related cleanups Simon Marchi
                   ` (5 preceding siblings ...)
  2021-07-14  4:55 ` [PATCH 06/16] gdb: remove inferior::{argc,argv} Simon Marchi
@ 2021-07-14  4:55 ` Simon Marchi
  2021-07-14  4:55 ` [PATCH 08/16] gdb: add setter/getter for inferior cwd Simon Marchi
                   ` (8 subsequent siblings)
  15 siblings, 0 replies; 50+ messages in thread
From: Simon Marchi @ 2021-07-14  4:55 UTC (permalink / raw)
  To: gdb-patches

Add args/set_args to the inferior class, remove the set_inferior_args
and get_inferior_args functions, that would just be wrappers around
them.

Change-Id: If87d52f3402ce08be26c32897ae8915d9f6d1ea3
---
 gdb/elf-none-tdep.c |  2 +-
 gdb/fbsd-tdep.c     |  2 +-
 gdb/infcmd.c        | 34 +++++++---------------------------
 gdb/inferior.h      | 36 +++++++++++++++++++++++++++++-------
 gdb/linux-tdep.c    |  3 +--
 gdb/procfs.c        |  2 +-
 6 files changed, 40 insertions(+), 39 deletions(-)

diff --git a/gdb/elf-none-tdep.c b/gdb/elf-none-tdep.c
index 52a6281290fa..0d94dc430403 100644
--- a/gdb/elf-none-tdep.c
+++ b/gdb/elf-none-tdep.c
@@ -49,7 +49,7 @@ elf_none_make_corefile_notes (struct gdbarch *gdbarch, bfd *obfd,
       fname = lbasename (exe);
       psargs = std::string (exe);
 
-      const char *infargs = get_inferior_args ();
+      const char *infargs = current_inferior ()->args ();
       if (infargs != nullptr)
 	psargs += " " + std::string (infargs);
 
diff --git a/gdb/fbsd-tdep.c b/gdb/fbsd-tdep.c
index eb792c36a54d..141d23662838 100644
--- a/gdb/fbsd-tdep.c
+++ b/gdb/fbsd-tdep.c
@@ -684,7 +684,7 @@ fbsd_make_corefile_notes (struct gdbarch *gdbarch, bfd *obfd, int *note_size)
       const char *fname = lbasename (get_exec_file (0));
       std::string psargs = fname;
 
-      const char *infargs = get_inferior_args ();
+      const char *infargs = current_inferior ()->args ();
       if (infargs != NULL)
 	psargs = psargs + " " + infargs;
 
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 05115958a8f8..1407a3ea4664 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -124,33 +124,12 @@ show_inferior_tty_command (struct ui_file *file, int from_tty,
 		      "is \"%s\".\n"), inferior_tty);
 }
 
-const char *
-get_inferior_args (void)
-{
-  if (current_inferior ()->args == nullptr)
-    return "";
-
-  return current_inferior ()->args.get ();
-}
-
-/* Set the arguments for the current inferior.  Ownership of
-   NEWARGS is not transferred.  */
-
-void
-set_inferior_args (const char *newargs)
-{
-  if (newargs != nullptr)
-    current_inferior ()->args = make_unique_xstrdup (newargs);
-  else
-    current_inferior ()->args.reset ();
-}
-
 void
 set_inferior_args_vector (int argc, char **argv)
 {
   gdb::array_view<char * const> args (argv, argc);
   std::string n = construct_inferior_arguments (args);
-  set_inferior_args (n.c_str ());
+  current_inferior ()->set_args (n.c_str ());
 }
 
 /* Notice when `set args' is run.  */
@@ -160,7 +139,7 @@ set_args_command (const char *args, int from_tty, struct cmd_list_element *c)
 {
   /* CLI has assigned the user-provided value to inferior_args_scratch.
      Now route it to current inferior.  */
-  set_inferior_args (inferior_args_scratch);
+  current_inferior ()->set_args (inferior_args_scratch);
 }
 
 /* Notice when `show args' is run.  */
@@ -171,7 +150,8 @@ show_args_command (struct ui_file *file, int from_tty,
 {
   /* Note that we ignore the passed-in value in favor of computing it
      directly.  */
-  deprecated_show_value_hack (file, from_tty, c, get_inferior_args ());
+  deprecated_show_value_hack (file, from_tty, c,
+			      current_inferior ()->args ());
 }
 
 /* Set the inferior current working directory.  If CWD is NULL, unset
@@ -471,7 +451,7 @@ run_command_1 (const char *args, int from_tty, enum run_how run_how)
 
   /* If there were other args, beside '&', process them.  */
   if (args != NULL)
-    set_inferior_args (args);
+    current_inferior ()->set_args (args);
 
   if (from_tty)
     {
@@ -480,13 +460,13 @@ run_command_1 (const char *args, int from_tty, enum run_how run_how)
       if (exec_file)
 	uiout->field_string ("execfile", exec_file);
       uiout->spaces (1);
-      uiout->field_string ("infargs", get_inferior_args ());
+      uiout->field_string ("infargs", current_inferior ()->args ());
       uiout->text ("\n");
       uiout->flush ();
     }
 
   run_target->create_inferior (exec_file,
-			       std::string (get_inferior_args ()),
+			       current_inferior ()->args (),
 			       current_inferior ()->environment.envp (),
 			       from_tty);
   /* to_create_inferior should push the target, so after this point we
diff --git a/gdb/inferior.h b/gdb/inferior.h
index af03887d63b3..c90abae73dbe 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -204,10 +204,6 @@ extern void post_create_inferior (int from_tty);
 
 extern void attach_command (const char *, int);
 
-extern const char *get_inferior_args (void);
-
-extern void set_inferior_args (const char *);
-
 extern void set_inferior_args_vector (int, char **);
 
 extern void registers_info (const char *, int);
@@ -446,6 +442,30 @@ class inferior : public refcounted_object,
   void set_tty (const char *terminal_name);
   const char *tty ();
 
+  /* Set the argument string to use when running this inferior.
+
+     Either nullptr or an empty string can be used to represent "no
+     arguments".  */
+  void set_args (const char *args)
+  {
+    if (args != nullptr && args[0] != '\0')
+      m_args = make_unique_xstrdup (args);
+    else
+      m_args.reset ();
+  };
+
+  /* Get the argument string to use when running this inferior.
+
+     The return value is always non-nullptr.  No arguments is represented by
+     an empty string.  */
+  const char *args () const
+  {
+    if (m_args == nullptr)
+      return "";
+
+    return m_args.get ();
+  }
+
   /* Convenient handle (GDB inferior id).  Unique across all
      inferiors.  */
   int num = 0;
@@ -475,9 +495,6 @@ class inferior : public refcounted_object,
   /* The program space bound to this inferior.  */
   struct program_space *pspace = NULL;
 
-  /* The arguments string to use when running.  */
-  gdb::unique_xmalloc_ptr<char> args;
-
   /* The current working directory that will be used when starting
      this inferior.  */
   gdb::unique_xmalloc_ptr<char> cwd;
@@ -569,6 +586,11 @@ class inferior : public refcounted_object,
 
   /* The list of continuations.  */
   std::list<std::function<void ()>> m_continuations;
+
+  /* The arguments string to use when running.
+
+     This is nullptr when there are not args.  */
+  gdb::unique_xmalloc_ptr<char> m_args;
 };
 
 /* Keep a registry of per-inferior data-pointers required by other GDB
diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
index 8c0885ed5b49..99e868eed6e6 100644
--- a/gdb/linux-tdep.c
+++ b/gdb/linux-tdep.c
@@ -1804,7 +1804,6 @@ linux_fill_prpsinfo (struct elf_internal_linux_prpsinfo *p)
   char filename[100];
   /* The basename of the executable.  */
   const char *basename;
-  const char *infargs;
   /* Temporary buffer.  */
   char *tmpstr;
   /* The valid states of a process, according to the Linux kernel.  */
@@ -1848,7 +1847,7 @@ linux_fill_prpsinfo (struct elf_internal_linux_prpsinfo *p)
   strncpy (p->pr_fname, basename, sizeof (p->pr_fname) - 1);
   p->pr_fname[sizeof (p->pr_fname) - 1] = '\0';
 
-  infargs = get_inferior_args ();
+  const char *infargs = current_inferior ()->args ();
 
   /* The arguments of the program.  */
   std::string psargs = fname.get ();
diff --git a/gdb/procfs.c b/gdb/procfs.c
index 529ee33df900..d764c95d422a 100644
--- a/gdb/procfs.c
+++ b/gdb/procfs.c
@@ -3613,7 +3613,7 @@ procfs_target::make_corefile_notes (bfd *obfd, int *note_size)
       strncpy (psargs, get_exec_file (0), sizeof (psargs));
       psargs[sizeof (psargs) - 1] = 0;
 
-      inf_args = get_inferior_args ();
+      inf_args = current_inferior ()->args ();
       if (inf_args && *inf_args
 	  && (strlen (inf_args)
 	      < ((int) sizeof (psargs) - (int) strlen (psargs))))
-- 
2.32.0


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

* [PATCH 08/16] gdb: add setter/getter for inferior cwd
  2021-07-14  4:55 [PATCH 00/16] Bunch of commands related cleanups Simon Marchi
                   ` (6 preceding siblings ...)
  2021-07-14  4:55 ` [PATCH 07/16] gdb: add setter/getter for inferior arguments Simon Marchi
@ 2021-07-14  4:55 ` Simon Marchi
  2021-07-14  4:55 ` [PATCH 09/16] gdb: make inferior::m_args an std::string Simon Marchi
                   ` (7 subsequent siblings)
  15 siblings, 0 replies; 50+ messages in thread
From: Simon Marchi @ 2021-07-14  4:55 UTC (permalink / raw)
  To: gdb-patches

Add cwd/set_cwd to the inferior class, remove set_inferior_args.
Keep get_inferior_args, because it is used from fork_inferior, in shared
code.  The cwd could eventually be passed as a parameter eventually,
though, I think that would be cleaner.

Change-Id: Ifb72ea865d7e6f9a491308f0d5c1595579d8427e
---
 gdb/infcmd.c      | 24 ++++--------------------
 gdb/inferior.h    | 27 +++++++++++++++++++++++----
 gdb/remote.c      |  2 +-
 gdb/windows-nat.c |  2 +-
 4 files changed, 29 insertions(+), 26 deletions(-)

diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 1407a3ea4664..be6031d29364 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -154,28 +154,12 @@ show_args_command (struct ui_file *file, int from_tty,
 			      current_inferior ()->args ());
 }
 
-/* Set the inferior current working directory.  If CWD is NULL, unset
-   the directory.  */
-
-static void
-set_inferior_cwd (const char *cwd)
-{
-  struct inferior *inf = current_inferior ();
-
-  gdb_assert (inf != NULL);
-
-  if (cwd == NULL)
-    inf->cwd.reset ();
-  else
-    inf->cwd.reset (xstrdup (cwd));
-}
-
 /* See gdbsupport/common-inferior.h.  */
 
 const char *
 get_inferior_cwd ()
 {
-  return current_inferior ()->cwd.get ();
+  return current_inferior ()->cwd ();
 }
 
 /* Handle the 'set cwd' command.  */
@@ -184,9 +168,9 @@ static void
 set_cwd_command (const char *args, int from_tty, struct cmd_list_element *c)
 {
   if (*inferior_cwd_scratch == '\0')
-    set_inferior_cwd (NULL);
+    current_inferior ()->set_cwd (nullptr);
   else
-    set_inferior_cwd (inferior_cwd_scratch);
+    current_inferior ()->set_cwd (inferior_cwd_scratch);
 }
 
 /* Handle the 'show cwd' command.  */
@@ -195,7 +179,7 @@ static void
 show_cwd_command (struct ui_file *file, int from_tty,
 		  struct cmd_list_element *c, const char *value)
 {
-  const char *cwd = get_inferior_cwd ();
+  const char *cwd = current_inferior ()->cwd ();
 
   if (cwd == NULL)
     fprintf_filtered (gdb_stdout,
diff --git a/gdb/inferior.h b/gdb/inferior.h
index c90abae73dbe..0b28e7e4766d 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -466,6 +466,25 @@ class inferior : public refcounted_object,
     return m_args.get ();
   }
 
+  /* Set the inferior current working directory.
+
+     If CWD is NULL, unset the directory.  */
+  void set_cwd (const char *cwd)
+  {
+    if (cwd == NULL)
+      m_cwd.reset ();
+    else
+      m_cwd.reset (xstrdup (cwd));
+  }
+
+  /* Get the inferior current working directory.
+
+     Return nullptr if the current working directory is not specified.  */
+  const char *cwd () const
+  {
+    return m_cwd.get ();
+  }
+
   /* Convenient handle (GDB inferior id).  Unique across all
      inferiors.  */
   int num = 0;
@@ -495,10 +514,6 @@ class inferior : public refcounted_object,
   /* The program space bound to this inferior.  */
   struct program_space *pspace = NULL;
 
-  /* The current working directory that will be used when starting
-     this inferior.  */
-  gdb::unique_xmalloc_ptr<char> cwd;
-
   /* The terminal state as set by the last target_terminal::terminal_*
      call.  */
   target_terminal_state terminal_state = target_terminal_state::is_ours;
@@ -591,6 +606,10 @@ class inferior : public refcounted_object,
 
      This is nullptr when there are not args.  */
   gdb::unique_xmalloc_ptr<char> m_args;
+
+  /* The current working directory that will be used when starting
+     this inferior.  */
+  gdb::unique_xmalloc_ptr<char> m_cwd;
 };
 
 /* Keep a registry of per-inferior data-pointers required by other GDB
diff --git a/gdb/remote.c b/gdb/remote.c
index 96e2750cd322..a0726a00f05c 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -10397,7 +10397,7 @@ remote_target::extended_remote_set_inferior_cwd ()
 {
   if (packet_support (PACKET_QSetWorkingDir) != PACKET_DISABLE)
     {
-      const char *inferior_cwd = get_inferior_cwd ();
+      const char *inferior_cwd = current_inferior ()->cwd ();
       remote_state *rs = get_remote_state ();
 
       if (inferior_cwd != NULL)
diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c
index 05d95ea7d6ad..9526149311b1 100644
--- a/gdb/windows-nat.c
+++ b/gdb/windows-nat.c
@@ -2558,7 +2558,7 @@ windows_nat_target::create_inferior (const char *exec_file,
   if (!exec_file)
     error (_("No executable specified, use `target exec'."));
 
-  const char *inferior_cwd = get_inferior_cwd ();
+  const char *inferior_cwd = current_inferior ()->cwd ();
   std::string expanded_infcwd;
   if (inferior_cwd != NULL)
     {
-- 
2.32.0


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

* [PATCH 09/16] gdb: make inferior::m_args an std::string
  2021-07-14  4:55 [PATCH 00/16] Bunch of commands related cleanups Simon Marchi
                   ` (7 preceding siblings ...)
  2021-07-14  4:55 ` [PATCH 08/16] gdb: add setter/getter for inferior cwd Simon Marchi
@ 2021-07-14  4:55 ` Simon Marchi
  2021-07-14  4:55 ` [PATCH 10/16] gdb: make inferior::m_cwd " Simon Marchi
                   ` (6 subsequent siblings)
  15 siblings, 0 replies; 50+ messages in thread
From: Simon Marchi @ 2021-07-14  4:55 UTC (permalink / raw)
  To: gdb-patches

With the current code, both a NULL pointer and an empty string can mean
"no arguments".  We don't need this distinction.  Changing to a string
has the advantage that there is now a single state for that (an empty
string), which makes the code a bit simpler in my opinion.

Change-Id: Icdc622820f7869478791dbaa84b4a1c7fec21ced
---
 gdb/elf-none-tdep.c |  6 +++---
 gdb/fbsd-tdep.c     |  6 +++---
 gdb/infcmd.c        |  4 ++--
 gdb/inferior.h      | 26 ++++++++------------------
 gdb/linux-tdep.c    |  6 +++---
 gdb/procfs.c        | 10 ++++------
 6 files changed, 23 insertions(+), 35 deletions(-)

diff --git a/gdb/elf-none-tdep.c b/gdb/elf-none-tdep.c
index 0d94dc430403..1802ffb1ac09 100644
--- a/gdb/elf-none-tdep.c
+++ b/gdb/elf-none-tdep.c
@@ -49,9 +49,9 @@ elf_none_make_corefile_notes (struct gdbarch *gdbarch, bfd *obfd,
       fname = lbasename (exe);
       psargs = std::string (exe);
 
-      const char *infargs = current_inferior ()->args ();
-      if (infargs != nullptr)
-	psargs += " " + std::string (infargs);
+      const std::string &infargs = current_inferior ()->args ();
+      if (!infargs.empty ())
+	psargs += ' ' + infargs;
 
       /* All existing targets that handle writing out prpsinfo expect the
 	 fname and psargs strings to be at least 16 and 80 characters long
diff --git a/gdb/fbsd-tdep.c b/gdb/fbsd-tdep.c
index 141d23662838..07cd844c818f 100644
--- a/gdb/fbsd-tdep.c
+++ b/gdb/fbsd-tdep.c
@@ -684,9 +684,9 @@ fbsd_make_corefile_notes (struct gdbarch *gdbarch, bfd *obfd, int *note_size)
       const char *fname = lbasename (get_exec_file (0));
       std::string psargs = fname;
 
-      const char *infargs = current_inferior ()->args ();
-      if (infargs != NULL)
-	psargs = psargs + " " + infargs;
+      const std::string &infargs = current_inferior ()->args ();
+      if (!infargs.empty ())
+	psargs += ' ' + infargs;
 
       note_data.reset (elfcore_write_prpsinfo (obfd, note_data.release (),
 					       note_size, fname,
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index be6031d29364..2d0d6cc3e965 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -129,7 +129,7 @@ set_inferior_args_vector (int argc, char **argv)
 {
   gdb::array_view<char * const> args (argv, argc);
   std::string n = construct_inferior_arguments (args);
-  current_inferior ()->set_args (n.c_str ());
+  current_inferior ()->set_args (std::move (n));
 }
 
 /* Notice when `set args' is run.  */
@@ -151,7 +151,7 @@ show_args_command (struct ui_file *file, int from_tty,
   /* Note that we ignore the passed-in value in favor of computing it
      directly.  */
   deprecated_show_value_hack (file, from_tty, c,
-			      current_inferior ()->args ());
+			      current_inferior ()->args ().c_str ());
 }
 
 /* See gdbsupport/common-inferior.h.  */
diff --git a/gdb/inferior.h b/gdb/inferior.h
index 0b28e7e4766d..6c1e08a7671b 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -444,26 +444,18 @@ class inferior : public refcounted_object,
 
   /* Set the argument string to use when running this inferior.
 
-     Either nullptr or an empty string can be used to represent "no
-     arguments".  */
-  void set_args (const char *args)
+     An empty string can be used to represent "no arguments".  */
+  void set_args (std::string args)
   {
-    if (args != nullptr && args[0] != '\0')
-      m_args = make_unique_xstrdup (args);
-    else
-      m_args.reset ();
+    m_args = std::move (args);
   };
 
   /* Get the argument string to use when running this inferior.
 
-     The return value is always non-nullptr.  No arguments is represented by
-     an empty string.  */
-  const char *args () const
+     No arguments is represented by an empty string.  */
+  const std::string &args () const
   {
-    if (m_args == nullptr)
-      return "";
-
-    return m_args.get ();
+    return m_args;
   }
 
   /* Set the inferior current working directory.
@@ -602,10 +594,8 @@ class inferior : public refcounted_object,
   /* The list of continuations.  */
   std::list<std::function<void ()>> m_continuations;
 
-  /* The arguments string to use when running.
-
-     This is nullptr when there are not args.  */
-  gdb::unique_xmalloc_ptr<char> m_args;
+  /* The arguments string to use when running.  */
+  std::string m_args;
 
   /* The current working directory that will be used when starting
      this inferior.  */
diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
index 99e868eed6e6..637d3d36a0be 100644
--- a/gdb/linux-tdep.c
+++ b/gdb/linux-tdep.c
@@ -1847,12 +1847,12 @@ linux_fill_prpsinfo (struct elf_internal_linux_prpsinfo *p)
   strncpy (p->pr_fname, basename, sizeof (p->pr_fname) - 1);
   p->pr_fname[sizeof (p->pr_fname) - 1] = '\0';
 
-  const char *infargs = current_inferior ()->args ();
+  const std::string &infargs = current_inferior ()->args ();
 
   /* The arguments of the program.  */
   std::string psargs = fname.get ();
-  if (infargs != NULL)
-    psargs = psargs + " " + infargs;
+  if (!infargs.empty ())
+    psargs += ' ' + infargs;
 
   strncpy (p->pr_psargs, psargs.c_str (), sizeof (p->pr_psargs) - 1);
   p->pr_psargs[sizeof (p->pr_psargs) - 1] = '\0';
diff --git a/gdb/procfs.c b/gdb/procfs.c
index d764c95d422a..aa441284c7b8 100644
--- a/gdb/procfs.c
+++ b/gdb/procfs.c
@@ -3603,7 +3603,6 @@ procfs_target::make_corefile_notes (bfd *obfd, int *note_size)
   char psargs[80] = {'\0'};
   procinfo *pi = find_procinfo_or_die (inferior_ptid.pid (), 0);
   gdb::unique_xmalloc_ptr<char> note_data;
-  const char *inf_args;
   enum gdb_signal stop_signal;
 
   if (get_exec_file (0))
@@ -3613,14 +3612,13 @@ procfs_target::make_corefile_notes (bfd *obfd, int *note_size)
       strncpy (psargs, get_exec_file (0), sizeof (psargs));
       psargs[sizeof (psargs) - 1] = 0;
 
-      inf_args = current_inferior ()->args ();
-      if (inf_args && *inf_args
-	  && (strlen (inf_args)
-	      < ((int) sizeof (psargs) - (int) strlen (psargs))))
+      const std::string &inf_args = current_inferior ()->args ();
+      if (!inf_args.empty () &&
+	  inf_args.length () < ((int) sizeof (psargs) - (int) strlen (psargs)))
 	{
 	  strncat (psargs, " ",
 		   sizeof (psargs) - strlen (psargs));
-	  strncat (psargs, inf_args,
+	  strncat (psargs, inf_args.c_str (),
 		   sizeof (psargs) - strlen (psargs));
 	}
     }
-- 
2.32.0


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

* [PATCH 10/16] gdb: make inferior::m_cwd an std::string
  2021-07-14  4:55 [PATCH 00/16] Bunch of commands related cleanups Simon Marchi
                   ` (8 preceding siblings ...)
  2021-07-14  4:55 ` [PATCH 09/16] gdb: make inferior::m_args an std::string Simon Marchi
@ 2021-07-14  4:55 ` Simon Marchi
  2021-07-14  4:55 ` [PATCH 11/16] gdb: make inferior::m_terminal " Simon Marchi
                   ` (5 subsequent siblings)
  15 siblings, 0 replies; 50+ messages in thread
From: Simon Marchi @ 2021-07-14  4:55 UTC (permalink / raw)
  To: gdb-patches

Same idea as the previous patch, but for m_cwd.

To keep things consistent across the board, change get_inferior_cwd as
well, which is shared with GDBserver.  So update the related GDBserver
code too.

Change-Id: Ia2c047fda738d45f3d18bc999eb67ceb8400ce4e
---
 gdb/infcmd.c                 | 14 ++++++--------
 gdb/inferior.h               | 18 ++++++++----------
 gdb/nat/fork-inferior.c      | 15 ++++++---------
 gdb/remote.c                 |  9 +++++----
 gdbserver/inferiors.cc       | 16 +++++++---------
 gdbserver/inferiors.h        |  4 ++--
 gdbserver/server.cc          |  6 +++---
 gdbsupport/common-inferior.h |  7 ++++---
 8 files changed, 41 insertions(+), 48 deletions(-)

diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 2d0d6cc3e965..c02717631643 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -156,7 +156,7 @@ show_args_command (struct ui_file *file, int from_tty,
 
 /* See gdbsupport/common-inferior.h.  */
 
-const char *
+const std::string &
 get_inferior_cwd ()
 {
   return current_inferior ()->cwd ();
@@ -167,10 +167,7 @@ get_inferior_cwd ()
 static void
 set_cwd_command (const char *args, int from_tty, struct cmd_list_element *c)
 {
-  if (*inferior_cwd_scratch == '\0')
-    current_inferior ()->set_cwd (nullptr);
-  else
-    current_inferior ()->set_cwd (inferior_cwd_scratch);
+  current_inferior ()->set_cwd (inferior_cwd_scratch);
 }
 
 /* Handle the 'show cwd' command.  */
@@ -179,9 +176,9 @@ static void
 show_cwd_command (struct ui_file *file, int from_tty,
 		  struct cmd_list_element *c, const char *value)
 {
-  const char *cwd = current_inferior ()->cwd ();
+  const std::string &cwd = current_inferior ()->cwd ();
 
-  if (cwd == NULL)
+  if (cwd.empty ())
     fprintf_filtered (gdb_stdout,
 		      _("\
 You have not set the inferior's current working directory.\n\
@@ -190,7 +187,8 @@ server's cwd if remote debugging.\n"));
   else
     fprintf_filtered (gdb_stdout,
 		      _("Current working directory that will be used "
-			"when starting the inferior is \"%s\".\n"), cwd);
+			"when starting the inferior is \"%s\".\n"),
+		      cwd.c_str ());
 }
 
 
diff --git a/gdb/inferior.h b/gdb/inferior.h
index 6c1e08a7671b..c52c38caa93e 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -460,21 +460,19 @@ class inferior : public refcounted_object,
 
   /* Set the inferior current working directory.
 
-     If CWD is NULL, unset the directory.  */
-  void set_cwd (const char *cwd)
+     If CWD is empty, unset the directory.  */
+  void set_cwd (std::string cwd)
   {
-    if (cwd == NULL)
-      m_cwd.reset ();
-    else
-      m_cwd.reset (xstrdup (cwd));
+    m_cwd = std::move (cwd);
   }
 
   /* Get the inferior current working directory.
 
-     Return nullptr if the current working directory is not specified.  */
-  const char *cwd () const
+     Return an empty string if the current working directory is not
+     specified.  */
+  const std::string &cwd () const
   {
-    return m_cwd.get ();
+    return m_cwd;
   }
 
   /* Convenient handle (GDB inferior id).  Unique across all
@@ -599,7 +597,7 @@ class inferior : public refcounted_object,
 
   /* The current working directory that will be used when starting
      this inferior.  */
-  gdb::unique_xmalloc_ptr<char> m_cwd;
+  std::string m_cwd;
 };
 
 /* Keep a registry of per-inferior data-pointers required by other GDB
diff --git a/gdb/nat/fork-inferior.c b/gdb/nat/fork-inferior.c
index d280e1120ccd..c88cf4cf7165 100644
--- a/gdb/nat/fork-inferior.c
+++ b/gdb/nat/fork-inferior.c
@@ -281,8 +281,6 @@ fork_inferior (const char *exec_file_arg, const std::string &allargs,
   char **save_our_env;
   int i;
   int save_errno;
-  const char *inferior_cwd;
-  std::string expanded_inferior_cwd;
 
   /* If no exec file handed to us, get it from the exec-file command
      -- with a good, common error message if none is specified.  */
@@ -326,14 +324,13 @@ fork_inferior (const char *exec_file_arg, const std::string &allargs,
 
   /* Check if the user wants to set a different working directory for
      the inferior.  */
-  inferior_cwd = get_inferior_cwd ();
+  std::string inferior_cwd = get_inferior_cwd ();
 
-  if (inferior_cwd != NULL)
+  if (!inferior_cwd.empty ())
     {
       /* Expand before forking because between fork and exec, the child
 	 process may only execute async-signal-safe operations.  */
-      expanded_inferior_cwd = gdb_tilde_expand (inferior_cwd);
-      inferior_cwd = expanded_inferior_cwd.c_str ();
+      inferior_cwd = gdb_tilde_expand (inferior_cwd.c_str ());
     }
 
   /* If there's any initialization of the target layers that must
@@ -373,10 +370,10 @@ fork_inferior (const char *exec_file_arg, const std::string &allargs,
 
       /* Change to the requested working directory if the user
 	 requested it.  */
-      if (inferior_cwd != NULL)
+      if (!inferior_cwd.empty ())
 	{
-	  if (chdir (inferior_cwd) < 0)
-	    trace_start_error_with_name (inferior_cwd);
+	  if (chdir (inferior_cwd.c_str ()) < 0)
+	    trace_start_error_with_name (inferior_cwd.c_str ());
 	}
 
       if (debug_fork)
diff --git a/gdb/remote.c b/gdb/remote.c
index a0726a00f05c..e2a08c9a113d 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -10397,13 +10397,14 @@ remote_target::extended_remote_set_inferior_cwd ()
 {
   if (packet_support (PACKET_QSetWorkingDir) != PACKET_DISABLE)
     {
-      const char *inferior_cwd = current_inferior ()->cwd ();
+      const std::string &inferior_cwd = current_inferior ()->cwd ();
       remote_state *rs = get_remote_state ();
 
-      if (inferior_cwd != NULL)
+      if (!inferior_cwd.empty ())
 	{
-	  std::string hexpath = bin2hex ((const gdb_byte *) inferior_cwd,
-					 strlen (inferior_cwd));
+	  std::string hexpath
+	    = bin2hex ((const gdb_byte *) inferior_cwd.data (),
+		       inferior_cwd.size ());
 
 	  xsnprintf (rs->buf.data (), get_remote_packet_size (),
 		     "QSetWorkingDir:%s", hexpath.c_str ());
diff --git a/gdbserver/inferiors.cc b/gdbserver/inferiors.cc
index 0a09de79071d..32f847812e60 100644
--- a/gdbserver/inferiors.cc
+++ b/gdbserver/inferiors.cc
@@ -28,8 +28,10 @@ std::list<thread_info *> all_threads;
 
 struct thread_info *current_thread;
 
-/* The current working directory used to start the inferior.  */
-static const char *current_inferior_cwd = NULL;
+/* The current working directory used to start the inferior.
+
+   Empty if not specified.  */
+static std::string current_inferior_cwd;
 
 struct thread_info *
 add_thread (ptid_t thread_id, void *target_data)
@@ -235,7 +237,7 @@ switch_to_process (process_info *proc)
 
 /* See gdbsupport/common-inferior.h.  */
 
-const char *
+const std::string &
 get_inferior_cwd ()
 {
   return current_inferior_cwd;
@@ -244,11 +246,7 @@ get_inferior_cwd ()
 /* See inferiors.h.  */
 
 void
-set_inferior_cwd (const char *cwd)
+set_inferior_cwd (std::string cwd)
 {
-  xfree ((void *) current_inferior_cwd);
-  if (cwd != NULL)
-    current_inferior_cwd = xstrdup (cwd);
-  else
-    current_inferior_cwd = NULL;
+  current_inferior_cwd = std::move (cwd);
 }
diff --git a/gdbserver/inferiors.h b/gdbserver/inferiors.h
index 3b8959a28cf7..1e0fe67033cb 100644
--- a/gdbserver/inferiors.h
+++ b/gdbserver/inferiors.h
@@ -154,8 +154,8 @@ void *thread_target_data (struct thread_info *);
 struct regcache *thread_regcache_data (struct thread_info *);
 void set_thread_regcache_data (struct thread_info *, struct regcache *);
 
-/* Set the inferior current working directory.  If CWD is NULL, unset
+/* Set the inferior current working directory.  If CWD is empty, unset
    the directory.  */
-void set_inferior_cwd (const char *cwd);
+void set_inferior_cwd (std::string cwd);
 
 #endif /* GDBSERVER_INFERIORS_H */
diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index 32dcc05924e1..193c3d9d7d17 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -949,17 +949,17 @@ handle_general_set (char *own_buf)
 	{
 	  std::string path = hex2str (p);
 
-	  set_inferior_cwd (path.c_str ());
-
 	  if (remote_debug)
 	    debug_printf (_("[Set the inferior's current directory to %s]\n"),
 			  path.c_str ());
+
+	  set_inferior_cwd (std::move (path));
 	}
       else
 	{
 	  /* An empty argument means that we should clear out any
 	     previously set cwd for the inferior.  */
-	  set_inferior_cwd (NULL);
+	  set_inferior_cwd ("");
 
 	  if (remote_debug)
 	    debug_printf (_("\
diff --git a/gdbsupport/common-inferior.h b/gdbsupport/common-inferior.h
index 5e1221277b87..92c880e12123 100644
--- a/gdbsupport/common-inferior.h
+++ b/gdbsupport/common-inferior.h
@@ -32,9 +32,10 @@ extern const char *get_exec_wrapper ();
    otherwise return 0 in that case.  */
 extern const char *get_exec_file (int err);
 
-/* Return the inferior's current working directory.  If nothing has
-   been set, then return NULL.  */
-extern const char *get_inferior_cwd ();
+/* Return the inferior's current working directory.
+
+   If it is not set, the string is empty.  */
+extern const std::string &get_inferior_cwd ();
 
 /* Whether to start up the debuggee under a shell.
 
-- 
2.32.0


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

* [PATCH 11/16] gdb: make inferior::m_terminal an std::string
  2021-07-14  4:55 [PATCH 00/16] Bunch of commands related cleanups Simon Marchi
                   ` (9 preceding siblings ...)
  2021-07-14  4:55 ` [PATCH 10/16] gdb: make inferior::m_cwd " Simon Marchi
@ 2021-07-14  4:55 ` Simon Marchi
  2021-07-14  4:55 ` [PATCH 12/16] gdb: rename cfunc to simple_func Simon Marchi
                   ` (4 subsequent siblings)
  15 siblings, 0 replies; 50+ messages in thread
From: Simon Marchi @ 2021-07-14  4:55 UTC (permalink / raw)
  To: gdb-patches

Same idea as the previous patch, but for m_terminal.

Change-Id: If9367d5db8c976a4336680adca4ea5bc31ab64d2
---
 gdb/infcmd.c        |  6 ++----
 gdb/inferior.c      | 11 ++++-------
 gdb/inferior.h      | 18 ++++++++++--------
 gdb/inflow.c        | 46 ++++++++++++++++++---------------------------
 gdb/mi/mi-cmd-env.c |  9 ++++++---
 gdb/terminal.h      |  2 +-
 6 files changed, 41 insertions(+), 51 deletions(-)

diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index c02717631643..c183b60e81a8 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -115,13 +115,11 @@ show_inferior_tty_command (struct ui_file *file, int from_tty,
 {
   /* Note that we ignore the passed-in value in favor of computing it
      directly.  */
-  const char *inferior_tty = current_inferior ()->tty ();
+  const std::string &inferior_tty = current_inferior ()->tty ();
 
-  if (inferior_tty == nullptr)
-    inferior_tty = "";
   fprintf_filtered (gdb_stdout,
 		    _("Terminal for future runs of program being debugged "
-		      "is \"%s\".\n"), inferior_tty);
+		      "is \"%s\".\n"), inferior_tty.c_str ());
 }
 
 void
diff --git a/gdb/inferior.c b/gdb/inferior.c
index 74b1c65fa3da..40ab3e7e90b9 100644
--- a/gdb/inferior.c
+++ b/gdb/inferior.c
@@ -111,18 +111,15 @@ inferior::unpush_target (struct target_ops *t)
 }
 
 void
-inferior::set_tty (const char *terminal_name)
+inferior::set_tty (std::string terminal_name)
 {
-  if (terminal_name != nullptr && *terminal_name != '\0')
-    m_terminal = make_unique_xstrdup (terminal_name);
-  else
-    m_terminal = NULL;
+  m_terminal = std::move (terminal_name);
 }
 
-const char *
+const std::string &
 inferior::tty ()
 {
-  return m_terminal.get ();
+  return m_terminal;
 }
 
 void
diff --git a/gdb/inferior.h b/gdb/inferior.h
index c52c38caa93e..2efe66ac009a 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -434,13 +434,15 @@ class inferior : public refcounted_object,
   void add_continuation (std::function<void ()> &&cont);
   void do_all_continuations ();
 
-  /* Set/get file name for default use for standard in/out in the
-     inferior.  On Unix systems, we try to make TERMINAL_NAME the
-     inferior's controlling terminal.  If TERMINAL_NAME is nullptr or
-     the empty string, then the inferior inherits GDB's terminal (or
-     GDBserver's if spawning a remote process).  */
-  void set_tty (const char *terminal_name);
-  const char *tty ();
+  /* Set/get file name for default use for standard in/out in the inferior.
+
+     On Unix systems, we try to make TERMINAL_NAME the inferior's controlling
+     terminal.
+
+     If TERMINAL_NAME is the empty string, then the inferior inherits GDB's
+     terminal (or GDBserver's if spawning a remote process).  */
+  void set_tty (std::string terminal_name);
+  const std::string &tty ();
 
   /* Set the argument string to use when running this inferior.
 
@@ -587,7 +589,7 @@ class inferior : public refcounted_object,
   target_stack m_target_stack;
 
   /* The name of terminal device to use for I/O.  */
-  gdb::unique_xmalloc_ptr<char> m_terminal;
+  std::string m_terminal;
 
   /* The list of continuations.  */
   std::list<std::function<void ()>> m_continuations;
diff --git a/gdb/inflow.c b/gdb/inflow.c
index 970450632159..74dda702a8a9 100644
--- a/gdb/inflow.c
+++ b/gdb/inflow.c
@@ -65,7 +65,7 @@ struct terminal_info
 
   /* The name of the tty (from the `tty' command) that we gave to the
      inferior when it was started.  */
-  char *run_terminal = nullptr;
+  std::string run_terminal;
 
   /* TTY state.  We save it whenever the inferior stops, and restore
      it when it resumes in the foreground.  */
@@ -123,7 +123,7 @@ static sighandler_t sigquit_ours;
    be) used as a transient global by new_tty_prefork,
    create_tty_session, new_tty and new_tty_postfork, all called from
    fork_inferior, while forking a new child.  */
-static const char *inferior_thisrun_terminal;
+static std::string inferior_thisrun_terminal;
 
 /* Track who owns GDB's terminal (is it GDB or some inferior?).  While
    target_terminal::is_ours() etc. tracks the core's intention and is
@@ -303,8 +303,8 @@ sharing_input_terminal (inferior *inf)
 	 output was redirected to our terminal), and with a false
 	 positive we just end up trying to save/restore terminal
 	 settings when we didn't need to or we actually can't.  */
-      if (tinfo->run_terminal != NULL)
-	res = is_gdb_terminal (tinfo->run_terminal);
+      if (!tinfo->run_terminal.empty ())
+	res = is_gdb_terminal (tinfo->run_terminal.c_str ());
 
       /* If we still can't determine, assume yes.  */
       if (res == TRIBOOL_UNKNOWN)
@@ -603,7 +603,6 @@ static const struct inferior_key<terminal_info> inflow_inferior_data;
 
 terminal_info::~terminal_info ()
 {
-  xfree (run_terminal);
   xfree (ttystate);
 }
 
@@ -643,15 +642,10 @@ copy_terminal_info (struct inferior *to, struct inferior *from)
   tinfo_to = get_inflow_inferior_data (to);
   tinfo_from = get_inflow_inferior_data (from);
 
-  xfree (tinfo_to->run_terminal);
   xfree (tinfo_to->ttystate);
 
   *tinfo_to = *tinfo_from;
 
-  if (tinfo_from->run_terminal)
-    tinfo_to->run_terminal
-      = xstrdup (tinfo_from->run_terminal);
-
   if (tinfo_from->ttystate)
     tinfo_to->ttystate
       = serial_copy_tty_state (stdin_serial, tinfo_from->ttystate);
@@ -765,19 +759,19 @@ child_terminal_info (struct target_ops *self, const char *args, int from_tty)
 \f
 /* NEW_TTY_PREFORK is called before forking a new child process,
    so we can record the state of ttys in the child to be formed.
-   TTYNAME is null if we are to share the terminal with gdb;
-   or points to a string containing the name of the desired tty.
+   TTYNAME is empty if we are to share the terminal with gdb;
+   otherwise it contains the name of the desired tty.
 
    NEW_TTY is called in new child processes under Unix, which will
    become debugger target processes.  This actually switches to
    the terminal specified in the NEW_TTY_PREFORK call.  */
 
 void
-new_tty_prefork (const char *ttyname)
+new_tty_prefork (std::string ttyname)
 {
   /* Save the name for later, for determining whether we and the child
      are sharing a tty.  */
-  inferior_thisrun_terminal = ttyname;
+  inferior_thisrun_terminal = std::move (ttyname);
 }
 
 #if !defined(__GO32__) && !defined(_WIN32)
@@ -798,7 +792,7 @@ check_syscall (const char *msg, int result)
 void
 new_tty (void)
 {
-  if (inferior_thisrun_terminal == 0)
+  if (inferior_thisrun_terminal.empty ())
     return;
 #if !defined(__GO32__) && !defined(_WIN32)
   int tty;
@@ -818,8 +812,8 @@ new_tty (void)
 #endif
 
   /* Now open the specified new terminal.  */
-  tty = open (inferior_thisrun_terminal, O_RDWR | O_NOCTTY);
-  check_syscall (inferior_thisrun_terminal, tty);
+  tty = open (inferior_thisrun_terminal.c_str (), O_RDWR | O_NOCTTY);
+  check_syscall (inferior_thisrun_terminal.c_str (), tty);
 
   /* Avoid use of dup2; doesn't exist on all systems.  */
   if (tty != 0)
@@ -854,7 +848,7 @@ new_tty (void)
 
 /* NEW_TTY_POSTFORK is called after forking a new child process, and
    adding it to the inferior table, to store the TTYNAME being used by
-   the child, or null if it sharing the terminal with gdb.  */
+   the child, or empty if it sharing the terminal with gdb.  */
 
 void
 new_tty_postfork (void)
@@ -862,15 +856,11 @@ new_tty_postfork (void)
   /* Save the name for later, for determining whether we and the child
      are sharing a tty.  */
 
-  if (inferior_thisrun_terminal)
-    {
-      struct inferior *inf = current_inferior ();
-      struct terminal_info *tinfo = get_inflow_inferior_data (inf);
-
-      tinfo->run_terminal = xstrdup (inferior_thisrun_terminal);
-    }
+  struct inferior *inf = current_inferior ();
+  struct terminal_info *tinfo = get_inflow_inferior_data (inf);
 
-  inferior_thisrun_terminal = NULL;
+  tinfo->run_terminal = std::move (inferior_thisrun_terminal);
+  inferior_thisrun_terminal.clear ();
 }
 
 \f
@@ -894,7 +884,7 @@ set_sigint_trap (void)
   struct inferior *inf = current_inferior ();
   struct terminal_info *tinfo = get_inflow_inferior_data (inf);
 
-  if (inf->attach_flag || tinfo->run_terminal)
+  if (inf->attach_flag || !tinfo->run_terminal.empty ())
     {
       osig = signal (SIGINT, pass_signal);
       osig_set = 1;
@@ -927,7 +917,7 @@ create_tty_session (void)
 #ifdef HAVE_SETSID
   pid_t ret;
 
-  if (!job_control || inferior_thisrun_terminal == 0)
+  if (!job_control || inferior_thisrun_terminal.empty ())
     return 0;
 
   ret = setsid ();
diff --git a/gdb/mi/mi-cmd-env.c b/gdb/mi/mi-cmd-env.c
index f7ee1e166eba..703c63251fd3 100644
--- a/gdb/mi/mi-cmd-env.c
+++ b/gdb/mi/mi-cmd-env.c
@@ -244,7 +244,10 @@ mi_cmd_env_dir (const char *command, char **argv, int argc)
 void
 mi_cmd_inferior_tty_set (const char *command, char **argv, int argc)
 {
-  current_inferior ()->set_tty (argv[0]);
+  if (argc > 0)
+    current_inferior ()->set_tty (argv[0]);
+  else
+    current_inferior ()->set_tty ("");
 }
 
 /* Print the inferior terminal device name.  */
@@ -255,8 +258,8 @@ mi_cmd_inferior_tty_show (const char *command, char **argv, int argc)
   if ( !mi_valid_noargs ("-inferior-tty-show", argc, argv))
     error (_("-inferior-tty-show: Usage: No args"));
 
-  const char *inferior_tty = current_inferior ()->tty ();
-  if (inferior_tty != NULL)
+  const std::string &inferior_tty = current_inferior ()->tty ();
+  if (!inferior_tty.empty ())
     current_uiout->field_string ("inferior_tty_terminal", inferior_tty);
 }
 
diff --git a/gdb/terminal.h b/gdb/terminal.h
index e1f642b84665..186295e18f78 100644
--- a/gdb/terminal.h
+++ b/gdb/terminal.h
@@ -21,7 +21,7 @@
 
 struct inferior;
 
-extern void new_tty_prefork (const char *);
+extern void new_tty_prefork (std::string ttyname);
 
 extern void new_tty (void);
 
-- 
2.32.0


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

* [PATCH 12/16] gdb: rename cfunc to simple_func
  2021-07-14  4:55 [PATCH 00/16] Bunch of commands related cleanups Simon Marchi
                   ` (10 preceding siblings ...)
  2021-07-14  4:55 ` [PATCH 11/16] gdb: make inferior::m_terminal " Simon Marchi
@ 2021-07-14  4:55 ` Simon Marchi
  2021-07-14  4:55 ` [PATCH 13/16] gdb: remove cmd_list_element::function::sfunc Simon Marchi
                   ` (3 subsequent siblings)
  15 siblings, 0 replies; 50+ messages in thread
From: Simon Marchi @ 2021-07-14  4:55 UTC (permalink / raw)
  To: gdb-patches

After browsing the CLI code for quite a while and trying really hard, I
reached the conclusion that I can't give a meaningful explanation of
what "sfunc" and "cfunc" functions are, in cmd_list_element.  I don't
see a logic at all.  That makes it very difficult to do any kind of
change.  Unless somebody can make sense out of all that, I'd like to try
to retro-fit some logic in the cmd_list_element callback function code
so that we can understand what is going on, do some cleanups and add new
features.

The first change is about "cfunc".  I can't figure out what the "c" in
cfunc means.  It's not const, because there's already "const" in
"cmd_const_cfunc_ftype", and the previous "cmd_cfunc_ftype" had nothing
const..  It's not "cmd" or "command", because there's already "cmd" in
"cmd_const_cfunc_ftype".

The "main" command callback, cmd_list_element::func, has three
parameters, whereas cfunc has two.  It is missing the cmd_list_element
parameter.  So the only reason I see for cfunc to exist is to be a shim
between the three and two parameter versions.  Most commands don't need
to receive the cmd_list_element object, so adding it everywhere would be
long and would just add more unnecessary boilerplate.  So since this is
the "simple" version of the callback, compared to the "full", I suggest
renaming cmd_const_cfunc_ftype into cmd_simple_func_ftype, as well as
everything (like the utility functions) that goes with it.

Change-Id: I4e46cacfd77a66bc1cbf683f6a362072504b7868
---
 gdb/cli/cli-cmds.c   |  2 +-
 gdb/cli/cli-decode.c | 44 +++++++++++++++++++++++---------------------
 gdb/cli/cli-decode.h |  8 ++++++--
 gdb/command.h        | 25 ++++++++++++++-----------
 gdb/tracepoint.c     | 18 +++++++++---------
 5 files changed, 53 insertions(+), 44 deletions(-)

diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index 56ae12a0c19d..5ff0b77eb68f 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -443,7 +443,7 @@ complete_command (const char *arg, int from_tty)
 int
 is_complete_command (struct cmd_list_element *c)
 {
-  return cmd_cfunc_eq (c, complete_command);
+  return cmd_simple_func_eq (c, complete_command);
 }
 
 static void
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index 633a3ad93095..3c39e47ac69b 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -94,22 +94,23 @@ print_help_for_command (struct cmd_list_element *c,
 \f
 /* Set the callback function for the specified command.  For each both
    the commands callback and func() are set.  The latter set to a
-   bounce function (unless cfunc / sfunc is NULL that is).  */
+   bounce function (unless simple_func / sfunc is NULL that is).  */
 
 static void
-do_const_cfunc (struct cmd_list_element *c, const char *args, int from_tty)
+do_simple_func (struct cmd_list_element *c, const char *args, int from_tty)
 {
-  c->function.const_cfunc (args, from_tty);
+  c->function.simple_func (args, from_tty);
 }
 
 static void
-set_cmd_cfunc (struct cmd_list_element *cmd, cmd_const_cfunc_ftype *cfunc)
+set_cmd_simple_func (struct cmd_list_element *cmd, cmd_simple_func_ftype *simple_func)
 {
-  if (cfunc == NULL)
+  if (simple_func == NULL)
     cmd->func = NULL;
   else
-    cmd->func = do_const_cfunc;
-  cmd->function.const_cfunc = cfunc;
+    cmd->func = do_simple_func;
+
+  cmd->function.simple_func = simple_func;
 }
 
 static void
@@ -129,9 +130,10 @@ set_cmd_sfunc (struct cmd_list_element *cmd, cmd_const_sfunc_ftype *sfunc)
 }
 
 int
-cmd_cfunc_eq (struct cmd_list_element *cmd, cmd_const_cfunc_ftype *cfunc)
+cmd_simple_func_eq (struct cmd_list_element *cmd, cmd_simple_func_ftype *simple_func)
 {
-  return cmd->func == do_const_cfunc && cmd->function.const_cfunc == cfunc;
+  return (cmd->func == do_simple_func
+	  && cmd->function.simple_func == simple_func);
 }
 
 void
@@ -238,17 +240,17 @@ add_cmd (const char *name, enum command_class theclass,
 {
   cmd_list_element *result = do_add_cmd (name, theclass, doc, list);
   result->func = NULL;
-  result->function.const_cfunc = NULL;
+  result->function.simple_func = NULL;
   return result;
 }
 
 struct cmd_list_element *
 add_cmd (const char *name, enum command_class theclass,
-	 cmd_const_cfunc_ftype *fun,
+	 cmd_simple_func_ftype *fun,
 	 const char *doc, struct cmd_list_element **list)
 {
   cmd_list_element *result = do_add_cmd (name, theclass, doc, list);
-  set_cmd_cfunc (result, fun);
+  set_cmd_simple_func (result, fun);
   return result;
 }
 
@@ -256,7 +258,7 @@ add_cmd (const char *name, enum command_class theclass,
 
 struct cmd_list_element *
 add_cmd_suppress_notification (const char *name, enum command_class theclass,
-			       cmd_const_cfunc_ftype *fun, const char *doc,
+			       cmd_simple_func_ftype *fun, const char *doc,
 			       struct cmd_list_element **list,
 			       int *suppress_notification)
 {
@@ -359,7 +361,7 @@ update_prefix_field_of_prefixed_commands (struct cmd_list_element *c)
 
 struct cmd_list_element *
 add_prefix_cmd (const char *name, enum command_class theclass,
-		cmd_const_cfunc_ftype *fun,
+		cmd_simple_func_ftype *fun,
 		const char *doc, struct cmd_list_element **subcommands,
 		int allow_unknown, struct cmd_list_element **list)
 {
@@ -432,7 +434,7 @@ add_show_prefix_cmd (const char *name, enum command_class theclass,
 struct cmd_list_element *
 add_prefix_cmd_suppress_notification
 	       (const char *name, enum command_class theclass,
-		cmd_const_cfunc_ftype *fun,
+		cmd_simple_func_ftype *fun,
 		const char *doc, struct cmd_list_element **subcommands,
 		int allow_unknown, struct cmd_list_element **list,
 		int *suppress_notification)
@@ -448,7 +450,7 @@ add_prefix_cmd_suppress_notification
 
 struct cmd_list_element *
 add_abbrev_prefix_cmd (const char *name, enum command_class theclass,
-		       cmd_const_cfunc_ftype *fun, const char *doc,
+		       cmd_simple_func_ftype *fun, const char *doc,
 		       struct cmd_list_element **subcommands,
 		       int allow_unknown, struct cmd_list_element **list)
 {
@@ -460,7 +462,7 @@ add_abbrev_prefix_cmd (const char *name, enum command_class theclass,
   return c;
 }
 
-/* This is an empty "cfunc".  */
+/* This is an empty "simple func".  */
 void
 not_just_help_class_command (const char *args, int from_tty)
 {
@@ -951,7 +953,7 @@ delete_cmd (const char *name, struct cmd_list_element **list,
 /* Add an element to the list of info subcommands.  */
 
 struct cmd_list_element *
-add_info (const char *name, cmd_const_cfunc_ftype *fun, const char *doc)
+add_info (const char *name, cmd_simple_func_ftype *fun, const char *doc)
 {
   return add_cmd (name, class_info, fun, doc, &infolist);
 }
@@ -968,7 +970,7 @@ add_info_alias (const char *name, cmd_list_element *target, int abbrev_flag)
 
 struct cmd_list_element *
 add_com (const char *name, enum command_class theclass,
-	 cmd_const_cfunc_ftype *fun,
+	 cmd_simple_func_ftype *fun,
 	 const char *doc)
 {
   return add_cmd (name, theclass, fun, doc, &cmdlist);
@@ -990,7 +992,7 @@ add_com_alias (const char *name, cmd_list_element *target,
 
 struct cmd_list_element *
 add_com_suppress_notification (const char *name, enum command_class theclass,
-			       cmd_const_cfunc_ftype *fun, const char *doc,
+			       cmd_simple_func_ftype *fun, const char *doc,
 			       int *suppress_notification)
 {
   return add_cmd_suppress_notification (name, theclass, fun, doc,
@@ -2167,5 +2169,5 @@ int
 cli_user_command_p (struct cmd_list_element *cmd)
 {
   return (cmd->theclass == class_user
-	  && (cmd->func == do_const_cfunc || cmd->func == do_sfunc));
+	  && (cmd->func == do_simple_func || cmd->func == do_sfunc));
 }
diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h
index 241535ae5b50..4cbdf7ff6d17 100644
--- a/gdb/cli/cli-decode.h
+++ b/gdb/cli/cli-decode.h
@@ -174,8 +174,12 @@ struct cmd_list_element
      to one of the below.  */
   union
     {
-      /* If type is not_set_cmd, call it like this: */
-      cmd_const_cfunc_ftype *const_cfunc;
+      /* Most commands don't need the cmd_list_element parameter passed to FUNC.
+	 They therefore register a command of this type, which doesn't have the
+	 cmd_list_element parameter.  do_simple_func is installed as FUNC, and
+	 acts as a shim between the two.  */
+      cmd_simple_func_ftype *simple_func;
+
       /* If type is set_cmd or show_cmd, first set the variables,
 	 and then call this: */
       cmd_const_sfunc_ftype *sfunc;
diff --git a/gdb/command.h b/gdb/command.h
index 711cbdcf43e1..1bda67c937af 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -128,7 +128,10 @@ var_types;
 /* This structure records one command'd definition.  */
 struct cmd_list_element;
 
-typedef void cmd_const_cfunc_ftype (const char *args, int from_tty);
+/* The "simple" signature of command callbacks, which doesn't include a
+   cmd_list_element parameter.  */
+
+typedef void cmd_simple_func_ftype (const char *args, int from_tty);
 
 /* This structure specifies notifications to be suppressed by a cli
    command interpreter.  */
@@ -158,7 +161,7 @@ extern bool valid_cmd_char_p (int c);
 /* Const-correct variant of the above.  */
 
 extern struct cmd_list_element *add_cmd (const char *, enum command_class,
-					 cmd_const_cfunc_ftype *fun,
+					 cmd_simple_func_ftype *fun,
 					 const char *,
 					 struct cmd_list_element **);
 
@@ -170,7 +173,7 @@ extern struct cmd_list_element *add_cmd (const char *, enum command_class,
 
 extern struct cmd_list_element *add_cmd_suppress_notification
 			(const char *name, enum command_class theclass,
-			 cmd_const_cfunc_ftype *fun, const char *doc,
+			 cmd_simple_func_ftype *fun, const char *doc,
 			 struct cmd_list_element **list,
 			 int *suppress_notification);
 
@@ -181,7 +184,7 @@ extern struct cmd_list_element *add_alias_cmd (const char *,
 
 
 extern struct cmd_list_element *add_prefix_cmd (const char *, enum command_class,
-						cmd_const_cfunc_ftype *fun,
+						cmd_simple_func_ftype *fun,
 						const char *,
 						struct cmd_list_element **,
 						int,
@@ -203,7 +206,7 @@ extern struct cmd_list_element *add_show_prefix_cmd
 
 extern struct cmd_list_element *add_prefix_cmd_suppress_notification
 			(const char *name, enum command_class theclass,
-			 cmd_const_cfunc_ftype *fun,
+			 cmd_simple_func_ftype *fun,
 			 const char *doc, struct cmd_list_element **subcommands,
 			 int allow_unknown,
 			 struct cmd_list_element **list,
@@ -211,7 +214,7 @@ extern struct cmd_list_element *add_prefix_cmd_suppress_notification
 
 extern struct cmd_list_element *add_abbrev_prefix_cmd (const char *,
 						       enum command_class,
-						       cmd_const_cfunc_ftype *fun,
+						       cmd_simple_func_ftype *fun,
 						       const char *,
 						       struct cmd_list_element
 						       **, int,
@@ -250,8 +253,8 @@ extern void set_cmd_completer_handle_brkchars (struct cmd_list_element *,
 
 /* HACK: cagney/2002-02-23: Code, mostly in tracepoints.c, grubs
    around in cmd objects to test the value of the commands sfunc().  */
-extern int cmd_cfunc_eq (struct cmd_list_element *cmd,
-			 cmd_const_cfunc_ftype *cfun);
+extern int cmd_simple_func_eq (struct cmd_list_element *cmd,
+			 cmd_simple_func_ftype *cfun);
 
 /* Execute CMD's pre/post hook.  Throw an error if the command fails.
    If already executing this pre/post hook, or there is no pre/post
@@ -346,7 +349,7 @@ extern int lookup_cmd_composition (const char *text,
 				   struct cmd_list_element **cmd);
 
 extern struct cmd_list_element *add_com (const char *, enum command_class,
-					 cmd_const_cfunc_ftype *fun,
+					 cmd_simple_func_ftype *fun,
 					 const char *);
 
 extern cmd_list_element *add_com_alias (const char *name,
@@ -356,11 +359,11 @@ extern cmd_list_element *add_com_alias (const char *name,
 
 extern struct cmd_list_element *add_com_suppress_notification
 		       (const char *name, enum command_class theclass,
-			cmd_const_cfunc_ftype *fun, const char *doc,
+			cmd_simple_func_ftype *fun, const char *doc,
 			int *supress_notification);
 
 extern struct cmd_list_element *add_info (const char *,
-					  cmd_const_cfunc_ftype *fun,
+					  cmd_simple_func_ftype *fun,
 					  const char *);
 
 extern cmd_list_element *add_info_alias (const char *name,
diff --git a/gdb/tracepoint.c b/gdb/tracepoint.c
index 83a32ec8252d..06cf9cad29ad 100644
--- a/gdb/tracepoint.c
+++ b/gdb/tracepoint.c
@@ -655,7 +655,7 @@ validate_actionline (const char *line, struct breakpoint *b)
   if (c == 0)
     error (_("`%s' is not a tracepoint action, or is ambiguous."), p);
 
-  if (cmd_cfunc_eq (c, collect_pseudocommand))
+  if (cmd_simple_func_eq (c, collect_pseudocommand))
     {
       int trace_string = 0;
 
@@ -723,7 +723,7 @@ validate_actionline (const char *line, struct breakpoint *b)
       while (p && *p++ == ',');
     }
 
-  else if (cmd_cfunc_eq (c, teval_pseudocommand))
+  else if (cmd_simple_func_eq (c, teval_pseudocommand))
     {
       do
 	{			/* Repeat over a comma-separated list.  */
@@ -750,7 +750,7 @@ validate_actionline (const char *line, struct breakpoint *b)
       while (p && *p++ == ',');
     }
 
-  else if (cmd_cfunc_eq (c, while_stepping_pseudocommand))
+  else if (cmd_simple_func_eq (c, while_stepping_pseudocommand))
     {
       char *endp;
 
@@ -761,7 +761,7 @@ validate_actionline (const char *line, struct breakpoint *b)
       p = endp;
     }
 
-  else if (cmd_cfunc_eq (c, end_actions_pseudocommand))
+  else if (cmd_simple_func_eq (c, end_actions_pseudocommand))
     ;
 
   else
@@ -1308,7 +1308,7 @@ encode_actions_1 (struct command_line *action,
       if (cmd == 0)
 	error (_("Bad action list item: %s"), action_exp);
 
-      if (cmd_cfunc_eq (cmd, collect_pseudocommand))
+      if (cmd_simple_func_eq (cmd, collect_pseudocommand))
 	{
 	  int trace_string = 0;
 
@@ -1464,7 +1464,7 @@ encode_actions_1 (struct command_line *action,
 	    }
 	  while (action_exp && *action_exp++ == ',');
 	}			/* if */
-      else if (cmd_cfunc_eq (cmd, teval_pseudocommand))
+      else if (cmd_simple_func_eq (cmd, teval_pseudocommand))
 	{
 	  do
 	    {			/* Repeat over a comma-separated list.  */
@@ -1488,7 +1488,7 @@ encode_actions_1 (struct command_line *action,
 	    }
 	  while (action_exp && *action_exp++ == ',');
 	}			/* if */
-      else if (cmd_cfunc_eq (cmd, while_stepping_pseudocommand))
+      else if (cmd_simple_func_eq (cmd, while_stepping_pseudocommand))
 	{
 	  /* We check against nested while-stepping when setting
 	     breakpoint action, so no way to run into nested
@@ -2690,13 +2690,13 @@ trace_dump_actions (struct command_line *action,
       if (cmd == 0)
 	error (_("Bad action list item: %s"), action_exp);
 
-      if (cmd_cfunc_eq (cmd, while_stepping_pseudocommand))
+      if (cmd_simple_func_eq (cmd, while_stepping_pseudocommand))
 	{
 	  gdb_assert (action->body_list_1 == nullptr);
 	  trace_dump_actions (action->body_list_0.get (),
 			      1, stepping_frame, from_tty);
 	}
-      else if (cmd_cfunc_eq (cmd, collect_pseudocommand))
+      else if (cmd_simple_func_eq (cmd, collect_pseudocommand))
 	{
 	  /* Display the collected data.
 	     For the trap frame, display only what was collected at
-- 
2.32.0


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

* [PATCH 13/16] gdb: remove cmd_list_element::function::sfunc
  2021-07-14  4:55 [PATCH 00/16] Bunch of commands related cleanups Simon Marchi
                   ` (11 preceding siblings ...)
  2021-07-14  4:55 ` [PATCH 12/16] gdb: rename cfunc to simple_func Simon Marchi
@ 2021-07-14  4:55 ` Simon Marchi
  2021-07-28 19:10   ` Tom Tromey
  2021-07-14  4:55 ` [PATCH 14/16] gdb/testsuite: test get/set value of unregistered Guile parameter Simon Marchi
                   ` (2 subsequent siblings)
  15 siblings, 1 reply; 50+ messages in thread
From: Simon Marchi @ 2021-07-14  4:55 UTC (permalink / raw)
  To: gdb-patches

I don't understand what the sfunc function type in
cmd_list_element::function is for.  Compared to cmd_simple_func_ftype,
it has an extra cmd_list_element parameter, giving the callback access
to the cmd_list_element for the command being invoked.  This allows
registering the same callback with many commands, and alter the behavior
using the cmd_list_element's context.

From the comment in cmd_list_element, it sounds like at some point it
was the callback function type for set and show functions, hence the
"s".  But nowadays, it's used for many more commands that need to access
the cmd_list_element object (see add_catch_command for example).

I don't really see the point of having sfunc at all, since do_sfunc is
just a trivial shim that changes the order of the arguments.  All
commands using sfunc could just as well set cmd_list_element::func to
their callback directly.

Therefore, remove the sfunc field in cmd_list_element and everything
that goes with it.  Rename cmd_const_sfunc_ftype to cmd_func_ftype and
use it for cmd_list_element::func, as well as for the add_setshow
commands.

Change-Id: I1eb96326c9b511c293c76996cea0ebc51c70fac0
---
 gdb/breakpoint.c      |  6 ++--
 gdb/breakpoint.h      |  2 +-
 gdb/cli/cli-decode.c  | 66 +++++++++++++++----------------------------
 gdb/cli/cli-decode.h  |  8 ++----
 gdb/cli/cli-dump.c    |  2 +-
 gdb/cli/cli-setshow.c |  5 ++--
 gdb/command.h         | 30 +++++++++-----------
 gdb/guile/scm-cmd.c   |  3 +-
 gdb/guile/scm-param.c |  2 +-
 gdb/python/py-cmd.c   |  3 +-
 gdb/target.c          |  4 +--
 gdb/tui/tui-layout.c  |  3 +-
 12 files changed, 52 insertions(+), 82 deletions(-)

diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 7fd23348a8e8..89af44ee4c6b 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -15204,7 +15204,7 @@ static struct cmd_list_element *tcatch_cmdlist;
 
 void
 add_catch_command (const char *name, const char *docstring,
-		   cmd_const_sfunc_ftype *sfunc,
+		   cmd_func_ftype *func,
 		   completer_ftype *completer,
 		   void *user_data_catch,
 		   void *user_data_tcatch)
@@ -15213,13 +15213,13 @@ add_catch_command (const char *name, const char *docstring,
 
   command = add_cmd (name, class_breakpoint, docstring,
 		     &catch_cmdlist);
-  set_cmd_sfunc (command, sfunc);
+  command->func = func;
   command->set_context (user_data_catch);
   set_cmd_completer (command, completer);
 
   command = add_cmd (name, class_breakpoint, docstring,
 		     &tcatch_cmdlist);
-  set_cmd_sfunc (command, sfunc);
+  command->func = func;
   command->set_context (user_data_tcatch);
   set_cmd_completer (command, completer);
 }
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index fe68730f1651..ab65f41a91b5 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -1347,7 +1347,7 @@ extern void initialize_breakpoint_ops (void);
 
 extern void
   add_catch_command (const char *name, const char *docstring,
-		     cmd_const_sfunc_ftype *sfunc,
+		     cmd_func_ftype *func,
 		     completer_ftype *completer,
 		     void *user_data_catch,
 		     void *user_data_tcatch);
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index 3c39e47ac69b..06f3de0f038a 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -91,13 +91,8 @@ static void
 print_help_for_command (struct cmd_list_element *c,
 			bool recurse, struct ui_file *stream);
 
-\f
-/* Set the callback function for the specified command.  For each both
-   the commands callback and func() are set.  The latter set to a
-   bounce function (unless simple_func / sfunc is NULL that is).  */
-
 static void
-do_simple_func (struct cmd_list_element *c, const char *args, int from_tty)
+do_simple_func (const char *args, int from_tty, cmd_list_element *c)
 {
   c->function.simple_func (args, from_tty);
 }
@@ -113,22 +108,6 @@ set_cmd_simple_func (struct cmd_list_element *cmd, cmd_simple_func_ftype *simple
   cmd->function.simple_func = simple_func;
 }
 
-static void
-do_sfunc (struct cmd_list_element *c, const char *args, int from_tty)
-{
-  c->function.sfunc (args, from_tty, c);
-}
-
-void
-set_cmd_sfunc (struct cmd_list_element *cmd, cmd_const_sfunc_ftype *sfunc)
-{
-  if (sfunc == NULL)
-    cmd->func = NULL;
-  else
-    cmd->func = do_sfunc;
-  cmd->function.sfunc = sfunc;
-}
-
 int
 cmd_simple_func_eq (struct cmd_list_element *cmd, cmd_simple_func_ftype *simple_func)
 {
@@ -401,7 +380,7 @@ add_basic_prefix_cmd (const char *name, enum command_class theclass,
   struct cmd_list_element *cmd = add_prefix_cmd (name, theclass, nullptr,
 						 doc, subcommands,
 						 allow_unknown, list);
-  set_cmd_sfunc (cmd, do_prefix_cmd);
+  cmd->func = do_prefix_cmd;
   return cmd;
 }
 
@@ -424,7 +403,7 @@ add_show_prefix_cmd (const char *name, enum command_class theclass,
   struct cmd_list_element *cmd = add_prefix_cmd (name, theclass, nullptr,
 						 doc, subcommands,
 						 allow_unknown, list);
-  set_cmd_sfunc (cmd, do_show_prefix_cmd);
+  cmd->func = do_show_prefix_cmd;
   return cmd;
 }
 
@@ -468,10 +447,10 @@ not_just_help_class_command (const char *args, int from_tty)
 {
 }
 
-/* This is an empty "sfunc".  */
+/* This is an empty cmd func.  */
 
 static void
-empty_sfunc (const char *args, int from_tty, struct cmd_list_element *c)
+empty_func (const char *args, int from_tty, cmd_list_element *c)
 {
 }
 
@@ -500,7 +479,7 @@ add_set_or_show_cmd (const char *name,
   c->var = var;
   /* This needs to be something besides NULL so that this isn't
      treated as a help class.  */
-  set_cmd_sfunc (c, empty_sfunc);
+  c->func = empty_func;
   return c;
 }
 
@@ -519,7 +498,7 @@ add_setshow_cmd_full (const char *name,
 		      var_types var_type, void *var,
 		      const char *set_doc, const char *show_doc,
 		      const char *help_doc,
-		      cmd_const_sfunc_ftype *set_func,
+		      cmd_func_ftype *set_func,
 		      show_value_ftype *show_func,
 		      struct cmd_list_element **set_list,
 		      struct cmd_list_element **show_list)
@@ -544,7 +523,7 @@ add_setshow_cmd_full (const char *name,
   set->doc_allocated = 1;
 
   if (set_func != NULL)
-    set_cmd_sfunc (set, set_func);
+    set->func = set_func;
 
   show = add_set_or_show_cmd (name, show_cmd, theclass, var_type, var,
 			      full_show_doc, show_list);
@@ -570,7 +549,7 @@ add_setshow_enum_cmd (const char *name,
 		      const char *set_doc,
 		      const char *show_doc,
 		      const char *help_doc,
-		      cmd_const_sfunc_ftype *set_func,
+		      cmd_func_ftype *set_func,
 		      show_value_ftype *show_func,
 		      struct cmd_list_element **set_list,
 		      struct cmd_list_element **show_list)
@@ -598,7 +577,7 @@ add_setshow_auto_boolean_cmd (const char *name,
 			      enum auto_boolean *var,
 			      const char *set_doc, const char *show_doc,
 			      const char *help_doc,
-			      cmd_const_sfunc_ftype *set_func,
+			      cmd_func_ftype *set_func,
 			      show_value_ftype *show_func,
 			      struct cmd_list_element **set_list,
 			      struct cmd_list_element **show_list)
@@ -627,7 +606,7 @@ set_show_commands
 add_setshow_boolean_cmd (const char *name, enum command_class theclass, bool *var,
 			 const char *set_doc, const char *show_doc,
 			 const char *help_doc,
-			 cmd_const_sfunc_ftype *set_func,
+			 cmd_func_ftype *set_func,
 			 show_value_ftype *show_func,
 			 struct cmd_list_element **set_list,
 			 struct cmd_list_element **show_list)
@@ -651,7 +630,7 @@ add_setshow_filename_cmd (const char *name, enum command_class theclass,
 			  char **var,
 			  const char *set_doc, const char *show_doc,
 			  const char *help_doc,
-			  cmd_const_sfunc_ftype *set_func,
+			  cmd_func_ftype *set_func,
 			  show_value_ftype *show_func,
 			  struct cmd_list_element **set_list,
 			  struct cmd_list_element **show_list)
@@ -675,7 +654,7 @@ add_setshow_string_cmd (const char *name, enum command_class theclass,
 			char **var,
 			const char *set_doc, const char *show_doc,
 			const char *help_doc,
-			cmd_const_sfunc_ftype *set_func,
+			cmd_func_ftype *set_func,
 			show_value_ftype *show_func,
 			struct cmd_list_element **set_list,
 			struct cmd_list_element **show_list)
@@ -700,7 +679,7 @@ add_setshow_string_noescape_cmd (const char *name, enum command_class theclass,
 				 char **var,
 				 const char *set_doc, const char *show_doc,
 				 const char *help_doc,
-				 cmd_const_sfunc_ftype *set_func,
+				 cmd_func_ftype *set_func,
 				 show_value_ftype *show_func,
 				 struct cmd_list_element **set_list,
 				 struct cmd_list_element **show_list)
@@ -725,7 +704,7 @@ add_setshow_optional_filename_cmd (const char *name, enum command_class theclass
 				   char **var,
 				   const char *set_doc, const char *show_doc,
 				   const char *help_doc,
-				   cmd_const_sfunc_ftype *set_func,
+				   cmd_func_ftype *set_func,
 				   show_value_ftype *show_func,
 				   struct cmd_list_element **set_list,
 				   struct cmd_list_element **show_list)
@@ -769,7 +748,7 @@ add_setshow_integer_cmd (const char *name, enum command_class theclass,
 			 int *var,
 			 const char *set_doc, const char *show_doc,
 			 const char *help_doc,
-			 cmd_const_sfunc_ftype *set_func,
+			 cmd_func_ftype *set_func,
 			 show_value_ftype *show_func,
 			 struct cmd_list_element **set_list,
 			 struct cmd_list_element **show_list)
@@ -795,7 +774,7 @@ add_setshow_uinteger_cmd (const char *name, enum command_class theclass,
 			  unsigned int *var,
 			  const char *set_doc, const char *show_doc,
 			  const char *help_doc,
-			  cmd_const_sfunc_ftype *set_func,
+			  cmd_func_ftype *set_func,
 			  show_value_ftype *show_func,
 			  struct cmd_list_element **set_list,
 			  struct cmd_list_element **show_list)
@@ -821,7 +800,7 @@ add_setshow_zinteger_cmd (const char *name, enum command_class theclass,
 			  int *var,
 			  const char *set_doc, const char *show_doc,
 			  const char *help_doc,
-			  cmd_const_sfunc_ftype *set_func,
+			  cmd_func_ftype *set_func,
 			  show_value_ftype *show_func,
 			  struct cmd_list_element **set_list,
 			  struct cmd_list_element **show_list)
@@ -839,7 +818,7 @@ add_setshow_zuinteger_unlimited_cmd (const char *name,
 				     const char *set_doc,
 				     const char *show_doc,
 				     const char *help_doc,
-				     cmd_const_sfunc_ftype *set_func,
+				     cmd_func_ftype *set_func,
 				     show_value_ftype *show_func,
 				     struct cmd_list_element **set_list,
 				     struct cmd_list_element **show_list)
@@ -865,7 +844,7 @@ add_setshow_zuinteger_cmd (const char *name, enum command_class theclass,
 			   unsigned int *var,
 			   const char *set_doc, const char *show_doc,
 			   const char *help_doc,
-			   cmd_const_sfunc_ftype *set_func,
+			   cmd_func_ftype *set_func,
 			   show_value_ftype *show_func,
 			   struct cmd_list_element **set_list,
 			   struct cmd_list_element **show_list)
@@ -2159,7 +2138,7 @@ cmd_func (struct cmd_list_element *cmd, const char *args, int from_tty)
       if (cmd->suppress_notification != NULL)
 	restore_suppress.emplace (cmd->suppress_notification, 1);
 
-      (*cmd->func) (cmd, args, from_tty);
+      cmd->func (args, from_tty, cmd);
     }
   else
     error (_("Invalid command"));
@@ -2168,6 +2147,5 @@ cmd_func (struct cmd_list_element *cmd, const char *args, int from_tty)
 int
 cli_user_command_p (struct cmd_list_element *cmd)
 {
-  return (cmd->theclass == class_user
-	  && (cmd->func == do_simple_func || cmd->func == do_sfunc));
+  return cmd->theclass == class_user && cmd->func == do_simple_func;
 }
diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h
index 4cbdf7ff6d17..651d1ef8abb7 100644
--- a/gdb/cli/cli-decode.h
+++ b/gdb/cli/cli-decode.h
@@ -168,8 +168,8 @@ struct cmd_list_element
      cagney/2002-02-02: This function signature is evolving.  For
      the moment suggest sticking with either set_cmd_cfunc() or
      set_cmd_sfunc().  */
-  void (*func) (struct cmd_list_element *c, const char *args, int from_tty)
-    = nullptr;
+  cmd_func_ftype *func;
+
   /* The command's real callback.  At present func() bounces through
      to one of the below.  */
   union
@@ -179,10 +179,6 @@ struct cmd_list_element
 	 cmd_list_element parameter.  do_simple_func is installed as FUNC, and
 	 acts as a shim between the two.  */
       cmd_simple_func_ftype *simple_func;
-
-      /* If type is set_cmd or show_cmd, first set the variables,
-	 and then call this: */
-      cmd_const_sfunc_ftype *sfunc;
     }
   function;
 
diff --git a/gdb/cli/cli-dump.c b/gdb/cli/cli-dump.c
index efb400418042..6f7688ad58fa 100644
--- a/gdb/cli/cli-dump.c
+++ b/gdb/cli/cli-dump.c
@@ -331,7 +331,7 @@ struct dump_context
 };
 
 static void
-call_dump_func (struct cmd_list_element *c, const char *args, int from_tty)
+call_dump_func (const char *args, int from_tty, cmd_list_element *c)
 {
   struct dump_context *d = (struct dump_context *) c->context ();
 
diff --git a/gdb/cli/cli-setshow.c b/gdb/cli/cli-setshow.c
index 5fd5fd15c6ad..0290acede7fa 100644
--- a/gdb/cli/cli-setshow.c
+++ b/gdb/cli/cli-setshow.c
@@ -517,7 +517,8 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
     default:
       error (_("gdb internal error: bad var_type in do_setshow_command"));
     }
-  c->func (c, NULL, from_tty);
+
+  c->func (NULL, from_tty, c);
 
   if (notify_command_param_changed_p (option_changed, c))
     {
@@ -723,7 +724,7 @@ do_show_command (const char *arg, int from_tty, struct cmd_list_element *c)
 	deprecated_show_value_hack (gdb_stdout, from_tty, c, val.c_str ());
     }
 
-  c->func (c, NULL, from_tty);
+  c->func (NULL, from_tty, c);
 }
 
 /* Show all the settings in a list of show commands.  */
diff --git a/gdb/command.h b/gdb/command.h
index 1bda67c937af..baf34401a070 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -221,10 +221,8 @@ extern struct cmd_list_element *add_abbrev_prefix_cmd (const char *,
 						       struct cmd_list_element
 						       **);
 
-typedef void cmd_const_sfunc_ftype (const char *args, int from_tty,
-				    struct cmd_list_element *c);
-extern void set_cmd_sfunc (struct cmd_list_element *cmd,
-			   cmd_const_sfunc_ftype *sfunc);
+typedef void cmd_func_ftype (const char *args, int from_tty,
+			     cmd_list_element *c);
 
 /* A completion routine.  Add possible completions to tracker.
 
@@ -404,73 +402,73 @@ struct set_show_commands
 extern set_show_commands add_setshow_enum_cmd
   (const char *name, command_class theclass, const char *const *enumlist,
    const char **var, const char *set_doc, const char *show_doc,
-   const char *help_doc, cmd_const_sfunc_ftype *set_func,
+   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, auto_boolean *var,
    const char *set_doc, const char *show_doc, const char *help_doc,
-   cmd_const_sfunc_ftype *set_func, show_value_ftype *show_func,
+   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, bool *var, const char *set_doc,
-   const char *show_doc, const char *help_doc, cmd_const_sfunc_ftype *set_func,
+   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, char **var, const char *set_doc,
-   const char *show_doc, const char *help_doc, cmd_const_sfunc_ftype *set_func,
+   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 *show_doc, const char *help_doc, cmd_const_sfunc_ftype *set_func,
+   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 *show_doc, const char *help_doc, cmd_const_sfunc_ftype *set_func,
+   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 *show_doc, const char *help_doc, cmd_const_sfunc_ftype *set_func,
+   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, int *var, const char *set_doc,
-   const char *show_doc, const char *help_doc, cmd_const_sfunc_ftype *set_func,
+   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, unsigned int *var,
    const char *set_doc, const char *show_doc, const char *help_doc,
-   cmd_const_sfunc_ftype *set_func, show_value_ftype *show_func,
+   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, int *var, const char *set_doc,
-   const char *show_doc, const char *help_doc, cmd_const_sfunc_ftype *set_func,
+   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, unsigned int *var,
    const char *set_doc, const char *show_doc, const char *help_doc,
-   cmd_const_sfunc_ftype *set_func, show_value_ftype *show_func,
+   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, int *var, const char *set_doc,
-   const char *show_doc, const char *help_doc, cmd_const_sfunc_ftype *set_func,
+   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/guile/scm-cmd.c b/gdb/guile/scm-cmd.c
index ab3dad7483cc..676428577b28 100644
--- a/gdb/guile/scm-cmd.c
+++ b/gdb/guile/scm-cmd.c
@@ -291,8 +291,7 @@ cmdscm_destroyer (struct cmd_list_element *self, void *context)
 /* Called by gdb to invoke the command.  */
 
 static void
-cmdscm_function (struct cmd_list_element *command,
-		 const char *args, int from_tty)
+cmdscm_function (const char *args, int from_tty, cmd_list_element *command)
 {
   command_smob *c_smob/*obj*/ = (command_smob *) command->context ();
   SCM arg_scm, tty_scm, result;
diff --git a/gdb/guile/scm-param.c b/gdb/guile/scm-param.c
index c052d04974ae..44ea167be277 100644
--- a/gdb/guile/scm-param.c
+++ b/gdb/guile/scm-param.c
@@ -353,7 +353,7 @@ static set_show_commands
 add_setshow_generic (enum var_types param_type, enum command_class cmd_class,
 		     char *cmd_name, param_smob *self,
 		     char *set_doc, char *show_doc, char *help_doc,
-		     cmd_const_sfunc_ftype *set_func,
+		     cmd_func_ftype *set_func,
 		     show_value_ftype *show_func,
 		     struct cmd_list_element **set_list,
 		     struct cmd_list_element **show_list)
diff --git a/gdb/python/py-cmd.c b/gdb/python/py-cmd.c
index 0467ebd67349..94608e0bbcfd 100644
--- a/gdb/python/py-cmd.c
+++ b/gdb/python/py-cmd.c
@@ -100,8 +100,7 @@ cmdpy_destroyer (struct cmd_list_element *self, void *context)
 /* Called by gdb to invoke the command.  */
 
 static void
-cmdpy_function (struct cmd_list_element *command,
-		const char *args, int from_tty)
+cmdpy_function (const char *args, int from_tty, cmd_list_element *command)
 {
   cmdpy_object *obj = (cmdpy_object *) command->context ();
 
diff --git a/gdb/target.c b/gdb/target.c
index 787528981917..e7ef1d5eee2c 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -876,7 +876,7 @@ information on the arguments for a particular protocol, type\n\
 			  &targetlist, 0, &cmdlist);
   c = add_cmd (t.shortname, no_class, t.doc, &targetlist);
   c->set_context ((void *) &t);
-  set_cmd_sfunc (c, open_target);
+  c->func = open_target;
   if (completer != NULL)
     set_cmd_completer (c, completer);
 }
@@ -892,7 +892,7 @@ add_deprecated_target_alias (const target_info &tinfo, const char *alias)
   /* If we use add_alias_cmd, here, we do not get the deprecated warning,
      see PR cli/15104.  */
   c = add_cmd (alias, no_class, tinfo.doc, &targetlist);
-  set_cmd_sfunc (c, open_target);
+  c->func = open_target;
   c->set_context ((void *) &tinfo);
   alt = xstrprintf ("target %s", tinfo.shortname);
   deprecate_cmd (c, alt);
diff --git a/gdb/tui/tui-layout.c b/gdb/tui/tui-layout.c
index f9c94dea96cd..2dfc51935d96 100644
--- a/gdb/tui/tui-layout.c
+++ b/gdb/tui/tui-layout.c
@@ -169,8 +169,7 @@ find_layout (tui_layout_split *layout)
 /* Function to set the layout. */
 
 static void
-tui_apply_layout (struct cmd_list_element *command,
-		  const char *args, int from_tty)
+tui_apply_layout (const char *args, int from_tty, cmd_list_element *command)
 {
   tui_layout_split *layout = (tui_layout_split *) command->context ();
 
-- 
2.32.0


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

* [PATCH 14/16] gdb/testsuite: test get/set value of unregistered Guile parameter
  2021-07-14  4:55 [PATCH 00/16] Bunch of commands related cleanups Simon Marchi
                   ` (12 preceding siblings ...)
  2021-07-14  4:55 ` [PATCH 13/16] gdb: remove cmd_list_element::function::sfunc Simon Marchi
@ 2021-07-14  4:55 ` Simon Marchi
  2021-07-23 19:42   ` Simon Marchi
  2021-07-14  4:55 ` [PATCH 15/16] gdb: make cmd_list_element var an optional union Simon Marchi
  2021-07-14  4:55 ` [PATCH 16/16] gdb: make string-like set show commands use std::string variable Simon Marchi
  15 siblings, 1 reply; 50+ messages in thread
From: Simon Marchi @ 2021-07-14  4:55 UTC (permalink / raw)
  To: gdb-patches

When creating a parameter in Guile, you have to create it using
make-parameter and then register it with GDB with register-parameter!.
In between, it's still possible (though not documented) to set the
parameter's value.  I broke this use case by mistake while writing this
series, so thought it would be good to have a test for it.

I suppose that people could use this "feature" to give their parameter
an initial value, even though make-parameter has an initial-value
parameter for this.  Nevertheless, changing this behavior could break
some scripts, which is why I think it's important for it to be tested.

Change-Id: I5b2103e3cec0cfdcccf7ffb00eb05fed8626e66d
---
 gdb/testsuite/gdb.guile/scm-parameter.exp | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/gdb/testsuite/gdb.guile/scm-parameter.exp b/gdb/testsuite/gdb.guile/scm-parameter.exp
index 67120b233664..5a2fb42503d0 100644
--- a/gdb/testsuite/gdb.guile/scm-parameter.exp
+++ b/gdb/testsuite/gdb.guile/scm-parameter.exp
@@ -176,6 +176,14 @@ with_test_prefix "test-restricted-param" {
 gdb_test "guile (register-parameter! (make-parameter \"height\"))" \
     "ERROR.*is already defined.*" "error registering existing parameter"
 
+# Test printing and setting the value of an unregistered parameter.
+gdb_test "guile (print (parameter-value (make-parameter \"foo\")))" \
+    "= #f"
+gdb_test "guile (define myparam (make-parameter \"foo\"))"
+gdb_test_no_output "guile (set-parameter-value! myparam #t)"
+gdb_test "guile (print (parameter-value myparam))" \
+    "= #t"
+
 # Test registering a parameter named with what was an ambiguous spelling
 # of existing parameters.
 
-- 
2.32.0


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

* [PATCH 15/16] gdb: make cmd_list_element var an optional union
  2021-07-14  4:55 [PATCH 00/16] Bunch of commands related cleanups Simon Marchi
                   ` (13 preceding siblings ...)
  2021-07-14  4:55 ` [PATCH 14/16] gdb/testsuite: test get/set value of unregistered Guile parameter Simon Marchi
@ 2021-07-14  4:55 ` Simon Marchi
  2021-07-14 12:08   ` Lancelot SIX
                     ` (2 more replies)
  2021-07-14  4:55 ` [PATCH 16/16] gdb: make string-like set show commands use std::string variable Simon Marchi
  15 siblings, 3 replies; 50+ messages in thread
From: Simon Marchi @ 2021-07-14  4:55 UTC (permalink / raw)
  To: gdb-patches

The field cmd_list_element::var is used by set and show commands and
points to the variable affected by the set command and shown by the show
command.  It is of type `void *` and cast to a pointer of the
appriopriate type on each use depending on the type of the setting
(cmd_list_element::var_type).

I propose changing cmd_list_element to be a union of all the possible
pointer types.  Fundamentally, this doesn't change much what is
happening.  But I think this helps understand better how the field is
used and get a bit of type safety (while it is still possible to use the
wrong member at some point, we can't cast to something completely
unrealted).  Finally, when doing refactorings, such as one later in this
series which changes `char *` to `std::string`, it ensures that we don't
forget to update some spots.

I wrapped the union in an optional, because we need to check in some
spots whether var is set or not.  I think that conceptually, an optional
makes the most sense.  Another option would be to pick an arbitrary
member of the union and compare it to nullptr, whenever we want to know
whether var is set, but that seems a bit more hack-ish.

A note on the Guile situation: something that makes our life a little
bit complicated is that is possible to assign and read the value of a
Guile-created parameter that is not yet registered (see previous patch
that adds a test for this).  It would have been much more simple to say
that this is simply not supported anymore, but that could break existing
scripts, so I don't think it is a good idea.  In some cases, for example
when printing the value of a built-in parameter, we have access to a
show command and its union setting_variable.  When we have an
un-registered Guile param, we don't have a show command associated to
it, but we can pass the parameter's pascm_variable union, stored in
param_smob.  Because we have these two kinds of incompatible parameters,
we need two versions of the pascm_param_value function.

Change-Id: I74a6be2a9188520dbdac4cb123c08e00ebb40a91
---
 gdb/cli/cli-cmds.c           |  48 +++++++++-----
 gdb/cli/cli-decode.c         | 113 +++++++++++++++++++++++--------
 gdb/cli/cli-decode.h         |  31 ++++++++-
 gdb/cli/cli-setshow.c        |  87 +++++++++++++-----------
 gdb/guile/scm-param.c        | 125 +++++++++++++++++++++++++++++++----
 gdb/python/py-param.c        |   8 ++-
 gdb/python/python-internal.h |   2 +-
 gdb/python/python.c          |  35 +++++++---
 gdb/remote.c                 |   2 +-
 9 files changed, 340 insertions(+), 111 deletions(-)

diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index 5ff0b77eb68f..a6b7ef637ab3 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -232,7 +232,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
@@ -2093,26 +2093,26 @@ value_from_setting (const cmd_list_element *cmd, struct gdbarch *gdbarch)
   switch (cmd->var_type)
     {
     case var_integer:
-      if (*(int *) cmd->var == INT_MAX)
+      if (*cmd->var->int_var == 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);
+				   *cmd->var->int_var);
     case var_zinteger:
       return value_from_longest (builtin_type (gdbarch)->builtin_int,
-				 *(int *) cmd->var);
+				 *cmd->var->int_var);
     case var_boolean:
       return value_from_longest (builtin_type (gdbarch)->builtin_int,
-				 *(bool *) cmd->var ? 1 : 0);
+				 *cmd->var->bool_var ? 1 : 0);
     case var_zuinteger_unlimited:
       return value_from_longest (builtin_type (gdbarch)->builtin_int,
-				 *(int *) cmd->var);
+				 *cmd->var->int_var);
     case var_auto_boolean:
       {
 	int val;
 
-	switch (*(enum auto_boolean*) cmd->var)
+	switch (*cmd->var->auto_boolean_var)
 	  {
 	  case AUTO_BOOLEAN_TRUE:
 	    val = 1;
@@ -2130,23 +2130,32 @@ value_from_setting (const cmd_list_element *cmd, struct gdbarch *gdbarch)
 				   val);
       }
     case var_uinteger:
-      if (*(unsigned int *) cmd->var == UINT_MAX)
+      if (*cmd->var->unsigned_int_var == 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);
+	   *cmd->var->unsigned_int_var);
     case var_zuinteger:
       return value_from_ulongest (builtin_type (gdbarch)->builtin_unsigned_int,
-				  *(unsigned int *) cmd->var);
+				  *cmd->var->unsigned_int_var);
     case var_string:
     case var_string_noescape:
     case var_optional_filename:
     case var_filename:
+      if (*cmd->var->char_ptr_var != nullptr)
+	return value_cstring (*cmd->var->char_ptr_var,
+			      strlen (*cmd->var->char_ptr_var) + 1,
+			      builtin_type (gdbarch)->builtin_char);
+      else
+	return value_cstring ("", 1,
+			      builtin_type (gdbarch)->builtin_char);
+
     case var_enum:
-      if (*(char **) cmd->var)
-	return value_cstring (*(char **) cmd->var, strlen (*(char **) cmd->var),
+      if (*cmd->var->const_char_ptr_var != nullptr)
+	return value_cstring (*cmd->var->const_char_ptr_var,
+			      strlen (*cmd->var->const_char_ptr_var) + 1,
 			      builtin_type (gdbarch)->builtin_char);
       else
 	return value_cstring ("", 1,
@@ -2206,13 +2215,22 @@ str_value_from_setting (const cmd_list_element *cmd, struct gdbarch *gdbarch)
     case var_string_noescape:
     case var_optional_filename:
     case var_filename:
-    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),
+      if (*cmd->var->char_ptr_var)
+	return value_cstring (*cmd->var->char_ptr_var,
+			      strlen (*cmd->var->char_ptr_var) + 1,
+			      builtin_type (gdbarch)->builtin_char);
+      else
+	return value_cstring ("", 1,
+			      builtin_type (gdbarch)->builtin_char);
+
+    case var_enum:
+      if (*cmd->var->const_char_ptr_var)
+	return value_cstring (*cmd->var->const_char_ptr_var,
+			      strlen (*cmd->var->const_char_ptr_var) + 1,
 			      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 06f3de0f038a..2db3ddc0551b 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -459,7 +459,6 @@ empty_func (const char *args, int from_tty, cmd_list_element *c)
    TYPE is set_cmd or show_cmd.
    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.
    DOC is the documentation string.  */
 
 static struct cmd_list_element *
@@ -467,7 +466,6 @@ add_set_or_show_cmd (const char *name,
 		     enum cmd_types type,
 		     enum command_class theclass,
 		     var_types var_type,
-		     void *var,
 		     const char *doc,
 		     struct cmd_list_element **list)
 {
@@ -476,7 +474,6 @@ 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;
   /* This needs to be something besides NULL so that this isn't
      treated as a help class.  */
   c->func = empty_func;
@@ -485,8 +482,7 @@ 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
+   setting.  SET_FUNC and SHOW_FUNC are the callback functions (if
    non-NULL).  SET_DOC, SHOW_DOC and HELP_DOC are the documentation
    strings.
 
@@ -495,7 +491,7 @@ add_set_or_show_cmd (const char *name,
 static set_show_commands
 add_setshow_cmd_full (const char *name,
 		      enum command_class theclass,
-		      var_types var_type, void *var,
+		      var_types var_type,
 		      const char *set_doc, const char *show_doc,
 		      const char *help_doc,
 		      cmd_func_ftype *set_func,
@@ -518,14 +514,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,
 			     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,
 			      full_show_doc, show_list);
   show->doc_allocated = 1;
   show->show_value_func = show_func;
@@ -555,11 +551,17 @@ 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,
+    =  add_setshow_cmd_full (name, theclass, var_enum,
 			     set_doc, show_doc, help_doc,
 			     set_func, show_func,
 			     set_list, show_list);
+
+  commands.set->var.emplace ();
+  commands.set->var->const_char_ptr_var = var;
   commands.set->enums = enumlist;
+  commands.show->var.emplace ();
+  commands.show->var->const_char_ptr_var = var;
+
   return commands;
 }
 
@@ -583,12 +585,16 @@ 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,
+    = add_setshow_cmd_full (name, theclass, var_auto_boolean,
 			    set_doc, show_doc, help_doc,
 			    set_func, show_func,
 			    set_list, show_list);
 
+  commands.set->var.emplace ();
+  commands.set->var->auto_boolean_var = var;
   commands.set->enums = auto_boolean_enums;
+  commands.show->var.emplace ();
+  commands.show->var->auto_boolean_var = var;
 
   return commands;
 }
@@ -612,12 +618,16 @@ 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,
+    = add_setshow_cmd_full (name, theclass, var_boolean,
 			    set_doc, show_doc, help_doc,
 			    set_func, show_func,
 			    set_list, show_list);
 
+  commands.set->var.emplace ();
+  commands.set->var->bool_var = var;
   commands.set->enums = boolean_enums;
+  commands.show->var.emplace ();
+  commands.show->var->bool_var = var;
 
   return commands;
 }
@@ -636,13 +646,18 @@ 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,
+    = add_setshow_cmd_full (name, theclass, var_filename,
 			    set_doc, show_doc, help_doc,
 			    set_func, show_func,
 			    set_list, show_list);
 
   set_cmd_completer (commands.set, filename_completer);
 
+  commands.set->var.emplace ();
+  commands.set->var->char_ptr_var = var;
+  commands.show->var.emplace ();
+  commands.show->var->char_ptr_var = var;
+
   return commands;
 }
 
@@ -660,7 +675,7 @@ 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,
+    = add_setshow_cmd_full (name, theclass, var_string,
 			    set_doc, show_doc, help_doc,
 			    set_func, show_func,
 			    set_list, show_list);
@@ -668,6 +683,11 @@ add_setshow_string_cmd (const char *name, enum command_class theclass,
   /* Disable the default symbol completer.  */
   set_cmd_completer (commands.set, nullptr);
 
+  commands.set->var.emplace ();
+  commands.set->var->char_ptr_var = var;
+  commands.show->var.emplace ();
+  commands.show->var->char_ptr_var = var;
+
   return commands;
 }
 
@@ -685,7 +705,7 @@ 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,
+    = add_setshow_cmd_full (name, theclass, var_string_noescape,
 			    set_doc, show_doc, help_doc,
 			    set_func, show_func,
 			    set_list, show_list);
@@ -693,6 +713,11 @@ add_setshow_string_noescape_cmd (const char *name, enum command_class theclass,
   /* Disable the default symbol completer.  */
   set_cmd_completer (commands.set, nullptr);
 
+  commands.set->var.emplace ();
+  commands.set->var->char_ptr_var = var;
+  commands.show->var.emplace ();
+  commands.show->var->char_ptr_var = var;
+
   return commands;
 }
 
@@ -710,13 +735,18 @@ 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,
+    = add_setshow_cmd_full (name, theclass, var_optional_filename,
 			    set_doc, show_doc, help_doc,
 			    set_func, show_func,
 			    set_list, show_list);
 		
   set_cmd_completer (commands.set, filename_completer);
 
+  commands.set->var.emplace ();
+  commands.set->var->char_ptr_var = var;
+  commands.show->var.emplace ();
+  commands.show->var->char_ptr_var = var;
+
   return commands;
 }
 
@@ -754,13 +784,18 @@ 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,
+    = add_setshow_cmd_full (name, theclass, var_integer,
 			    set_doc, show_doc, help_doc,
 			    set_func, show_func,
 			    set_list, show_list);
 
   set_cmd_completer (commands.set, integer_unlimited_completer);
 
+  commands.set->var.emplace ();
+  commands.set->var->int_var = var;
+  commands.show->var.emplace ();
+  commands.show->var->int_var = var;
+
   return commands;
 }
 
@@ -780,13 +815,18 @@ 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,
+    = add_setshow_cmd_full (name, theclass, var_uinteger,
 			    set_doc, show_doc, help_doc,
 			    set_func, show_func,
 			    set_list, show_list);
 
   set_cmd_completer (commands.set, integer_unlimited_completer);
 
+  commands.set->var.emplace ();
+  commands.set->var->unsigned_int_var = var;
+  commands.show->var.emplace ();
+  commands.show->var->unsigned_int_var = var;
+
   return commands;
 }
 
@@ -805,10 +845,18 @@ 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);
+  set_show_commands commands
+    = add_setshow_cmd_full (name, theclass, var_zinteger,
+			    set_doc, show_doc, help_doc,
+			    set_func, show_func,
+			    set_list, show_list);
+
+  commands.set->var.emplace ();
+  commands.set->var->int_var = var;
+  commands.show->var.emplace ();
+  commands.show->var->int_var = var;
+
+  return commands;
 }
 
 set_show_commands
@@ -824,13 +872,18 @@ 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,
+    = add_setshow_cmd_full (name, theclass, var_zuinteger_unlimited,
 			    set_doc, show_doc, help_doc,
 			    set_func, show_func,
 			    set_list, show_list);
 
   set_cmd_completer (commands.set, integer_unlimited_completer);
 
+  commands.set->var.emplace ();
+  commands.set->var->int_var = var;
+  commands.show->var.emplace ();
+  commands.show->var->int_var = var;
+
   return commands;
 }
 
@@ -849,10 +902,18 @@ 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);
+  set_show_commands commands
+    = add_setshow_cmd_full (name, theclass, var_zuinteger,
+			    set_doc, show_doc, help_doc,
+			    set_func, show_func,
+			    set_list, show_list);
+
+  commands.set->var.emplace ();
+  commands.set->var->unsigned_int_var = var;
+  commands.show->var.emplace ();
+  commands.show->var->unsigned_int_var = var;
+
+  return commands;
 }
 
 /* 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 651d1ef8abb7..ba07d6a69f17 100644
--- a/gdb/cli/cli-decode.h
+++ b/gdb/cli/cli-decode.h
@@ -228,9 +228,34 @@ 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;
+  /* Pointer to variable affected by "set" and "show".
+
+     Not used if TYPE is not_set.
+
+     The effective member of the union depends on VAR_TYPE.  */
+  union setting_variable
+  {
+    /* Used by var_integer, var_zinteger and var_zuinteger_unlimited.  */
+    int *int_var;
+
+    /* Used by var_uinteger and var_zuinteger.  */
+    unsigned int *unsigned_int_var;
+
+    /* Used by var_boolean.  */
+    bool *bool_var;
+
+    /* Used by var_auto_boolean.  */
+    auto_boolean *auto_boolean_var;
+
+    /* Used by var_filename, var_string, var_string_noescape and
+       var_optional_filename.  */
+    char **char_ptr_var;
+
+    /* Used by var_enum.  */
+    const char **const_char_ptr_var;
+  };
+
+  gdb::optional<setting_variable> 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 0290acede7fa..8f456b6fc14b 100644
--- a/gdb/cli/cli-setshow.c
+++ b/gdb/cli/cli-setshow.c
@@ -353,11 +353,11 @@ 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)
+	if (*c->var->char_ptr_var == NULL
+	    || strcmp (*c->var->char_ptr_var, newobj) != 0)
 	  {
-	    xfree (*(char **) c->var);
-	    *(char **) c->var = newobj;
+	    xfree (*c->var->char_ptr_var);
+	    *c->var->char_ptr_var = newobj;
 
 	    option_changed = 1;
 	  }
@@ -366,10 +366,10 @@ 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)
+      if (*c->var->char_ptr_var == NULL || strcmp (*c->var->char_ptr_var, arg) != 0)
 	{
-	  xfree (*(char **) c->var);
-	  *(char **) c->var = xstrdup (arg);
+	  xfree (*c->var->char_ptr_var);
+	  *c->var->char_ptr_var = xstrdup (arg);
 
 	  option_changed = 1;
 	}
@@ -398,11 +398,11 @@ 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)
+	if (*c->var->char_ptr_var == NULL
+	    || strcmp (*c->var->char_ptr_var, val) != 0)
 	  {
-	    xfree (*(char **) c->var);
-	    *(char **) c->var = val;
+	    xfree (*c->var->char_ptr_var);
+	    *c->var->char_ptr_var = val;
 
 	    option_changed = 1;
 	  }
@@ -416,9 +416,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->bool_var)
 	  {
-	    *(bool *) c->var = val;
+	    *c->var->bool_var = val;
 
 	    option_changed = 1;
 	  }
@@ -428,9 +428,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->auto_boolean_var != val)
 	  {
-	    *(enum auto_boolean *) c->var = val;
+	    *c->var->auto_boolean_var = val;
 
 	    option_changed = 1;
 	  }
@@ -441,9 +441,9 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
       {
 	unsigned int val = parse_cli_var_uinteger (c->var_type, &arg, true);
 
-	if (*(unsigned int *) c->var != val)
+	if (*c->var->unsigned_int_var != val)
 	  {
-	    *(unsigned int *) c->var = val;
+	    *c->var->unsigned_int_var = val;
 
 	    option_changed = 1;
 	  }
@@ -477,9 +477,9 @@ 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 (*(int *) c->var != val)
+	if (*c->var->int_var != val)
 	  {
-	    *(int *) c->var = val;
+	    *c->var->int_var = val;
 
 	    option_changed = 1;
 	  }
@@ -495,9 +495,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->const_char_ptr_var != match)
 	  {
-	    *(const char **) c->var = match;
+	    *c->var->const_char_ptr_var = match;
 
 	    option_changed = 1;
 	  }
@@ -507,9 +507,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->int_var != val)
 	  {
-	    *(int *) c->var = val;
+	    *c->var->int_var = val;
 	    option_changed = 1;
 	  }
       }
@@ -584,19 +584,23 @@ 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->char_ptr_var);
+	  break;
 	case var_enum:
-	  gdb::observers::command_param_changed.notify (name, *(char **) c->var);
+	  gdb::observers::command_param_changed.notify
+	    (name, *c->var->const_char_ptr_var);
 	  break;
 	case var_boolean:
 	  {
-	    const char *opt = *(bool *) c->var ? "on" : "off";
+	    const char *opt = *c->var->bool_var ? "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->auto_boolean_var];
 
 	    gdb::observers::command_param_changed.notify (name, s);
 	  }
@@ -606,7 +610,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->unsigned_int_var);
 	    gdb::observers::command_param_changed.notify (name, s);
 	  }
 	  break;
@@ -616,7 +620,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->int_var);
 	    gdb::observers::command_param_changed.notify (name, s);
 	  }
 	  break;
@@ -635,21 +639,24 @@ get_setshow_command_value_string (const cmd_list_element *c)
   switch (c->var_type)
     {
     case var_string:
-      if (*(char **) c->var)
-	stb.putstr (*(char **) c->var, '"');
+      if (*c->var->char_ptr_var)
+	stb.putstr (*c->var->char_ptr_var, '"');
       break;
     case var_string_noescape:
     case var_optional_filename:
     case var_filename:
+      if (*c->var->char_ptr_var)
+	stb.puts (*c->var->char_ptr_var);
+      break;
     case var_enum:
-      if (*(char **) c->var)
-	stb.puts (*(char **) c->var);
+      if (*c->var->const_char_ptr_var)
+	stb.puts (*c->var->const_char_ptr_var);
       break;
     case var_boolean:
-      stb.puts (*(bool *) c->var ? "on" : "off");
+      stb.puts (*c->var->bool_var ? "on" : "off");
       break;
     case var_auto_boolean:
-      switch (*(enum auto_boolean*) c->var)
+      switch (*c->var->auto_boolean_var)
 	{
 	case AUTO_BOOLEAN_TRUE:
 	  stb.puts ("on");
@@ -668,25 +675,25 @@ get_setshow_command_value_string (const cmd_list_element *c)
     case var_uinteger:
     case var_zuinteger:
       if (c->var_type == var_uinteger
-	  && *(unsigned int *) c->var == UINT_MAX)
+	  && *c->var->unsigned_int_var == UINT_MAX)
 	stb.puts ("unlimited");
       else
-	stb.printf ("%u", *(unsigned int *) c->var);
+	stb.printf ("%u", *c->var->unsigned_int_var);
       break;
     case var_integer:
     case var_zinteger:
       if (c->var_type == var_integer
-	  && *(int *) c->var == INT_MAX)
+	  && *c->var->int_var == INT_MAX)
 	stb.puts ("unlimited");
       else
-	stb.printf ("%d", *(int *) c->var);
+	stb.printf ("%d", *c->var->int_var);
       break;
     case var_zuinteger_unlimited:
       {
-	if (*(int *) c->var == -1)
+	if (*c->var->int_var == -1)
 	  stb.puts ("unlimited");
 	else
-	  stb.printf ("%d", *(int *) c->var);
+	  stb.printf ("%d", *c->var->int_var);
       }
       break;
     default:
diff --git a/gdb/guile/scm-param.c b/gdb/guile/scm-param.c
index 44ea167be277..57a8c7ec68a2 100644
--- a/gdb/guile/scm-param.c
+++ b/gdb/guile/scm-param.c
@@ -133,7 +133,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,
+static SCM pascm_param_value (enum var_types type,
+			      const pascm_variable *var,
 			      int arg_pos, const char *func_name);
 \f
 /* Administrivia for parameter smobs.  */
@@ -563,10 +564,13 @@ 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 TYPE is not supported, then a <gdb:exception> object is returned.
+
+   This overload can be used on Guile-created parameters, even if they
+   haven't been registered yet.  */
 
 static SCM
-pascm_param_value (enum var_types type, void *var,
+pascm_param_value (enum var_types type, const pascm_variable *param_value,
 		   int arg_pos, const char *func_name)
 {
   /* Note: We *could* support var_integer here in case someone is trying to get
@@ -579,9 +583,18 @@ pascm_param_value (enum var_types type, void *var,
     case var_string_noescape:
     case var_optional_filename:
     case var_filename:
+      {
+	const char *str = param_value->stringval;
+
+	if (str == NULL)
+	  str = "";
+
+	return gdbscm_scm_from_host_string (str, strlen (str));
+      }
+
     case var_enum:
       {
-	const char *str = *(char **) var;
+	const char *str = param_value->cstringval;
 
 	if (str == NULL)
 	  str = "";
@@ -590,7 +603,7 @@ pascm_param_value (enum var_types type, void *var,
 
     case var_boolean:
       {
-	if (* (bool *) var)
+	if (param_value->boolval)
 	  return SCM_BOOL_T;
 	else
 	  return SCM_BOOL_F;
@@ -598,7 +611,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 = param_value->autoboolval;
 
 	if (ab == AUTO_BOOLEAN_TRUE)
 	  return SCM_BOOL_T;
@@ -609,19 +622,19 @@ pascm_param_value (enum var_types type, void *var,
       }
 
     case var_zuinteger_unlimited:
-      if (* (int *) var == -1)
+      if (param_value->intval == -1)
 	return unlimited_keyword;
-      gdb_assert (* (int *) var >= 0);
+      gdb_assert (param_value->intval >= 0);
       /* Fall through.  */
     case var_zinteger:
-      return scm_from_int (* (int *) var);
+      return scm_from_int (param_value->intval);
 
     case var_uinteger:
-      if (* (unsigned int *) var == UINT_MAX)
+      if (param_value->uintval == UINT_MAX)
 	return unlimited_keyword;
       /* Fall through.  */
     case var_zuinteger:
-      return scm_from_uint (* (unsigned int *) var);
+      return scm_from_uint (param_value->uintval);
 
     default:
       break;
@@ -632,6 +645,92 @@ pascm_param_value (enum var_types type, void *var,
 					 _("program error: unhandled type"));
 }
 
+/* Return the value of a gdb parameter as a Scheme value.
+   If SHOW_CMD::VAR_TYPE is not supported, then a <gdb:exception> object is
+   returned.
+
+   This overload can be used with any parameter that has an associated show
+   command, such as built-in commands and registered Guile parameters.  */
+
+static SCM
+pascm_param_value (const cmd_list_element *show_cmd,
+		   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.  */
+
+  gdb_assert (show_cmd->type == cmd_types::show_cmd);
+
+  switch (show_cmd->var_type)
+    {
+    case var_string:
+    case var_string_noescape:
+    case var_optional_filename:
+    case var_filename:
+      {
+	const char *str = *show_cmd->var->char_ptr_var;
+
+	if (str == NULL)
+	  str = "";
+
+	return gdbscm_scm_from_host_string (str, strlen (str));
+      }
+
+    case var_enum:
+      {
+	const char *str = *show_cmd->var->const_char_ptr_var;
+
+	if (str == NULL)
+	  str = "";
+
+	return gdbscm_scm_from_host_string (str, strlen (str));
+      }
+
+    case var_boolean:
+      {
+	if (*show_cmd->var->bool_var)
+	  return SCM_BOOL_T;
+	else
+	  return SCM_BOOL_F;
+      }
+
+    case var_auto_boolean:
+      {
+	enum auto_boolean ab = *show_cmd->var->auto_boolean_var;
+
+	if (ab == AUTO_BOOLEAN_TRUE)
+	  return SCM_BOOL_T;
+	else if (ab == AUTO_BOOLEAN_FALSE)
+	  return SCM_BOOL_F;
+	else
+	  return auto_keyword;
+      }
+
+    case var_zuinteger_unlimited:
+      if (*show_cmd->var->int_var == -1)
+	return unlimited_keyword;
+      gdb_assert (*show_cmd->var->int_var >= 0);
+      /* Fall through.  */
+    case var_zinteger:
+      return scm_from_int (*show_cmd->var->int_var);
+
+    case var_uinteger:
+      if (*show_cmd->var->unsigned_int_var == UINT_MAX)
+	return unlimited_keyword;
+      /* Fall through.  */
+    case var_zuinteger:
+      return scm_from_uint (*show_cmd->var->unsigned_int_var);
+
+    default:
+      break;
+    }
+
+  return gdbscm_make_out_of_range_error (func_name, arg_pos,
+					 scm_from_int (show_cmd->var_type),
+					 _("program error: unhandled type"));
+}
+
 /* Set the value of a parameter of type TYPE in 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.  */
@@ -1062,13 +1161,13 @@ 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, SCM_ARG1, FUNC_NAME);
     }
 }
 
diff --git a/gdb/python/py-param.c b/gdb/python/py-param.c
index f9dcb076c603..b83e3d080eb3 100644
--- a/gdb/python/py-param.c
+++ b/gdb/python/py-param.c
@@ -86,6 +86,9 @@ struct parmpy_object
      allocated with xmalloc, as is each element.  It is
      NULL-terminated.  */
   const char **enumeration;
+
+  /* The commands created as a result of creating this parameter.  */
+  set_show_commands commands;
 };
 
 extern PyTypeObject parmpy_object_type
@@ -110,7 +113,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 (self->commands.show);
     }
 
   return PyObject_GenericGetAttr (obj, attr_name);
@@ -572,6 +575,9 @@ add_setshow_generic (int parmclass, enum command_class cmdclass,
   commands.set->set_context (self);
   commands.show->set_context (self);
 
+  /* Save the created set/show commands.  */
+  self->commands = commands;
+
   /* We (unfortunately) currently leak the command name.  */
   cmd_name.release ();
 }
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 690d2fb43c06..db467540c473 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 cmd_list_element *show_cmd);
 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 e42cbc4fd5ee..c5641c4db8f7 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -448,26 +448,38 @@ 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 cmd_list_element *show_cmd)
 {
-  switch (type)
+  gdb_assert (show_cmd->type == cmd_types::show_cmd);
+
+  switch (show_cmd->var_type)
     {
     case var_string:
     case var_string_noescape:
     case var_optional_filename:
     case var_filename:
+      {
+	const char *str = *show_cmd->var->char_ptr_var;
+
+	if (str == nullptr)
+	  str = "";
+
+	return host_string_to_python_string (str).release ();
+      }
+
     case var_enum:
       {
-	const char *str = *(char **) var;
+	const char *str = *show_cmd->var->const_char_ptr_var;
 
-	if (! str)
+	if (str == nullptr)
 	  str = "";
+
 	return host_string_to_python_string (str).release ();
       }
 
     case var_boolean:
       {
-	if (* (bool *) var)
+	if (*show_cmd->var->bool_var)
 	  Py_RETURN_TRUE;
 	else
 	  Py_RETURN_FALSE;
@@ -475,7 +487,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 = *show_cmd->var->auto_boolean_var;
 
 	if (ab == AUTO_BOOLEAN_TRUE)
 	  Py_RETURN_TRUE;
@@ -486,16 +498,16 @@ gdbpy_parameter_value (enum var_types type, void *var)
       }
 
     case var_integer:
-      if ((* (int *) var) == INT_MAX)
+      if (*show_cmd->var->int_var == 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 (*show_cmd->var->int_var).release ();
 
     case var_uinteger:
       {
-	unsigned int val = * (unsigned int *) var;
+	unsigned int val = *show_cmd->var->unsigned_int_var;
 
 	if (val == UINT_MAX)
 	  Py_RETURN_NONE;
@@ -504,7 +516,7 @@ gdbpy_parameter_value (enum var_types type, void *var)
 
     case var_zuinteger:
       {
-	unsigned int val = * (unsigned int *) var;
+	unsigned int val = *show_cmd->var->unsigned_int_var;
 	return gdb_py_object_from_ulongest (val).release ();
       }
     }
@@ -544,7 +556,8 @@ gdbpy_parameter (PyObject *self, PyObject *args)
   if (! cmd->var)
     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);
 }
 
 /* Wrapper for target_charset.  */
diff --git a/gdb/remote.c b/gdb/remote.c
index e2a08c9a113d..87981330ce87 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -2244,7 +2244,7 @@ show_remote_protocol_packet_cmd (struct ui_file *file, int from_tty,
        packet < &remote_protocol_packets[PACKET_MAX];
        packet++)
     {
-      if (&packet->detect == c->var)
+      if (&packet->detect == c->var->auto_boolean_var)
 	{
 	  show_packet_config_cmd (packet);
 	  return;
-- 
2.32.0


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

* [PATCH 16/16] gdb: make string-like set show commands use std::string variable
  2021-07-14  4:55 [PATCH 00/16] Bunch of commands related cleanups Simon Marchi
                   ` (14 preceding siblings ...)
  2021-07-14  4:55 ` [PATCH 15/16] gdb: make cmd_list_element var an optional union Simon Marchi
@ 2021-07-14  4:55 ` Simon Marchi
  2021-07-28 20:27   ` Tom Tromey
  15 siblings, 1 reply; 50+ messages in thread
From: Simon Marchi @ 2021-07-14  4:55 UTC (permalink / raw)
  To: gdb-patches

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 setting
them to nothing, they point 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
---
 gdb/auto-load.c           |  48 ++++++----------
 gdb/breakpoint.c          |  22 ++++----
 gdb/build-id.c            |   4 +-
 gdb/cli/cli-cmds.c        |  37 ++++---------
 gdb/cli/cli-decode.c      |  24 ++++----
 gdb/cli/cli-decode.h      |  14 ++++-
 gdb/cli/cli-logging.c     |  23 ++++----
 gdb/cli/cli-option.c      |   9 ++-
 gdb/cli/cli-option.h      |   4 +-
 gdb/cli/cli-setshow.c     |  41 +++++---------
 gdb/command.h             |  32 +++++------
 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     |  65 +++++++++++-----------
 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     |  40 ++++++++------
 gdb/python/python.c       |   7 +--
 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 +-
 45 files changed, 456 insertions(+), 505 deletions(-)

diff --git a/gdb/auto-load.c b/gdb/auto-load.c
index 9cd70f174c3c..e72c2436f954 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 89af44ee4c6b..d07084f1d520 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.  */
@@ -6672,7 +6672,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
@@ -8773,17 +8773,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)
@@ -15116,8 +15116,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 ());
@@ -16028,7 +16028,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."), _("\
@@ -16036,7 +16035,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 d2f2576d96d9..553d6cec3d21 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 a6b7ef637ab3..097ed321ab44 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -645,7 +645,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)
@@ -1037,12 +1037,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[] = {
@@ -1079,8 +1074,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)
@@ -1143,8 +1138,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]))
@@ -2144,13 +2139,9 @@ value_from_setting (const cmd_list_element *cmd, struct gdbarch *gdbarch)
     case var_string_noescape:
     case var_optional_filename:
     case var_filename:
-      if (*cmd->var->char_ptr_var != nullptr)
-	return value_cstring (*cmd->var->char_ptr_var,
-			      strlen (*cmd->var->char_ptr_var) + 1,
-			      builtin_type (gdbarch)->builtin_char);
-      else
-	return value_cstring ("", 1,
-			      builtin_type (gdbarch)->builtin_char);
+      return value_cstring (cmd->var->string_var->c_str (),
+			    cmd->var->string_var->length () + 1,
+			    builtin_type (gdbarch)->builtin_char);
 
     case var_enum:
       if (*cmd->var->const_char_ptr_var != nullptr)
@@ -2219,13 +2210,9 @@ str_value_from_setting (const cmd_list_element *cmd, struct gdbarch *gdbarch)
 	 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 (*cmd->var->char_ptr_var)
-	return value_cstring (*cmd->var->char_ptr_var,
-			      strlen (*cmd->var->char_ptr_var) + 1,
-			      builtin_type (gdbarch)->builtin_char);
-      else
-	return value_cstring ("", 1,
-			      builtin_type (gdbarch)->builtin_char);
+      return value_cstring (cmd->var->string_var->c_str (),
+			    cmd->var->string_var->length () + 1,
+			    builtin_type (gdbarch)->builtin_char);
 
     case var_enum:
       if (*cmd->var->const_char_ptr_var)
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index 2db3ddc0551b..78930831736c 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -637,7 +637,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,
@@ -654,9 +654,9 @@ add_setshow_filename_cmd (const char *name, enum command_class theclass,
   set_cmd_completer (commands.set, filename_completer);
 
   commands.set->var.emplace ();
-  commands.set->var->char_ptr_var = var;
+  commands.set->var->string_var = var;
   commands.show->var.emplace ();
-  commands.show->var->char_ptr_var = var;
+  commands.show->var->string_var = var;
 
   return commands;
 }
@@ -666,7 +666,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,
@@ -684,9 +684,9 @@ add_setshow_string_cmd (const char *name, enum command_class theclass,
   set_cmd_completer (commands.set, nullptr);
 
   commands.set->var.emplace ();
-  commands.set->var->char_ptr_var = var;
+  commands.set->var->string_var = var;
   commands.show->var.emplace ();
-  commands.show->var->char_ptr_var = var;
+  commands.show->var->string_var = var;
 
   return commands;
 }
@@ -696,7 +696,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,
@@ -714,9 +714,9 @@ add_setshow_string_noescape_cmd (const char *name, enum command_class theclass,
   set_cmd_completer (commands.set, nullptr);
 
   commands.set->var.emplace ();
-  commands.set->var->char_ptr_var = var;
+  commands.set->var->string_var = var;
   commands.show->var.emplace ();
-  commands.show->var->char_ptr_var = var;
+  commands.show->var->string_var = var;
 
   return commands;
 }
@@ -726,7 +726,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,
@@ -743,9 +743,9 @@ add_setshow_optional_filename_cmd (const char *name, enum command_class theclass
   set_cmd_completer (commands.set, filename_completer);
 
   commands.set->var.emplace ();
-  commands.set->var->char_ptr_var = var;
+  commands.set->var->string_var = var;
   commands.show->var.emplace ();
-  commands.show->var->char_ptr_var = var;
+  commands.show->var->string_var = var;
 
   return commands;
 }
diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h
index ba07d6a69f17..64a1777e6e8e 100644
--- a/gdb/cli/cli-decode.h
+++ b/gdb/cli/cli-decode.h
@@ -249,7 +249,7 @@ struct cmd_list_element
 
     /* Used by var_filename, var_string, var_string_noescape and
        var_optional_filename.  */
-    char **char_ptr_var;
+    std::string *string_var;
 
     /* Used by var_enum.  */
     const char **const_char_ptr_var;
@@ -293,6 +293,18 @@ struct cmd_list_element
   void *m_context = nullptr;
 };
 
+/* Return true if a setting of type VAR_TYPE is backed by an std::string
+   variable.  */
+
+static inline bool
+var_type_uses_string (var_types var_type)
+{
+  return (var_type == var_string
+	  || var_type == var_string_noescape
+	  || var_type == var_optional_filename
+	  || var_type == var_filename);
+}
+
 /* Functions that implement commands about CLI commands.  */
 
 extern void help_cmd (const char *, struct ui_file *);
diff --git a/gdb/cli/cli-logging.c b/gdb/cli/cli-logging.c
index dfedd7599a2f..c9093520a71b 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 ab76f194c3d9..846b8198985f 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 aa2ccbed5d01..b7ede4587485 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 8f456b6fc14b..7bff83c536d7 100644
--- a/gdb/cli/cli-setshow.c
+++ b/gdb/cli/cli-setshow.c
@@ -21,6 +21,7 @@
 #include <ctype.h>
 #include "arch-utils.h"
 #include "observable.h"
+#include "gdbsupport/gdb_tilde_expand.h"
 
 #include "ui-out.h"
 
@@ -353,24 +354,19 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
 	*q++ = '\0';
 	newobj = (char *) xrealloc (newobj, q - newobj);
 
-	if (*c->var->char_ptr_var == NULL
-	    || strcmp (*c->var->char_ptr_var, newobj) != 0)
+	if (*c->var->string_var != newobj)
 	  {
-	    xfree (*c->var->char_ptr_var);
-	    *c->var->char_ptr_var = newobj;
-
+	    *c->var->string_var = newobj;
 	    option_changed = 1;
 	  }
-	else
-	  xfree (newobj);
+
+	xfree (newobj);
       }
       break;
     case var_string_noescape:
-      if (*c->var->char_ptr_var == NULL || strcmp (*c->var->char_ptr_var, arg) != 0)
+      if (*c->var->string_var != arg)
 	{
-	  xfree (*c->var->char_ptr_var);
-	  *c->var->char_ptr_var = xstrdup (arg);
-
+	  *c->var->string_var = arg;
 	  option_changed = 1;
 	}
       break;
@@ -380,7 +376,7 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
       /* FALLTHROUGH */
     case var_optional_filename:
       {
-	char *val = NULL;
+	std::string val;
 
 	if (*arg != '\0')
 	  {
@@ -392,22 +388,15 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
 	      ptr--;
 	    copy = xstrndup (arg, ptr + 1 - arg);
 
-	    val = tilde_expand (copy);
+	    val = gdb_tilde_expand (copy);
 	    xfree (copy);
 	  }
-	else
-	  val = xstrdup ("");
 
-	if (*c->var->char_ptr_var == NULL
-	    || strcmp (*c->var->char_ptr_var, val) != 0)
+	if (*c->var->string_var != val)
 	  {
-	    xfree (*c->var->char_ptr_var);
-	    *c->var->char_ptr_var = val;
-
+	    *c->var->string_var = std::move (val);
 	    option_changed = 1;
 	  }
-	else
-	  xfree (val);
       }
       break;
     case var_boolean:
@@ -585,7 +574,7 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
 	case var_filename:
 	case var_optional_filename:
 	  gdb::observers::command_param_changed.notify
-	    (name, *c->var->char_ptr_var);
+	    (name, c->var->string_var->c_str ());
 	  break;
 	case var_enum:
 	  gdb::observers::command_param_changed.notify
@@ -639,14 +628,12 @@ get_setshow_command_value_string (const cmd_list_element *c)
   switch (c->var_type)
     {
     case var_string:
-      if (*c->var->char_ptr_var)
-	stb.putstr (*c->var->char_ptr_var, '"');
+      stb.putstr (c->var->string_var->c_str (), '"');
       break;
     case var_string_noescape:
     case var_optional_filename:
     case var_filename:
-      if (*c->var->char_ptr_var)
-	stb.puts (*c->var->char_ptr_var);
+      stb.puts (c->var->string_var->c_str ());
       break;
     case var_enum:
       if (*c->var->const_char_ptr_var)
diff --git a/gdb/command.h b/gdb/command.h
index baf34401a070..acf4427f8e82 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -419,28 +419,28 @@ 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 *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);
+  (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 *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);
+  (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 *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);
+  (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 *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);
+  (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_integer_cmd
   (const char *name, command_class theclass, int *var, const char *set_doc,
diff --git a/gdb/compile/compile.c b/gdb/compile/compile.c
index e815348ff078..25cfd283b333 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 ff2494738b0d..4b1451487e28 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 6dea4099554c..f7e09eca9db1 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 7f730f681887..c788f5b618c3 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 eb82bc3cd018..6cbfdcd4f6b6 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 f9d5db6b48a6..5e963d5b47a6 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 c3decf16ead6..f439b37db5ae 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 9292e52c0001..76a40ea36988 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -12016,10 +12016,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 ();
 	}
@@ -12027,7 +12027,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));
@@ -12098,7 +12098,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,
@@ -12427,7 +12427,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 002a7dc95e06..31e2e4dfb1a7 100644
--- a/gdb/event-top.c
+++ b/gdb/event-top.c
@@ -428,13 +428,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)
     {
@@ -445,7 +443,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;
@@ -1174,7 +1172,7 @@ 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);
 #if HAVE_SIGPROCMASK
@@ -1189,7 +1187,7 @@ async_sigtstp_handler (gdb_client_data arg)
 #endif
   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 3ce7d64b8554..3e995ed6f654 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 57a8c7ec68a2..eb9a4936990a 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
 {
@@ -410,14 +407,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);
@@ -426,7 +423,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);
@@ -434,7 +431,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;
@@ -584,12 +581,8 @@ pascm_param_value (enum var_types type, const pascm_variable *param_value,
     case var_optional_filename:
     case var_filename:
       {
-	const char *str = param_value->stringval;
-
-	if (str == NULL)
-	  str = "";
-
-	return gdbscm_scm_from_host_string (str, strlen (str));
+	const std::string &s = *param_value->stringval;
+	return gdbscm_scm_from_host_string (s.data (), s.size ());
       }
 
     case var_enum:
@@ -669,12 +662,8 @@ pascm_param_value (const cmd_list_element *show_cmd,
     case var_optional_filename:
     case var_filename:
       {
-	const char *str = *show_cmd->var->char_ptr_var;
-
-	if (str == NULL)
-	  str = "";
-
-	return gdbscm_scm_from_host_string (str, strlen (str));
+	const std::string &s = *show_cmd->var->string_var;
+	return gdbscm_scm_from_host_string (s.data (), s.size ());
       }
 
     case var_enum:
@@ -752,13 +741,7 @@ pascm_set_param_value_x (enum var_types type, union pascm_variable *var,
 		       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 ("");
-	  else
-	    var->stringval = NULL;
-	}
+	var->stringval->clear ();
       else
 	{
 	  SCM exception;
@@ -767,8 +750,7 @@ pascm_set_param_value_x (enum var_types type, union pascm_variable *var,
 	    = gdbscm_scm_to_host_string (value, NULL, &exception);
 	  if (string == NULL)
 	    gdbscm_throw (exception);
-	  xfree (var->stringval);
-	  var->stringval = string.release ();
+	  *var->stringval = string.get ();
 	}
       break;
 
@@ -868,7 +850,23 @@ pascm_set_param_value_x (enum var_types type, union pascm_variable *var,
       gdb_assert_not_reached ("bad parameter type");
     }
 }
-\f
+
+/* 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_string (p_smob->type))
+    {
+      delete p_smob->value.stringval;
+      p_smob->value.stringval = nullptr;
+    }
+
+  return 0;
+}
+
 /* Parameter Scheme functions.  */
 
 /* (make-parameter name
@@ -1024,6 +1022,11 @@ 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_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 c183b60e81a8..35c97d0296f9 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 5b4e5a8654f9..0c9bc1a88899 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.  */
@@ -942,7 +939,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;
@@ -955,7 +952,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));
@@ -1143,7 +1140,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)
     {
@@ -2006,8 +2003,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 5761ce2bdbef..ca4ccc375dc7 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 b773e759bb7c..77a013d3b7a2 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 0ce889055188..eea3ea995726 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 703c63251fd3..f9685a515ad9 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 5f60eeeb129d..40bde7893bd8 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 b83e3d080eb3..143f41d0bcf9 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;
@@ -142,13 +143,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>
@@ -156,8 +151,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;
 
@@ -504,14 +498,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);
@@ -519,7 +513,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,
@@ -528,7 +522,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;
@@ -714,6 +708,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_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)
@@ -746,7 +743,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_string (parm_obj->type))
+    delete parm_obj->value.stringval;
+}
 
 /* Initialize the 'parameters' module.  */
 int
@@ -785,7 +791,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 c5641c4db8f7..696614d4c9b5 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -459,12 +459,9 @@ gdbpy_parameter_value (const cmd_list_element *show_cmd)
     case var_optional_filename:
     case var_filename:
       {
-	const char *str = *show_cmd->var->char_ptr_var;
+	const std::string &str = *show_cmd->var->string_var;
 
-	if (str == nullptr)
-	  str = "";
-
-	return host_string_to_python_string (str).release ();
+	return host_string_to_python_string (str.c_str ()).release ();
       }
 
     case var_enum:
diff --git a/gdb/remote-sim.c b/gdb/remote-sim.c
index 5bedc04011d4..5bfce2a9a318 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 87981330ce87..197807a94a20 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.  */
@@ -12620,7 +12620,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);
 
@@ -13248,7 +13248,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 7d1bc535ed94..5ec79a11aac4 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 317f7eb485e2..3e7ad60d258b 100644
--- a/gdb/solib.c
+++ b/gdb/solib.c
@@ -96,7 +96,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)
@@ -155,7 +155,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
@@ -319,8 +319,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);
 
@@ -328,8 +328,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);
@@ -378,7 +378,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);
 
@@ -1394,18 +1394,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 c993e25a892e..54b45efab7b4 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.  */
 
@@ -365,17 +365,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.
@@ -386,7 +384,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");
 }
 
@@ -437,10 +435,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 ();
 }
 
@@ -456,20 +451,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);
     }
@@ -481,13 +476,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);
 }
@@ -675,6 +670,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)
@@ -953,7 +959,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)
@@ -1044,7 +1050,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;
 
@@ -1086,19 +1093,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 e4f6129c3e3c..54feef198bde 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 aa20018ad9f0..ad5dd3422175 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -2429,12 +2429,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.  */
@@ -2493,9 +2488,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.  */
@@ -2604,8 +2601,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 0eb48d04d5be..9e5c2d488812 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 fa3f42207ecd..df7ff1e0c6cb 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 f94ace756d4d..f5ec617bb2e3 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 6e0f43d2fd92..908a6d5ee543 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
@@ -908,12 +906,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
@@ -947,24 +949,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));
@@ -1231,14 +1221,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));
@@ -1266,11 +1257,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));
     }
 }
@@ -1643,12 +1634,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;
 }
@@ -1658,10 +1649,7 @@ get_prompt (void)
 void
 set_prompt (const char *s)
 {
-  char *p = xstrdup (s);
-
-  xfree (top_prompt);
-  top_prompt = p;
+  top_prompt = s;
 }
 \f
 
@@ -1801,7 +1789,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;
 
@@ -2049,27 +2037,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
@@ -2119,21 +2088,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 ();
 }
@@ -2158,12 +2126,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 ();
     }
 }
 
@@ -2329,7 +2298,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, _("\
@@ -2415,3 +2384,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 ef88ca024e4e..f8a5c39bbdf4 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 06cf9cad29ad..3997d211182c 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 b32dd61fdfc8..a66226f93f16 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.32.0


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

* Re: [PATCH 15/16] gdb: make cmd_list_element var an optional union
  2021-07-14  4:55 ` [PATCH 15/16] gdb: make cmd_list_element var an optional union Simon Marchi
@ 2021-07-14 12:08   ` Lancelot SIX
  2021-07-14 17:12     ` Lancelot SIX
  2021-07-18 15:44   ` Lancelot SIX
  2021-07-28 19:47   ` Tom Tromey
  2 siblings, 1 reply; 50+ messages in thread
From: Lancelot SIX @ 2021-07-14 12:08 UTC (permalink / raw)
  To: Simon Marchi; +Cc: gdb-patches

On Wed, Jul 14, 2021 at 12:55:19AM -0400, Simon Marchi via Gdb-patches wrote:
> The field cmd_list_element::var is used by set and show commands and
> points to the variable affected by the set command and shown by the show
> command.  It is of type `void *` and cast to a pointer of the
> appriopriate type on each use depending on the type of the setting
> (cmd_list_element::var_type).
> 
> I propose changing cmd_list_element to be a union of all the possible
> pointer types.  Fundamentally, this doesn't change much what is
> happening.  But I think this helps understand better how the field is
> used and get a bit of type safety (while it is still possible to use the
> wrong member at some point, we can't cast to something completely
> unrealted).

Hi,

I happen to have been working on a different approach to the same
problem.  This was triggered when I was reading one of your previous
patches where the surrounding codes had a lot of C style casts all
around.  My approach is arguably too complicated for the task, but it
is meant to address (at least partly) the last point you discuss.

Here is the gist of my approach:  I add templated getter / setter to the
var member, the template parameter being the var_type you expect.  The
usage looks like this, if you want to access the value of a
cmd_list_element of var_types var_zuinteger:

	cmd_list_element *el;
	unsigned int val = el->get_var<var_zuinteger> ();  // This type checks.

The implementation for that requires a bit of boiler plate, but is not
too complex to follow.  I add a helper struct I use to describe the
storage type used or each var_types:

	template<var_types T> struct var_types_storage { };
	template<> struct var_types_storage<var_integer> { using t = int; } ;
	template<> struct var_types_storage<var_uinteger> { using t = unsigned int; };
	template<> struct var_types_storage<var_string> { using t = char *; };
	template<> struct var_types_storage<var_enum> { using t = const char *; };
	// And so on for all the possible var type

With that available, the getter / setter is quite easy to write:

	template<var_types T>
	typename var_types_storage<T>::t get_var () const
	{
	  gdb_assert (T == this->var_type);
	  return *static_cast<typename var_types_storage<T>::t *> (this->var);
	}
	
	template<var_types T>
	void set_var (typename var_types_storage<T>::t v)
	{
	  gdb_assert (T == this->var_type);
	  *static_cast<typename var_types_storage<T>::t *> (this->var> = v;
	}

	template<var_types T>
	void set_var_p (typename var_types_storage<T>::t v)
	{
	  this->var_type = T;
	  this->var = static_cast<void *> v;
	}

add_set_or_show_cmd can also be updated in a similar way:

	template<var_types T> static struct cmd_list_element *
	add_set_or_show_cmd (const char *name,
			     enum cmd_types type,
			     enum command_class theclass,
			     typename var_types_storage<T>::t *var,
			     const char *doc,
			     struct cmd_list_element **list)
	{
	  struct cmd_list_element *c = add_cmd (name, theclass, doc, list);
	  c->set_var_p<T> (var);
	  // rest is unchanged
	  return c;
	}
	
	// similar stuff for add_setshow_cmd_full

Obviously there are a few differences with your approach.

First, with my approach, it is possible to add the gdb_assert to check
that when one accesses a value assuming it is of a given type, it
actually is.  However, there are few limitations with that, most
notably in gdb/cli/cli-setshow.c, where some processing is common
between cases (var_uinteger and var_zuinteger have quite a lot in
common for example).  At some point, the caller wants to call
set_var<one of var_uinteger or var_zuinteger>, but obviously the
assertion will fail half of the time. To go around this, I also use less
type-safe setters / getters:

	template<typename T>
	const T get_var_as() { return *static_cast<T *> (this->var); }
	template<typename T>
	void set_var_as (T v) { *static_cast<T *> (this->var) = v; }

Second, with the templated approach the caller does not need to know
which member of the union he should access for a given var_types, the
compiler will tel him if an incompatible types are used on the call
site.

On the other hand, my approach might be more difficult to follow / more
surprising for someone not familiar with this kind of use of template
specialization.  It also requires a bit more boiler plate code (i.e. the
var_types_storage struct).

I still rely on type punning and a underlying void * pointer, which is
not ideal.  But at least, this becomes a detail of the implementation
and can be ignored by the caller with all the casting done in just one
place.  The void * pointer should (in my approach) eventually be turned
into a private member, which I have not done yet.

Anyway, your approach is quite an improvement compared to the current
type casting all over the place.  I guess that we could, if you want,
have a solution which which is a hybrid of both (i.e. a union instead of
a void*, and templated access functions).

Obviously I am a bit biased toward my approach.  Any thoughts?  I’ll
pause my work on my patch for the moment.

I still need to give a proper read to your patch series, I just skimmed
through to compare our approaches for the moment.

Best,
Lancelot.


> 
> I wrapped the union in an optional, because we need to check in some
> spots whether var is set or not.  I think that conceptually, an optional
> makes the most sense.  Another option would be to pick an arbitrary
> member of the union and compare it to nullptr, whenever we want to know
> whether var is set, but that seems a bit more hack-ish.
> 
> A note on the Guile situation: something that makes our life a little
> bit complicated is that is possible to assign and read the value of a
> Guile-created parameter that is not yet registered (see previous patch
> that adds a test for this).  It would have been much more simple to say
> that this is simply not supported anymore, but that could break existing
> scripts, so I don't think it is a good idea.  In some cases, for example
> when printing the value of a built-in parameter, we have access to a
> show command and its union setting_variable.  When we have an
> un-registered Guile param, we don't have a show command associated to
> it, but we can pass the parameter's pascm_variable union, stored in
> param_smob.  Because we have these two kinds of incompatible parameters,
> we need two versions of the pascm_param_value function.
> 
> Change-Id: I74a6be2a9188520dbdac4cb123c08e00ebb40a91
> ---
>  gdb/cli/cli-cmds.c           |  48 +++++++++-----
>  gdb/cli/cli-decode.c         | 113 +++++++++++++++++++++++--------
>  gdb/cli/cli-decode.h         |  31 ++++++++-
>  gdb/cli/cli-setshow.c        |  87 +++++++++++++-----------
>  gdb/guile/scm-param.c        | 125 +++++++++++++++++++++++++++++++----
>  gdb/python/py-param.c        |   8 ++-
>  gdb/python/python-internal.h |   2 +-
>  gdb/python/python.c          |  35 +++++++---
>  gdb/remote.c                 |   2 +-
>  9 files changed, 340 insertions(+), 111 deletions(-)

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

* Re: [PATCH 15/16] gdb: make cmd_list_element var an optional union
  2021-07-14 12:08   ` Lancelot SIX
@ 2021-07-14 17:12     ` Lancelot SIX
  2021-07-14 19:22       ` Simon Marchi
  0 siblings, 1 reply; 50+ messages in thread
From: Lancelot SIX @ 2021-07-14 17:12 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches

On Wed, Jul 14, 2021 at 12:08:51PM +0000, Lancelot SIX via Gdb-patches wrote:
> On Wed, Jul 14, 2021 at 12:55:19AM -0400, Simon Marchi via Gdb-patches wrote:
> > The field cmd_list_element::var is used by set and show commands and
> > points to the variable affected by the set command and shown by the show
> > command.  It is of type `void *` and cast to a pointer of the
> > appriopriate type on each use depending on the type of the setting
> > (cmd_list_element::var_type).
> > 
> > I propose changing cmd_list_element to be a union of all the possible
> > pointer types.  Fundamentally, this doesn't change much what is
> > happening.  But I think this helps understand better how the field is
> > used and get a bit of type safety (while it is still possible to use the
> > wrong member at some point, we can't cast to something completely
> > unrealted).
> 
> Hi,
> 
> I happen to have been working on a different approach to the same
> problem.  This was triggered when I was reading one of your previous
> patches where the surrounding codes had a lot of C style casts all
> around.  My approach is arguably too complicated for the task, but it
> is meant to address (at least partly) the last point you discuss.
> 
> Here is the gist of my approach:  I add templated getter / setter to the
> var member, the template parameter being the var_type you expect.  The
> usage looks like this, if you want to access the value of a
> cmd_list_element of var_types var_zuinteger:
> 
> 	cmd_list_element *el;
> 	unsigned int val = el->get_var<var_zuinteger> ();  // This type checks.
> 
> The implementation for that requires a bit of boiler plate, but is not
> too complex to follow.  I add a helper struct I use to describe the
> storage type used or each var_types:
> 
> 	template<var_types T> struct var_types_storage { };
> 	template<> struct var_types_storage<var_integer> { using t = int; } ;
> 	template<> struct var_types_storage<var_uinteger> { using t = unsigned int; };
> 	template<> struct var_types_storage<var_string> { using t = char *; };
> 	template<> struct var_types_storage<var_enum> { using t = const char *; };
> 	// And so on for all the possible var type
> 
> With that available, the getter / setter is quite easy to write:
> 
> 	template<var_types T>
> 	typename var_types_storage<T>::t get_var () const
> 	{
> 	  gdb_assert (T == this->var_type);
> 	  return *static_cast<typename var_types_storage<T>::t *> (this->var);
> 	}
> 	
> 	template<var_types T>
> 	void set_var (typename var_types_storage<T>::t v)
> 	{
> 	  gdb_assert (T == this->var_type);
> 	  *static_cast<typename var_types_storage<T>::t *> (this->var> = v;
> 	}
> 
> 	template<var_types T>
> 	void set_var_p (typename var_types_storage<T>::t v)
> 	{
> 	  this->var_type = T;
> 	  this->var = static_cast<void *> v;
> 	}
> 
> add_set_or_show_cmd can also be updated in a similar way:
> 
> 	template<var_types T> static struct cmd_list_element *
> 	add_set_or_show_cmd (const char *name,
> 			     enum cmd_types type,
> 			     enum command_class theclass,
> 			     typename var_types_storage<T>::t *var,
> 			     const char *doc,
> 			     struct cmd_list_element **list)
> 	{
> 	  struct cmd_list_element *c = add_cmd (name, theclass, doc, list);
> 	  c->set_var_p<T> (var);
> 	  // rest is unchanged
> 	  return c;
> 	}
> 	
> 	// similar stuff for add_setshow_cmd_full
> 
> Obviously there are a few differences with your approach.
> 
> First, with my approach, it is possible to add the gdb_assert to check
> that when one accesses a value assuming it is of a given type, it
> actually is.  However, there are few limitations with that, most
> notably in gdb/cli/cli-setshow.c, where some processing is common
> between cases (var_uinteger and var_zuinteger have quite a lot in
> common for example).  At some point, the caller wants to call
> set_var<one of var_uinteger or var_zuinteger>, but obviously the
> assertion will fail half of the time. To go around this, I also use less
> type-safe setters / getters:
> 
> 	template<typename T>
> 	const T get_var_as() { return *static_cast<T *> (this->var); }
> 	template<typename T>
> 	void set_var_as (T v) { *static_cast<T *> (this->var) = v; }
> 
> Second, with the templated approach the caller does not need to know
> which member of the union he should access for a given var_types, the
> compiler will tel him if an incompatible types are used on the call
> site.
> 
> On the other hand, my approach might be more difficult to follow / more
> surprising for someone not familiar with this kind of use of template
> specialization.  It also requires a bit more boiler plate code (i.e. the
> var_types_storage struct).
> 
> I still rely on type punning and a underlying void * pointer, which is
> not ideal.  But at least, this becomes a detail of the implementation
> and can be ignored by the caller with all the casting done in just one
> place.  The void * pointer should (in my approach) eventually be turned
> into a private member, which I have not done yet.
> 
> Anyway, your approach is quite an improvement compared to the current
> type casting all over the place.  I guess that we could, if you want,
> have a solution which which is a hybrid of both (i.e. a union instead of
> a void*, and templated access functions).
> 
> Obviously I am a bit biased toward my approach.  Any thoughts?  I’ll
> pause my work on my patch for the moment.
> 
> I still need to give a proper read to your patch series, I just skimmed
> through to compare our approaches for the moment.
> 
> Best,
> Lancelot.

Actually, I have been playing around with it a bit more today, and with
extra boiler plate, I can remove the get_var_as accessor, and have a
more all-rounded get_var. 

It becomes possible to write something like:

	// No matter if we are looking at a var_uinteger or a
	// var_zuinteger, the underlying type we are interested in is
	// a unsigned int.
	unsigned int *foo = c->get_var<var_uinteger, var_zuinteger> ();

In this case, get_var will even figure the common ground for all
possibilities the user asks for.  The intended use case is when printing
the value of a string (ish) variable, and we want to cover multiple
scenarios including var_enum and var_string, we can write something
like:

	// c is a const char *, but would be char * if var_enum was not
	// in the list.
	auto c = v.get_var<var_enum, var_string, var_filename> ();

The code could look like something like this (I am sure it is possible
to have a simpler solution to create a similar result):

	template<var_types... Ts>
	struct var_types_storage_helper;
	
	template<var_types T>
	struct var_types_storage_helper<T>
	{
	  static constexpr bool all_same = true;
	  using type = typename var_types_storage<T>::t;
	  static constexpr bool covers_var_type (var_types t)
	    {
	      return t == T;
	    }
	};
	
	template<var_types T, var_types U>
	struct var_types_storage_helper<T, U>
	{
	  static constexpr bool all_same = std::is_same<typename var_types_storage<T>::t, typename var_types_storage<U>::t>::value;
	  using type = typename var_types_storage<T>::t;
	  static constexpr bool covers_var_type (var_types t)
	    {
	      return var_types_storage_helper<T>::covers_var_type (t) || var_types_storage_helper<U>::covers_var_type (t);
	    }
	};
	
	template<var_types T, var_types U, var_types... Us>
	struct var_types_storage_helper<T, U, Us...>
	{
	  static constexpr bool all_same = var_types_storage_helper<T, U>::all_same && var_types_storage_helper<T, Us...>::all_same;
	  using type = typename var_types_storage<T>::t;
	  static constexpr bool covers_var_type (var_types t)
	    {
	      return var_types_storage_helper<T> (t) || var_types_storage_helper<U, Us...>::covers_var_type (t);
	    }
	};

and the accessors become:

	  template<var_types... T>
	    typename std::common_type<typename var_types_storage<T>::t...>::type get_var() const
	      {
		assert (var_types_storage_helper<T...>::covers_var_type (this->var_type));
		return *static_cast<typename std::common_type<typename var_types_storage<T>::t...>::type *>(this->var);
	      }
	
	  template<var_types... T>
	    void set_var(typename var_types_storage_helper<T...>::type v)
	      {
		assert (var_types_storage_helper<T...>::covers_var_type (this->var_type));
		static_assert (var_types_storage_helper<T...>::all_same);
		*static_cast<typename var_types_storage_helper<T...>::type *> (this->var) = v;
	      }
	
	  template<var_types T>
	    void set_var_p (typename var_types_storage<T>::t *v)
	      {
		this->var_type = T;
		this->var = static_cast<void *> (v);
	      }

I guess improves the type safety side of things, but the
implementation definitely gets more complicated.  I am not sure the
added checks come at a reasonable price.

This should all be valid c++11.

Any thoughts?

Best,
Lancelot.

> 
> 
> > 
> > I wrapped the union in an optional, because we need to check in some
> > spots whether var is set or not.  I think that conceptually, an optional
> > makes the most sense.  Another option would be to pick an arbitrary
> > member of the union and compare it to nullptr, whenever we want to know
> > whether var is set, but that seems a bit more hack-ish.
> > 
> > A note on the Guile situation: something that makes our life a little
> > bit complicated is that is possible to assign and read the value of a
> > Guile-created parameter that is not yet registered (see previous patch
> > that adds a test for this).  It would have been much more simple to say
> > that this is simply not supported anymore, but that could break existing
> > scripts, so I don't think it is a good idea.  In some cases, for example
> > when printing the value of a built-in parameter, we have access to a
> > show command and its union setting_variable.  When we have an
> > un-registered Guile param, we don't have a show command associated to
> > it, but we can pass the parameter's pascm_variable union, stored in
> > param_smob.  Because we have these two kinds of incompatible parameters,
> > we need two versions of the pascm_param_value function.
> > 
> > Change-Id: I74a6be2a9188520dbdac4cb123c08e00ebb40a91
> > ---
> >  gdb/cli/cli-cmds.c           |  48 +++++++++-----
> >  gdb/cli/cli-decode.c         | 113 +++++++++++++++++++++++--------
> >  gdb/cli/cli-decode.h         |  31 ++++++++-
> >  gdb/cli/cli-setshow.c        |  87 +++++++++++++-----------
> >  gdb/guile/scm-param.c        | 125 +++++++++++++++++++++++++++++++----
> >  gdb/python/py-param.c        |   8 ++-
> >  gdb/python/python-internal.h |   2 +-
> >  gdb/python/python.c          |  35 +++++++---
> >  gdb/remote.c                 |   2 +-
> >  9 files changed, 340 insertions(+), 111 deletions(-)

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

* Re: [PATCH 15/16] gdb: make cmd_list_element var an optional union
  2021-07-14 17:12     ` Lancelot SIX
@ 2021-07-14 19:22       ` Simon Marchi
  2021-07-14 23:22         ` Lancelot SIX
  0 siblings, 1 reply; 50+ messages in thread
From: Simon Marchi @ 2021-07-14 19:22 UTC (permalink / raw)
  To: Lancelot SIX, gdb-patches

On 2021-07-14 1:12 p.m., Lancelot SIX wrote:
>> I happen to have been working on a different approach to the same
>> problem.  This was triggered when I was reading one of your previous
>> patches where the surrounding codes had a lot of C style casts all
>> around.  My approach is arguably too complicated for the task, but it
>> is meant to address (at least partly) the last point you discuss.

Hi Lancelot,

Thanks for taking a look, good to know that somebody else felt the need
to modernize this code.

>> Here is the gist of my approach:  I add templated getter / setter to the
>> var member, the template parameter being the var_type you expect.  The
>> usage looks like this, if you want to access the value of a
>> cmd_list_element of var_types var_zuinteger:
>>
>> 	cmd_list_element *el;
>> 	unsigned int val = el->get_var<var_zuinteger> ();  // This type checks.
>>
>> The implementation for that requires a bit of boiler plate, but is not
>> too complex to follow.  I add a helper struct I use to describe the
>> storage type used or each var_types:
>>
>> 	template<var_types T> struct var_types_storage { };
>> 	template<> struct var_types_storage<var_integer> { using t = int; } ;
>> 	template<> struct var_types_storage<var_uinteger> { using t = unsigned int; };
>> 	template<> struct var_types_storage<var_string> { using t = char *; };
>> 	template<> struct var_types_storage<var_enum> { using t = const char *; };
>> 	// And so on for all the possible var type
>>
>> With that available, the getter / setter is quite easy to write:
>>
>> 	template<var_types T>
>> 	typename var_types_storage<T>::t get_var () const
>> 	{
>> 	  gdb_assert (T == this->var_type);
>> 	  return *static_cast<typename var_types_storage<T>::t *> (this->var);
>> 	}
>> 	
>> 	template<var_types T>
>> 	void set_var (typename var_types_storage<T>::t v)
>> 	{
>> 	  gdb_assert (T == this->var_type);
>> 	  *static_cast<typename var_types_storage<T>::t *> (this->var> = v;
>> 	}
>>
>> 	template<var_types T>
>> 	void set_var_p (typename var_types_storage<T>::t v)
>> 	{
>> 	  this->var_type = T;
>> 	  this->var = static_cast<void *> v;
>> 	}
>>
>> add_set_or_show_cmd can also be updated in a similar way:
>>
>> 	template<var_types T> static struct cmd_list_element *
>> 	add_set_or_show_cmd (const char *name,
>> 			     enum cmd_types type,
>> 			     enum command_class theclass,
>> 			     typename var_types_storage<T>::t *var,
>> 			     const char *doc,
>> 			     struct cmd_list_element **list)
>> 	{
>> 	  struct cmd_list_element *c = add_cmd (name, theclass, doc, list);
>> 	  c->set_var_p<T> (var);
>> 	  // rest is unchanged
>> 	  return c;
>> 	}
>> 	
>> 	// similar stuff for add_setshow_cmd_full
>>
>> Obviously there are a few differences with your approach.
>>
>> First, with my approach, it is possible to add the gdb_assert to check
>> that when one accesses a value assuming it is of a given type, it
>> actually is.  However, there are few limitations with that, most
>> notably in gdb/cli/cli-setshow.c, where some processing is common
>> between cases (var_uinteger and var_zuinteger have quite a lot in
>> common for example).  At some point, the caller wants to call
>> set_var<one of var_uinteger or var_zuinteger>, but obviously the
>> assertion will fail half of the time. To go around this, I also use less
>> type-safe setters / getters:
>>
>> 	template<typename T>
>> 	const T get_var_as() { return *static_cast<T *> (this->var); }
>> 	template<typename T>
>> 	void set_var_as (T v) { *static_cast<T *> (this->var) = v; }
>>
>> Second, with the templated approach the caller does not need to know
>> which member of the union he should access for a given var_types, the
>> compiler will tel him if an incompatible types are used on the call
>> site.
>>
>> On the other hand, my approach might be more difficult to follow / more
>> surprising for someone not familiar with this kind of use of template
>> specialization.  It also requires a bit more boiler plate code (i.e. the
>> var_types_storage struct).
>>
>> I still rely on type punning and a underlying void * pointer, which is
>> not ideal.  But at least, this becomes a detail of the implementation
>> and can be ignored by the caller with all the casting done in just one
>> place.  The void * pointer should (in my approach) eventually be turned
>> into a private member, which I have not done yet.
>>
>> Anyway, your approach is quite an improvement compared to the current
>> type casting all over the place.  I guess that we could, if you want,
>> have a solution which which is a hybrid of both (i.e. a union instead of
>> a void*, and templated access functions).
>>
>> Obviously I am a bit biased toward my approach.  Any thoughts?  I’ll
>> pause my work on my patch for the moment.
>>
>> I still need to give a proper read to your patch series, I just skimmed
>> through to compare our approaches for the moment.
>>
>> Best,
>> Lancelot.
> 
> Actually, I have been playing around with it a bit more today, and with
> extra boiler plate, I can remove the get_var_as accessor, and have a
> more all-rounded get_var. 
> 
> It becomes possible to write something like:
> 
> 	// No matter if we are looking at a var_uinteger or a
> 	// var_zuinteger, the underlying type we are interested in is
> 	// a unsigned int.
> 	unsigned int *foo = c->get_var<var_uinteger, var_zuinteger> ();
> 
> In this case, get_var will even figure the common ground for all
> possibilities the user asks for.  The intended use case is when printing
> the value of a string (ish) variable, and we want to cover multiple
> scenarios including var_enum and var_string, we can write something
> like:
> 
> 	// c is a const char *, but would be char * if var_enum was not
> 	// in the list.
> 	auto c = v.get_var<var_enum, var_string, var_filename> ();
> 
> The code could look like something like this (I am sure it is possible
> to have a simpler solution to create a similar result):
> 
> 	template<var_types... Ts>
> 	struct var_types_storage_helper;
> 	
> 	template<var_types T>
> 	struct var_types_storage_helper<T>
> 	{
> 	  static constexpr bool all_same = true;
> 	  using type = typename var_types_storage<T>::t;
> 	  static constexpr bool covers_var_type (var_types t)
> 	    {
> 	      return t == T;
> 	    }
> 	};
> 	
> 	template<var_types T, var_types U>
> 	struct var_types_storage_helper<T, U>
> 	{
> 	  static constexpr bool all_same = std::is_same<typename var_types_storage<T>::t, typename var_types_storage<U>::t>::value;
> 	  using type = typename var_types_storage<T>::t;
> 	  static constexpr bool covers_var_type (var_types t)
> 	    {
> 	      return var_types_storage_helper<T>::covers_var_type (t) || var_types_storage_helper<U>::covers_var_type (t);
> 	    }
> 	};
> 	
> 	template<var_types T, var_types U, var_types... Us>
> 	struct var_types_storage_helper<T, U, Us...>
> 	{
> 	  static constexpr bool all_same = var_types_storage_helper<T, U>::all_same && var_types_storage_helper<T, Us...>::all_same;
> 	  using type = typename var_types_storage<T>::t;
> 	  static constexpr bool covers_var_type (var_types t)
> 	    {
> 	      return var_types_storage_helper<T> (t) || var_types_storage_helper<U, Us...>::covers_var_type (t);
> 	    }
> 	};
> 
> and the accessors become:
> 
> 	  template<var_types... T>
> 	    typename std::common_type<typename var_types_storage<T>::t...>::type get_var() const
> 	      {
> 		assert (var_types_storage_helper<T...>::covers_var_type (this->var_type));
> 		return *static_cast<typename std::common_type<typename var_types_storage<T>::t...>::type *>(this->var);
> 	      }
> 	
> 	  template<var_types... T>
> 	    void set_var(typename var_types_storage_helper<T...>::type v)
> 	      {
> 		assert (var_types_storage_helper<T...>::covers_var_type (this->var_type));
> 		static_assert (var_types_storage_helper<T...>::all_same);
> 		*static_cast<typename var_types_storage_helper<T...>::type *> (this->var) = v;
> 	      }
> 	
> 	  template<var_types T>
> 	    void set_var_p (typename var_types_storage<T>::t *v)
> 	      {
> 		this->var_type = T;
> 		this->var = static_cast<void *> (v);
> 	      }
> 
> I guess improves the type safety side of things, but the
> implementation definitely gets more complicated.  I am not sure the
> added checks come at a reasonable price.
> 
> This should all be valid c++11.
> 
> Any thoughts?

I am certainly for this kind of consistency checks, making sure we
access the right data the right way.  This is what I did with struct
dynamic_prop, for example.

I was thinking of adding accessors too, eventually.  I am not very good
with advanced C++ templates though, so what I had in mind was more
something like the obvious:

  struct cmd_list_element
  {
    int *var_as_int () // or int &
    {
      gdb_assert (m_var_type == a
		  || m_var_type == b);
      return m_var.int_var;
    }

    unsigned int *var_as_unsigned_int () // or unsigned int &
    {
      gdb_assert (m_var_type == c
		  || m_var_type == c);
      return m_var.unsigned_int_var;
    }

    // And so on.
  };

Your last proposition is a bit better in the sense that you ask "give me
the variable location for a var_zuinteger".  With mine, you still have
to know that a var_zuinteger uses the unsigned int member.  But at
least, there's a gdb_assert to catch mistakes at runtime.

Whether it's worth the extra complexity... I can't tell, I would have to
see the complete patch.  I would also have to see how that fits with the
upcoming fix I have for bug 28085.  In the mean time, does it make your
life more difficult if we merge my patch?  I would guess not, since
you'll need to modify all the call sites (places that access setting
variables) anyway.  You can then decide to keep the union or not in your
patch.

Simon

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

* Re: [PATCH 15/16] gdb: make cmd_list_element var an optional union
  2021-07-14 19:22       ` Simon Marchi
@ 2021-07-14 23:22         ` Lancelot SIX
  2021-07-19 14:32           ` Simon Marchi
  0 siblings, 1 reply; 50+ messages in thread
From: Lancelot SIX @ 2021-07-14 23:22 UTC (permalink / raw)
  To: Simon Marchi; +Cc: gdb-patches

On Wed, Jul 14, 2021 at 03:22:41PM -0400, Simon Marchi wrote:
> 
> I am certainly for this kind of consistency checks, making sure we
> access the right data the right way.  This is what I did with struct
> dynamic_prop, for example.
> 
> I was thinking of adding accessors too, eventually.  I am not very good
> with advanced C++ templates though, so what I had in mind was more
> something like the obvious:
> 
>   struct cmd_list_element
>   {
>     int *var_as_int () // or int &
>     {
>       gdb_assert (m_var_type == a
> 		  || m_var_type == b);
>       return m_var.int_var;
>     }
> 
>     unsigned int *var_as_unsigned_int () // or unsigned int &
>     {
>       gdb_assert (m_var_type == c
> 		  || m_var_type == c);
>       return m_var.unsigned_int_var;
>     }
> 
>     // And so on.
>   };
> 
> Your last proposition is a bit better in the sense that you ask "give me
> the variable location for a var_zuinteger".  With mine, you still have
> to know that a var_zuinteger uses the unsigned int member.  But at
> least, there's a gdb_assert to catch mistakes at runtime.
> 
> Whether it's worth the extra complexity... I can't tell, I would have to
> see the complete patch.  I would also have to see how that fits with the
> upcoming fix I have for bug 28085.  In the mean time, does it make your
> life more difficult if we merge my patch?  I would guess not, since
> you'll need to modify all the call sites (places that access setting
> variables) anyway.  You can then decide to keep the union or not in your
> patch.
> 
> Simon

I have not yet finished updating all callsites, but here is a
(hopefully) working patch if you want to see what it looks like.

I have not had a look at 28085 yet, so I do not yet if my approach gets
in the way of fixing it.  For the moment, I do not have something
similar to the gdb_optional you introduce, but it should not be too
difficult to have something similar.

Anyway, to not hold back on my account.  I have not read your series
carefully yet, but it seems a real improvement from the current
situation.  If it helps fixing a bug, it is even better.  I can rework
my patch to work on top of this one (at least if what it brings is
considered worthwhile).

Lancelot.

---
From 5b578bb0ed51bf9a7c03cc9b390ec636a49d68d5 Mon Sep 17 00:00:00 2001
From: Lancelot SIX <lsix@lancelotsix.com>
Date: Wed, 14 Jul 2021 22:30:14 +0000
Subject: [PATCH] [WIP] Add typesafe getter/setter for cmd_list_element.var

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

With this pattern, the user needs to know what the storage type
associated with a given VAR_TYPES tag, and needs to do the proper
casting.

This looks something like:

if (c->var_type == var_uinteger)
  unsigned_int v = *(unsigned int*)v->var_type;

With this approach, it is easy to make an error.

This patch is a proof of concept for an alternative approach.

We add templated get_var / set_var member functions to the
cmd_list_element.  The template parameter is the VAR_TYPES we want to
the access the data for.  The template is written so the return type of
the get_var / parameter type of the set_var matches the data type used
to store the actual data.

For example, instantiating the member functions with var_boolean will
be something similar to:

	bool get_var<var_boolean> () const
	{
	  gdb_assert (this->var_type == var_boolean);
	  return *static_cast<bool *> (this->var);
	}
	void set_var<var_boolean> (bool var)
	{
	  gdb_assert (this->var_type == var_boolean);
	  *static_cast<bool *> (this->var) = var;
	}

With those new methods, the caller does not need to know the underlying
storage nor he does need to perform the cast manually.

Given that the processing of some options is common between VAR_TYPES,
the templates can be instantiated with more than one VAR_TYPES.  In such
situation, all the template parameters need to share the same underlying
type.

Here is another example of what an instantiation looks like with 2
parameters:

	int get_var<var_integer, var_zinteger> ()
	{
	  gdb_assert (this->var_type == var_integer
	              || this->var_type == var_zinteger);
	  return *static_cast<int *> (this->var);
	}
	void set_var<var_integer, var_zinteger> (int v)
	{
	  gdb_assert (this->var_type == var_integer
	              || this->var_type == var_zinteger);
	  *static_cast<int *> (this->var) = v;
	}

For convenience reasons, the getter is written in such a way that it
accepts template parameters that have different underlying types, as long
as they are compatible (i.e. can all be implicitly converted to).

	char *get_var<var_string> ()
	{
	  gdb_assert (this->var_type == var_string);
	  return *static_cast<char **> (this->var);
	}
	const char *get_var<var_string, var_filename, var_enum> ()
	{
	  gdb_assert (this->var_type == var_string
	              || this->var_type == var_filename
	              || this->var_type == var_enum);
	  return *static_cast<const char **> (this->var);
	}

Eventually the 'var' and 'var_type' members of cmd_list_element will
have to be made private members to make sure all access is done through
the getter / setter, but since this is a POC, only a few callsites have
been updated.
---
 gdb/cli/cli-decode.c  | 114 ++++++++++++++++----------------
 gdb/cli/cli-decode.h  |  49 ++++++++++++++
 gdb/cli/cli-setshow.c |  91 ++++++++++++++------------
 gdb/command.h         | 147 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 305 insertions(+), 96 deletions(-)

diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index 633a3ad9309..35ab7c4d015 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -481,12 +481,12 @@ empty_sfunc (const char *args, int from_tty, struct cmd_list_element *c)
    VAR is address of the variable being controlled by this command.
    DOC is the documentation string.  */
 
+template<var_types T>
 static struct cmd_list_element *
 add_set_or_show_cmd (const char *name,
 		     enum cmd_types type,
 		     enum command_class theclass,
-		     var_types var_type,
-		     void *var,
+		     typename var_types_storage<T>::type *var,
 		     const char *doc,
 		     struct cmd_list_element **list)
 {
@@ -494,7 +494,7 @@ 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_type = T;
   c->var = var;
   /* This needs to be something besides NULL so that this isn't
      treated as a help class.  */
@@ -511,10 +511,11 @@ add_set_or_show_cmd (const char *name,
 
    Return the newly created set and show commands.  */
 
+template<var_types T>
 static set_show_commands
 add_setshow_cmd_full (const char *name,
 		      enum command_class theclass,
-		      var_types var_type, void *var,
+		      typename var_types_storage<T>::type *var,
 		      const char *set_doc, const char *show_doc,
 		      const char *help_doc,
 		      cmd_const_sfunc_ftype *set_func,
@@ -537,15 +538,15 @@ 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,
-			     full_set_doc, set_list);
+  set = add_set_or_show_cmd<T> (name, set_cmd, theclass, var,
+				full_set_doc, set_list);
   set->doc_allocated = 1;
 
   if (set_func != NULL)
     set_cmd_sfunc (set, set_func);
 
-  show = add_set_or_show_cmd (name, show_cmd, theclass, var_type, var,
-			      full_show_doc, show_list);
+  show = add_set_or_show_cmd<T> (name, show_cmd, theclass, var,
+				 full_show_doc, show_list);
   show->doc_allocated = 1;
   show->show_value_func = show_func;
   /* Disable the default symbol completer.  Doesn't make much sense
@@ -574,10 +575,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<var_enum> (name, theclass, var,
+				       set_doc, show_doc, help_doc,
+				       set_func, show_func,
+				       set_list, show_list);
   commands.set->enums = enumlist;
   return commands;
 }
@@ -602,10 +603,10 @@ 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<var_auto_boolean> (name, theclass, var,
+					      set_doc, show_doc, help_doc,
+					      set_func, show_func,
+					      set_list, show_list);
 
   commands.set->enums = auto_boolean_enums;
 
@@ -631,10 +632,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<var_boolean> (name, theclass, var,
+					 set_doc, show_doc, help_doc,
+					 set_func, show_func,
+					 set_list, show_list);
 
   commands.set->enums = boolean_enums;
 
@@ -655,10 +656,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<var_filename> (name, theclass, var,
+					  set_doc, show_doc, help_doc,
+					  set_func, show_func,
+					  set_list, show_list);
 
   set_cmd_completer (commands.set, filename_completer);
 
@@ -679,10 +680,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<var_string> (name, theclass, 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,10 +705,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 (name, theclass, var_string_noescape, var,
-			    set_doc, show_doc, help_doc,
-			    set_func, show_func,
-			    set_list, show_list);
+    = add_setshow_cmd_full<var_string_noescape> (name, theclass, 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);
@@ -729,10 +730,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<var_optional_filename> (name, theclass, var,
+						   set_doc, show_doc, help_doc,
+						   set_func, show_func,
+						   set_list, show_list);
 		
   set_cmd_completer (commands.set, filename_completer);
 
@@ -773,10 +774,10 @@ 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<var_integer> (name, theclass, var,
+					 set_doc, show_doc, help_doc,
+					 set_func, show_func,
+					 set_list, show_list);
 
   set_cmd_completer (commands.set, integer_unlimited_completer);
 
@@ -799,10 +800,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<var_uinteger> (name, theclass, var,
+					  set_doc, show_doc, help_doc,
+					  set_func, show_func,
+					  set_list, show_list);
 
   set_cmd_completer (commands.set, integer_unlimited_completer);
 
@@ -824,10 +825,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<var_zinteger> (name, theclass, var,
+					     set_doc, show_doc, help_doc,
+					     set_func, show_func,
+					     set_list, show_list);
 }
 
 set_show_commands
@@ -843,10 +844,11 @@ 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<var_zuinteger_unlimited> (name, theclass, var,
+						     set_doc, show_doc,
+						     help_doc, set_func,
+						     show_func, set_list,
+						     show_list);
 
   set_cmd_completer (commands.set, integer_unlimited_completer);
 
@@ -868,10 +870,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<var_zuinteger> (name, theclass, 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 241535ae5b5..d54dff5a997 100644
--- a/gdb/cli/cli-decode.h
+++ b/gdb/cli/cli-decode.h
@@ -26,6 +26,7 @@
 #include "gdb_regex.h"
 #include "cli-script.h"
 #include "completer.h"
+#include <type_traits>
 
 /* Not a set/show command.  Note that some commands which begin with
    "set" or "show" might be in this category, if their syntax does
@@ -102,6 +103,54 @@ struct cmd_list_element
   void *context () const
   { return m_context; }
 
+  /* Return the value of the current element that can be shown.
+     The expected template parameter is the VAR_TYPES of the current instance
+     is.  This is enforced with a runtime check.
+
+     If multiple template parameters are given, select the approapriate return
+     type that satisfies all possibilities.  If such type does not exist, the
+     instantiation will fail.  A runtime check is made to ensure that the
+     current instance's type is one of those given as template parameter.  */
+  template<var_types... Ts>
+  typename std::common_type<typename var_types_storage<Ts>::type...>::type get_var() const
+  {
+    gdb_assert (var_types_storage_helper<Ts...>::covers_type (this->var_type));
+
+    return *static_cast<
+      typename std::common_type<
+	typename var_types_storage<Ts>::type...>::type *> (this->var);
+  }
+
+  /* Update the enclosed VAR to the value given as a parameter.
+
+     If one template argument is given, it must be the VAR_TYPE of the current
+     instance.  This is enforced at runtime.
+     If multiple template parameters are given, they must all share the same
+     underlying type (this is checked at compile time), and THIS must be of
+     the type of one of the template parameters (this is checked at runtime).
+     */
+  template<var_types... Ts>
+  void set_var(typename var_types_storage_helper<Ts...>::type v)
+    {
+      /* Ensure that all Ts share the same underlying type.  */
+      static_assert (var_types_storage_helper<Ts...>::all_same);
+
+      /* Check that the current instance is of one of the supported types for
+         this instantiation.  */
+      gdb_assert (var_types_storage_helper<Ts...>::covers_type (this->var_type));
+
+      *static_cast<typename var_types_storage_helper<Ts...>::type *> (this->var)
+	= v;
+    }
+
+  /* Update the pointer to the VAR referenced by this instance.  */
+  template<var_types T>
+  void set_var_p (typename var_types_storage<T>::type *v)
+  {
+    this->type = T;
+    this->var = static_cast<void *> (v);
+  }
+
   /* Points to next command in this list.  */
   struct cmd_list_element *next = nullptr;
 
diff --git a/gdb/cli/cli-setshow.c b/gdb/cli/cli-setshow.c
index 5fd5fd15c6a..b5931fe0e2e 100644
--- a/gdb/cli/cli-setshow.c
+++ b/gdb/cli/cli-setshow.c
@@ -353,11 +353,11 @@ 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)
+	if (c->get_var<var_string> () == nullptr
+	    || strcmp (c->get_var<var_string> (), newobj) != 0)
 	  {
-	    xfree (*(char **) c->var);
-	    *(char **) c->var = newobj;
+	    xfree (c->get_var<var_string> ());
+	    c->set_var<var_string> (newobj);
 
 	    option_changed = 1;
 	  }
@@ -366,10 +366,11 @@ 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)
+      if (c->get_var<var_string_noescape> () == nullptr
+	  || strcmp (c->get_var<var_string_noescape> (), arg) != 0)
 	{
-	  xfree (*(char **) c->var);
-	  *(char **) c->var = xstrdup (arg);
+	  xfree (c->get_var<var_string_noescape> ());
+	  c->set_var<var_string_noescape> (xstrdup (arg));
 
 	  option_changed = 1;
 	}
@@ -398,11 +399,11 @@ 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)
+	if (c->get_var<var_optional_filename> () == nullptr
+	    || strcmp (c->get_var<var_optional_filename> (), val) != 0)
 	  {
-	    xfree (*(char **) c->var);
-	    *(char **) c->var = val;
+	    xfree (c->get_var<var_optional_filename> ());
+	    c->set_var<var_optional_filename> (val);
 
 	    option_changed = 1;
 	  }
@@ -416,9 +417,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->get_var<var_boolean> ())
 	  {
-	    *(bool *) c->var = val;
+	    c->set_var<var_boolean> (val);
 
 	    option_changed = 1;
 	  }
@@ -428,9 +429,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->get_var<var_auto_boolean> () != val)
 	  {
-	    *(enum auto_boolean *) c->var = val;
+	    c->set_var<var_auto_boolean> (val);
 
 	    option_changed = 1;
 	  }
@@ -441,9 +442,9 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
       {
 	unsigned int val = parse_cli_var_uinteger (c->var_type, &arg, true);
 
-	if (*(unsigned int *) c->var != val)
+	if (c->get_var<var_uinteger, var_zuinteger> () != val)
 	  {
-	    *(unsigned int *) c->var = val;
+	    c->set_var<var_uinteger, var_zuinteger> (val);
 
 	    option_changed = 1;
 	  }
@@ -477,9 +478,9 @@ 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 (*(int *) c->var != val)
+	if (c->get_var<var_integer, var_zinteger> () != val)
 	  {
-	    *(int *) c->var = val;
+	    c->set_var<var_integer, var_zinteger> (val);
 
 	    option_changed = 1;
 	  }
@@ -495,9 +496,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->get_var<var_enum> () != match)
 	  {
-	    *(const char **) c->var = match;
+	    c->set_var<var_enum> (match);
 
 	    option_changed = 1;
 	  }
@@ -507,9 +508,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->get_var<var_zuinteger_unlimited> () != val)
 	  {
-	    *(int *) c->var = val;
+	    c->set_var<var_zuinteger_unlimited> (val);
 	    option_changed = 1;
 	  }
       }
@@ -584,18 +585,24 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
 	case var_filename:
 	case var_optional_filename:
 	case var_enum:
-	  gdb::observers::command_param_changed.notify (name, *(char **) c->var);
+	  gdb::observers::command_param_changed.notify
+	    (name,
+	     c->get_var<var_string,
+			var_string_noescape,
+			var_filename,
+			var_optional_filename,
+			var_enum> ());
 	  break;
 	case var_boolean:
 	  {
-	    const char *opt = *(bool *) c->var ? "on" : "off";
+	    const char *opt = c->get_var<var_boolean> () ? "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->get_var<var_auto_boolean> ()];
 
 	    gdb::observers::command_param_changed.notify (name, s);
 	  }
@@ -605,7 +612,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->get_var<var_uinteger, var_zuinteger> ());
 	    gdb::observers::command_param_changed.notify (name, s);
 	  }
 	  break;
@@ -615,7 +622,8 @@ 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->get_var<var_integer, var_zinteger, var_zuinteger_unlimited> ());
 	    gdb::observers::command_param_changed.notify (name, s);
 	  }
 	  break;
@@ -634,21 +642,24 @@ get_setshow_command_value_string (const cmd_list_element *c)
   switch (c->var_type)
     {
     case var_string:
-      if (*(char **) c->var)
-	stb.putstr (*(char **) c->var, '"');
+      if (c->get_var<var_string> () != nullptr)
+	stb.putstr (c->get_var<var_string> (), '"');
       break;
     case var_string_noescape:
     case var_optional_filename:
     case var_filename:
     case var_enum:
-      if (*(char **) c->var)
-	stb.puts (*(char **) c->var);
+      if (c->get_var<var_string_noescape,
+		     var_optional_filename,
+		     var_filename,
+		     var_enum> () != nullptr)
+	stb.puts (c->get_var<var_enum> ());
       break;
     case var_boolean:
-      stb.puts (*(bool *) c->var ? "on" : "off");
+      stb.puts (c->get_var<var_boolean> () ? "on" : "off");
       break;
     case var_auto_boolean:
-      switch (*(enum auto_boolean*) c->var)
+      switch (c->get_var<var_auto_boolean> ())
 	{
 	case AUTO_BOOLEAN_TRUE:
 	  stb.puts ("on");
@@ -667,25 +678,25 @@ get_setshow_command_value_string (const cmd_list_element *c)
     case var_uinteger:
     case var_zuinteger:
       if (c->var_type == var_uinteger
-	  && *(unsigned int *) c->var == UINT_MAX)
+	  && c->get_var<var_uinteger> () == UINT_MAX)
 	stb.puts ("unlimited");
       else
-	stb.printf ("%u", *(unsigned int *) c->var);
+	stb.printf ("%u", c->get_var<var_uinteger, var_zuinteger> ());
       break;
     case var_integer:
     case var_zinteger:
       if (c->var_type == var_integer
-	  && *(int *) c->var == INT_MAX)
+	  && c->get_var<var_integer> () == INT_MAX)
 	stb.puts ("unlimited");
       else
-	stb.printf ("%d", *(int *) c->var);
+	stb.printf ("%d", c->get_var<var_integer, var_zinteger> ());
       break;
     case var_zuinteger_unlimited:
       {
-	if (*(int *) c->var == -1)
+	if (c->get_var<var_zuinteger_unlimited> () == -1)
 	  stb.puts ("unlimited");
 	else
-	  stb.printf ("%d", *(int *) c->var);
+	  stb.printf ("%d", c->get_var<var_zuinteger_unlimited> ());
       }
       break;
     default:
diff --git a/gdb/command.h b/gdb/command.h
index 711cbdcf43e..8fc36e8eb5e 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -125,6 +125,153 @@ typedef enum var_types
   }
 var_types;
 
+/* Helper classes used to associate a storage type for each possible
+   var_type. */
+
+template<var_types T>
+struct var_types_storage;
+
+template<>
+struct var_types_storage<var_boolean>
+{
+  using type = bool;
+};
+
+template<>
+struct var_types_storage<var_auto_boolean>
+{
+  using type = enum auto_boolean;
+};
+
+template<>
+struct var_types_storage<var_uinteger>
+{
+  using type = unsigned int;
+};
+
+template<>
+struct var_types_storage<var_integer>
+{
+  using type = int;
+};
+
+template<>
+struct var_types_storage<var_string>
+{
+  using type = char *;
+};
+
+template<>
+struct var_types_storage<var_string_noescape>
+{
+  using type = char *;
+};
+
+template<>
+struct var_types_storage<var_optional_filename>
+{
+  using type = char *;
+};
+
+template<>
+struct var_types_storage<var_filename>
+{
+  using type = char *;
+};
+
+template<>
+struct var_types_storage<var_zinteger>
+{
+  using type = int;
+};
+
+template<>
+struct var_types_storage<var_zuinteger>
+{
+  using type = unsigned int;
+};
+
+template<>
+struct var_types_storage<var_zuinteger_unlimited>
+{
+  using type = int;
+};
+
+template<>
+struct var_types_storage<var_enum>
+{
+  using type = const char *;
+};
+
+/* Helper class used to check if multiple var_types are represented
+   using the same underlying type.  This class is meant to be instantiated
+   using any number of var_types, and will be used to assess common properties
+   of the underlying storage type.
+
+   Each template instantiating will define the following static members:
+	- all_same: True if and only if all the var_types are stored on the same
+	underlying storage type.
+	- covers_type (var_types t): True if and only if the parameter T is one
+	the templates parameter.
+	- type: Type alias of the underlying type if all_same is true, unspecified
+	otherwise.
+  */
+
+template<var_types... Ts>
+struct var_types_storage_helper;
+
+/* Specialization of var_types_storage_helper when instantiated with only 1
+   template parameter.  */
+template<var_types T>
+struct var_types_storage_helper<T>
+{
+  static constexpr bool all_same = true;
+
+  using type = typename var_types_storage<T>::type;
+
+  static constexpr bool covers_type (var_types t)
+    {
+      return t == T;
+    }
+};
+
+/* Specialization of var_types_storage_helper when instantiated with exactly
+   2 template parameters.  */
+template<var_types T, var_types U>
+struct var_types_storage_helper<T, U>
+{
+  static constexpr bool all_same
+    = std::is_same<typename var_types_storage<T>::type,
+		   typename var_types_storage<U>::type>::value;
+
+  using type = typename var_types_storage<T>::type;
+
+  static constexpr bool covers_type (var_types t)
+  {
+    return var_types_storage_helper<T>::covers_type (t)
+      || var_types_storage_helper<U>::covers_type (t);
+  }
+};
+
+/* Specialization of var_types_storage_helper when instantiated with 3 or more
+   template parameters.  */
+template<var_types T, var_types U, var_types... Us>
+struct var_types_storage_helper<T, U, Us...>
+{
+  static constexpr bool all_same
+    = var_types_storage_helper<T, U>::all_same
+    && var_types_storage_helper<T, Us...>::all_same;
+
+  using type = typename var_types_storage<T>::type;
+
+  static constexpr bool covers_type (var_types t)
+    {
+      return var_types_storage_helper<T>::covers_type (t)
+	|| var_types_storage_helper<U, Us...>::covers_type (t);
+    }
+};
+
+
 /* This structure records one command'd definition.  */
 struct cmd_list_element;
 
-- 
2.30.2

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

* Re: [PATCH 15/16] gdb: make cmd_list_element var an optional union
  2021-07-14  4:55 ` [PATCH 15/16] gdb: make cmd_list_element var an optional union Simon Marchi
  2021-07-14 12:08   ` Lancelot SIX
@ 2021-07-18 15:44   ` Lancelot SIX
  2021-07-19 14:19     ` Simon Marchi
  2021-07-28 19:47   ` Tom Tromey
  2 siblings, 1 reply; 50+ messages in thread
From: Lancelot SIX @ 2021-07-18 15:44 UTC (permalink / raw)
  To: Simon Marchi; +Cc: gdb-patches

> A note on the Guile situation: something that makes our life a little
> bit complicated is that is possible to assign and read the value of a
> Guile-created parameter that is not yet registered (see previous patch
> that adds a test for this).  It would have been much more simple to say
> that this is simply not supported anymore, but that could break existing
> scripts, so I don't think it is a good idea.  In some cases, for example
> when printing the value of a built-in parameter, we have access to a
> show command and its union setting_variable.  When we have an
> un-registered Guile param, we don't have a show command associated to
> it, but we can pass the parameter's pascm_variable union, stored in
> param_smob.  Because we have these two kinds of incompatible parameters,
> we need two versions of the pascm_param_value function.
> 

Hi,

I believe you can create an intermediate abstraction that works for
both the registered and unregistered settings.  In both cases, you can
use or build a cmd_list_element::setting_variable easily, and then have
only one function that does the conversion to a SCM value logic.

Given how a pascm_value is laid out (i.e. a union), you can create a
cmd_list_element setting_variable referencing it with something like:

const cmd_list_element::setting_variable v { .bool_var = &param_value->boolval };

This solution is not perfect either, and it forces the const qualifier
of the param_value of pascm_param_value to be dropped, but this avoids
code duplication which I think is good for easier maintenance.

What do you think?

I have built the above changes on top of your patch series (up to this
patch) and tested gdb.guile/*.exp with no regression noticed.

Lancelot.

---
diff --git a/gdb/guile/scm-param.c b/gdb/guile/scm-param.c
index 57a8c7ec68a..9dc4a5d910d 100644
--- a/gdb/guile/scm-param.c
+++ b/gdb/guile/scm-param.c
@@ -113,6 +113,8 @@ struct param_smob
   SCM containing_scm;
 };
 
+using setting_variable = cmd_list_element::setting_variable;
+
 static const char param_smob_name[] = "gdb:parameter";
 
 /* The tag Guile knows the param smob by.  */
@@ -133,8 +135,11 @@ 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,
-			      const pascm_variable *var,
+static SCM pascm_param_value (enum var_types type, pascm_variable *var,
+			      int arg_pos, const char *func_name);
+static SCM pascm_param_value (const cmd_list_element *,
+			      int arg_pos, const char *func_name);
+static SCM pascm_param_value (enum var_types type, const setting_variable v,
 			      int arg_pos, const char *func_name);
 \f
 /* Administrivia for parameter smobs.  */
@@ -570,79 +575,11 @@ pascm_param_type_name (enum var_types param_type)
    haven't been registered yet.  */
 
 static SCM
-pascm_param_value (enum var_types type, const pascm_variable *param_value,
+pascm_param_value (enum var_types type, pascm_variable *param_value,
 		   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)
-    {
-    case var_string:
-    case var_string_noescape:
-    case var_optional_filename:
-    case var_filename:
-      {
-	const char *str = param_value->stringval;
-
-	if (str == NULL)
-	  str = "";
-
-	return gdbscm_scm_from_host_string (str, strlen (str));
-      }
-
-    case var_enum:
-      {
-	const char *str = param_value->cstringval;
-
-	if (str == NULL)
-	  str = "";
-	return gdbscm_scm_from_host_string (str, strlen (str));
-      }
-
-    case var_boolean:
-      {
-	if (param_value->boolval)
-	  return SCM_BOOL_T;
-	else
-	  return SCM_BOOL_F;
-      }
-
-    case var_auto_boolean:
-      {
-	enum auto_boolean ab = param_value->autoboolval;
-
-	if (ab == AUTO_BOOLEAN_TRUE)
-	  return SCM_BOOL_T;
-	else if (ab == AUTO_BOOLEAN_FALSE)
-	  return SCM_BOOL_F;
-	else
-	  return auto_keyword;
-      }
-
-    case var_zuinteger_unlimited:
-      if (param_value->intval == -1)
-	return unlimited_keyword;
-      gdb_assert (param_value->intval >= 0);
-      /* Fall through.  */
-    case var_zinteger:
-      return scm_from_int (param_value->intval);
-
-    case var_uinteger:
-      if (param_value->uintval == UINT_MAX)
-	return unlimited_keyword;
-      /* Fall through.  */
-    case var_zuinteger:
-      return scm_from_uint (param_value->uintval);
-
-    default:
-      break;
-    }
-
-  return gdbscm_make_out_of_range_error (func_name, arg_pos,
-					 scm_from_int (type),
-					 _("program error: unhandled type"));
+  setting_variable v { .bool_var = &param_value->boolval };
+  return pascm_param_value (type, v, arg_pos, func_name);
 }
 
 /* Return the value of a gdb parameter as a Scheme value.
@@ -655,33 +592,37 @@ pascm_param_value (enum var_types type, const pascm_variable *param_value,
 static SCM
 pascm_param_value (const cmd_list_element *show_cmd,
 		   int arg_pos, const char *func_name)
+{
+  gdb_assert (show_cmd->type == cmd_types::show_cmd);
+  gdb_assert (show_cmd->var.has_value ());
+  return pascm_param_value(show_cmd->var_type, *show_cmd->var,
+			   arg_pos, func_name);
+}
+
+/* Return the value of a gdb parameter as a Scheme value.
+
+   contains the common code paths to handle a variable wether is has been
+   registered or not. */
+
+static SCM
+pascm_param_value (enum var_types var_type, const setting_variable 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.  */
 
-  gdb_assert (show_cmd->type == cmd_types::show_cmd);
-
-  switch (show_cmd->var_type)
+  switch (var_type)
     {
     case var_string:
     case var_string_noescape:
     case var_optional_filename:
     case var_filename:
-      {
-	const char *str = *show_cmd->var->char_ptr_var;
-
-	if (str == NULL)
-	  str = "";
-
-	return gdbscm_scm_from_host_string (str, strlen (str));
-      }
-
     case var_enum:
       {
-	const char *str = *show_cmd->var->const_char_ptr_var;
+	const char *str = *var.const_char_ptr_var;
 
-	if (str == NULL)
+	if (str == nullptr)
 	  str = "";
 
 	return gdbscm_scm_from_host_string (str, strlen (str));
@@ -689,7 +630,7 @@ pascm_param_value (const cmd_list_element *show_cmd,
 
     case var_boolean:
       {
-	if (*show_cmd->var->bool_var)
+	if (*var.bool_var)
 	  return SCM_BOOL_T;
 	else
 	  return SCM_BOOL_F;
@@ -697,7 +638,7 @@ pascm_param_value (const cmd_list_element *show_cmd,
 
     case var_auto_boolean:
       {
-	enum auto_boolean ab = *show_cmd->var->auto_boolean_var;
+	enum auto_boolean ab = *var.auto_boolean_var;
 
 	if (ab == AUTO_BOOLEAN_TRUE)
 	  return SCM_BOOL_T;
@@ -708,26 +649,26 @@ pascm_param_value (const cmd_list_element *show_cmd,
       }
 
     case var_zuinteger_unlimited:
-      if (*show_cmd->var->int_var == -1)
+      if (*var.int_var == -1)
 	return unlimited_keyword;
-      gdb_assert (*show_cmd->var->int_var >= 0);
+      gdb_assert (*var.int_var >= 0);
       /* Fall through.  */
     case var_zinteger:
-      return scm_from_int (*show_cmd->var->int_var);
+      return scm_from_int (*var.int_var);
 
     case var_uinteger:
-      if (*show_cmd->var->unsigned_int_var == UINT_MAX)
+      if (*var.unsigned_int_var == UINT_MAX)
 	return unlimited_keyword;
       /* Fall through.  */
     case var_zuinteger:
-      return scm_from_uint (*show_cmd->var->unsigned_int_var);
+      return scm_from_uint (*var.unsigned_int_var);
 
     default:
       break;
     }
 
   return gdbscm_make_out_of_range_error (func_name, arg_pos,
-					 scm_from_int (show_cmd->var_type),
+					 scm_from_int (var_type),
 					 _("program error: unhandled type"));
 }
 

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

* Re: [PATCH 15/16] gdb: make cmd_list_element var an optional union
  2021-07-18 15:44   ` Lancelot SIX
@ 2021-07-19 14:19     ` Simon Marchi
  2021-07-19 20:58       ` Lancelot SIX
  0 siblings, 1 reply; 50+ messages in thread
From: Simon Marchi @ 2021-07-19 14:19 UTC (permalink / raw)
  To: Lancelot SIX; +Cc: gdb-patches

> +  setting_variable v { .bool_var = &param_value->boolval };
> +  return pascm_param_value (type, v, arg_pos, func_name);

I thought about doing something like this, my only itch is that
declaration of `setting_variable v`.  It is confusing to see it just
setting the bool_var field, regardless of the actual type.  I understand
it works because the pointer value is the same in any case, but to be
pedantic it should be something like:

  setting_variable v;
  switch (type)
    {
    case var_boolean:
      v.bool_var = &param_value->boolval;
      break;
    case var_auto_boolean:
      v.auto_boolean_var = &param_value->autoboolval;
      break;
    // and so on
    }

It's a bit more verbose, but it's written only once, and it's not like
new var types are added every week.

I would be fine changing my patch to use your approach with that change,
if you are.

Simon

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

* Re: [PATCH 15/16] gdb: make cmd_list_element var an optional union
  2021-07-14 23:22         ` Lancelot SIX
@ 2021-07-19 14:32           ` Simon Marchi
  2021-07-19 19:52             ` Simon Marchi
  0 siblings, 1 reply; 50+ messages in thread
From: Simon Marchi @ 2021-07-19 14:32 UTC (permalink / raw)
  To: Lancelot SIX; +Cc: gdb-patches

On 2021-07-14 7:22 p.m., Lancelot SIX wrote:
> On Wed, Jul 14, 2021 at 03:22:41PM -0400, Simon Marchi wrote:
>>
>> I am certainly for this kind of consistency checks, making sure we
>> access the right data the right way.  This is what I did with struct
>> dynamic_prop, for example.
>>
>> I was thinking of adding accessors too, eventually.  I am not very good
>> with advanced C++ templates though, so what I had in mind was more
>> something like the obvious:
>>
>>   struct cmd_list_element
>>   {
>>     int *var_as_int () // or int &
>>     {
>>       gdb_assert (m_var_type == a
>> 		  || m_var_type == b);
>>       return m_var.int_var;
>>     }
>>
>>     unsigned int *var_as_unsigned_int () // or unsigned int &
>>     {
>>       gdb_assert (m_var_type == c
>> 		  || m_var_type == c);
>>       return m_var.unsigned_int_var;
>>     }
>>
>>     // And so on.
>>   };
>>
>> Your last proposition is a bit better in the sense that you ask "give me
>> the variable location for a var_zuinteger".  With mine, you still have
>> to know that a var_zuinteger uses the unsigned int member.  But at
>> least, there's a gdb_assert to catch mistakes at runtime.
>>
>> Whether it's worth the extra complexity... I can't tell, I would have to
>> see the complete patch.  I would also have to see how that fits with the
>> upcoming fix I have for bug 28085.  In the mean time, does it make your
>> life more difficult if we merge my patch?  I would guess not, since
>> you'll need to modify all the call sites (places that access setting
>> variables) anyway.  You can then decide to keep the union or not in your
>> patch.
>>
>> Simon
> 
> I have not yet finished updating all callsites, but here is a
> (hopefully) working patch if you want to see what it looks like.
> 
> I have not had a look at 28085 yet, so I do not yet if my approach gets
> in the way of fixing it.  For the moment, I do not have something
> similar to the gdb_optional you introduce, but it should not be too
> difficult to have something similar.
> 
> Anyway, to not hold back on my account.  I have not read your series
> carefully yet, but it seems a real improvement from the current
> situation.  If it helps fixing a bug, it is even better.  I can rework
> my patch to work on top of this one (at least if what it brings is
> considered worthwhile).
> 
> Lancelot.

I took a quick look, and that seems reasonable to me.  I don't think it
will conflict (in design) with my upcoming patch.  My upcoming patch
makes it so that some settings don't use the storage that is in
cmd_list_element at all, but instead provide some getter/setter
callback.  This is to fix display of settings where the "source of
truth" is not cmd_list_element::var.

With my patch, the code at various places becomes, conceptually:

  if (cmd_list_element::var is set)
    use cmd_list_element::var
  else
    use the getter/setter

I think it would be nice to make the first case (using
cmd_list_element::var for storage) just a specific case of using the
getter/setter.  That is, when we create a setting that uses
cmd_list_element::var for storage, we install a getter and a setter than
get and set the value in cmd_list_element::var.  But I haven't gotten
there yet.  You'll probably have some good ideas for achieving this :).

But before posting that patch, I'd like to decide how the current patch
series will end up, because that will very much affect how the next
patch will look like.

Simon

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

* Re: [PATCH 15/16] gdb: make cmd_list_element var an optional union
  2021-07-19 14:32           ` Simon Marchi
@ 2021-07-19 19:52             ` Simon Marchi
  2021-07-20 23:03               ` Lancelot SIX
  0 siblings, 1 reply; 50+ messages in thread
From: Simon Marchi @ 2021-07-19 19:52 UTC (permalink / raw)
  To: Lancelot SIX; +Cc: gdb-patches

> I took a quick look, and that seems reasonable to me.  I don't think it
> will conflict (in design) with my upcoming patch.  My upcoming patch
> makes it so that some settings don't use the storage that is in
> cmd_list_element at all, but instead provide some getter/setter
> callback.  This is to fix display of settings where the "source of
> truth" is not cmd_list_element::var.
> 
> With my patch, the code at various places becomes, conceptually:
> 
>   if (cmd_list_element::var is set)
>     use cmd_list_element::var
>   else
>     use the getter/setter
> 
> I think it would be nice to make the first case (using
> cmd_list_element::var for storage) just a specific case of using the
> getter/setter.  That is, when we create a setting that uses
> cmd_list_element::var for storage, we install a getter and a setter than
> get and set the value in cmd_list_element::var.  But I haven't gotten
> there yet.  You'll probably have some good ideas for achieving this :).
> 
> But before posting that patch, I'd like to decide how the current patch
> series will end up, because that will very much affect how the next
> patch will look like.

I realize I wasn't accurate here.  The storage isn't actually in
cmd_list_element::var, cmd_list_element::var only contains pointers to
the storage.  But the rest still stands.

Simon

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

* Re: [PATCH 15/16] gdb: make cmd_list_element var an optional union
  2021-07-19 14:19     ` Simon Marchi
@ 2021-07-19 20:58       ` Lancelot SIX
  0 siblings, 0 replies; 50+ messages in thread
From: Lancelot SIX @ 2021-07-19 20:58 UTC (permalink / raw)
  To: Simon Marchi; +Cc: gdb-patches

On Mon, Jul 19, 2021 at 10:19:21AM -0400, Simon Marchi wrote:
> > +  setting_variable v { .bool_var = &param_value->boolval };
> > +  return pascm_param_value (type, v, arg_pos, func_name);
> 
> I thought about doing something like this, my only itch is that
> declaration of `setting_variable v`.  It is confusing to see it just
> setting the bool_var field, regardless of the actual type.  I understand
> it works because the pointer value is the same in any case, but to be
> pedantic it should be something like:
> 
>   setting_variable v;
>   switch (type)
>     {
>     case var_boolean:
>       v.bool_var = &param_value->boolval;
>       break;
>     case var_auto_boolean:
>       v.auto_boolean_var = &param_value->autoboolval;
>       break;
>     // and so on
>     }
> 
> It's a bit more verbose, but it's written only once, and it's not like
> new var types are added every week.
> 
> I would be fine changing my patch to use your approach with that change,
> if you are.
> 
> Simon

When sending my comment, I was wondering about suggesting just that.  It
is definitely more verbose, but I would not mind that.  I tend to prefer
verbose code over code duplication.  It is too easy to update only one
of the two implementations and have behaviors diverge.

Just out of curiosity, I have checked with Compiler Explorer[1], and
starting with -01, the entire switch goes away (when using
__builtin_unreachable, without it the switch is dropped but a check is
maintained).  This is surely not a hot code path so this does not
changes much, but it always nice to know the compiler can do the clever
thing.

Lancelot.

[1] https://godbolt.org/z/GdqGno6cq

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

* Re: [PATCH 15/16] gdb: make cmd_list_element var an optional union
  2021-07-19 19:52             ` Simon Marchi
@ 2021-07-20 23:03               ` Lancelot SIX
  2021-07-23 19:56                 ` Simon Marchi
  2021-07-28 20:13                 ` Tom Tromey
  0 siblings, 2 replies; 50+ messages in thread
From: Lancelot SIX @ 2021-07-20 23:03 UTC (permalink / raw)
  To: Simon Marchi; +Cc: gdb-patches

On Mon, Jul 19, 2021 at 03:52:30PM -0400, Simon Marchi wrote:
> > I took a quick look, and that seems reasonable to me.  I don't think it
> > will conflict (in design) with my upcoming patch.  My upcoming patch
> > makes it so that some settings don't use the storage that is in
> > cmd_list_element at all, but instead provide some getter/setter
> > callback.  This is to fix display of settings where the "source of
> > truth" is not cmd_list_element::var.
> > 
> > With my patch, the code at various places becomes, conceptually:
> > 
> >   if (cmd_list_element::var is set)
> >     use cmd_list_element::var
> >   else
> >     use the getter/setter
> > 
> > I think it would be nice to make the first case (using
> > cmd_list_element::var for storage) just a specific case of using the
> > getter/setter.  That is, when we create a setting that uses
> > cmd_list_element::var for storage, we install a getter and a setter than
> > get and set the value in cmd_list_element::var.  But I haven't gotten
> > there yet.  You'll probably have some good ideas for achieving this :).
> > 
> > But before posting that patch, I'd like to decide how the current patch
> > series will end up, because that will very much affect how the next
> > patch will look like.
> 
> I realize I wasn't accurate here.  The storage isn't actually in
> cmd_list_element::var, cmd_list_element::var only contains pointers to
> the storage.  But the rest still stands.
> 
> Simon

Hi,

I reworked a bit my previous implementation to see to what extent it can
support what you are looking for.  Short answer: it can (and it is
completely transparent to the caller).

In the previous draft version[1], I introduced a struct that groups
together the var_type and the pointer to the actual data in order to
abstract a setting (i.e. a value with a type that can be SET or SHOWn).
This struct provides getters and setters in order to enforce that we
cast the pointer to the adequate type.  I have modified this data
structure so we can register user-provided callbacks.  If those
functions are provided, the getter and setters will call them to
generate retrieve the value instead of trying to dereference the
pointer.

For the moment, I just made sure that this compiles and I played with
dummy settings in my local tree.  It is not yet finalized nor fully
operational because do_set_command will try to free the pointer returned
by the getter when dealing with char* data.  With user provided
callbacks memory should probably not be managed at that level but by the
getter / setter.  Nothing worrying, you have a commit that plans to
change the char* with std::string, this should solve the issue quite
nicely.

As for the previous version, this adds a significant amount of templated
code that can make the implementation appear quite complex.

I think I’ll wait for your series to land before I finalize this
proposal (this is just a POC for the moment) unless you want to build on
top of this approach to introduce user provided callbacks.

Best,
Lancelot.

[1] https://sourceware.org/pipermail/gdb-patches/2021-July/180991.html

---

From 2217aeca7a963da7128311758fe3bbeddeaf43bd Mon Sep 17 00:00:00 2001
From: Lancelot SIX <lsix@lancelotsix.com>
Date: Wed, 14 Jul 2021 22:30:14 +0000
Subject: [RFC PATCH v2] Add typesafe getter/setter for cmd_list_element.var

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

With this pattern, the user needs to know what the storage type
associated with a given VAR_TYPES tag, and needs to do the proper
casting.

This looks something like:

	if (c->var_type == var_zuinteger)
	  unsigned int v = *(unsigned int*)v->var_type;

With this approach, it is easy to make an error.

This patch aims at addressing the same problem as
https://sourceware.org/pipermail/gdb-patches/2021-July/180920.html but
uses a different approach.  I am happy rebasing my work on top of the
mentioned series if it is considered a good enough improvement (and I am
happy dropping otherwise).

This patch proposes to add an abstraction around the pair of the
var_type and the void* pointer in order to prevent the user having to
handle the cast.  This is achieved by introducing the setting struct,
and a set of templated member functions.  The template parameter is the
VAR_TYPES we want to the access the data for.  The template ensures the
return type of the get method and the parameter type of the set method
matches the data type used to store the actual data.

For example, instantiating the member functions with var_boolean will
yield something similar to:

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

With those new methods, the caller does not need to know the underlying
storage nor does he need to perform the cast manually.  This combined
with the added dynamic checks (gdb_assert) makes this approach way safer
in my opinion.

Given that the processing of some options is common between VAR_TYPES,
the templates can be instantiated with more than one VAR_TYPES.  In such
situation, all the template parameters need to describe options that
share the same underlying storage type.

Here is another example of what an instantiation looks like with 2
parameters:

	int get<var_integer, var_zinteger> ()
	{
	  gdb_assert (this->m_var_type == var_integer
	              || this->m_var_type == var_zinteger);
	  gdb_assert (this->m_var != nullptr);
	  return *static_cast<int *> (this->m_var);
	}
	void set<var_integer, var_zinteger> (int v)
	{
	  gdb_assert (this->m_var_type == var_integer
	              || this->m_var_type == var_zinteger);
	  gdb_assert (this->m_var != nullptr);
	  *static_cast<int *> (this->m_var) = v;
	}

With such abstractions available, the var_type and var members of the
cmd_list_element are replaced with a setting instance.

No user visible change is expected after this patch.

Tested on GNU/Linux x86_64, with no regression noticed.
---
 gdb/auto-load.c              |   2 +-
 gdb/cli/cli-cmds.c           |  56 +++--
 gdb/cli/cli-decode.c         | 115 +++++-----
 gdb/cli/cli-decode.h         |   6 +-
 gdb/cli/cli-setshow.c        | 156 ++++++++-----
 gdb/command.h                | 418 +++++++++++++++++++++++++++++++++++
 gdb/guile/scm-param.c        | 145 +++++++-----
 gdb/maint.c                  |   2 +-
 gdb/python/py-param.c        |  16 +-
 gdb/python/python-internal.h |   2 +-
 gdb/python/python.c          |  35 +--
 gdb/remote.c                 |   2 +-
 12 files changed, 741 insertions(+), 214 deletions(-)

diff --git a/gdb/auto-load.c b/gdb/auto-load.c
index 9cd70f174c3..033c448eff7 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 56ae12a0c19..3365a78860c 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -232,7 +232,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)
     error (_("Cannot use this setting with the \"with\" command"));
 
   std::string temp_value
@@ -2090,29 +2090,29 @@ setting_cmd (const char *fnname, struct cmd_list_element *showlist,
 static struct value *
 value_from_setting (const cmd_list_element *cmd, struct gdbarch *gdbarch)
 {
-  switch (cmd->var_type)
+  switch (cmd->var.type ())
     {
     case var_integer:
-      if (*(int *) cmd->var == INT_MAX)
+      if (cmd->var.get<var_integer> () == 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);
+				   cmd->var.get<var_integer> ());
     case var_zinteger:
       return value_from_longest (builtin_type (gdbarch)->builtin_int,
-				 *(int *) cmd->var);
+				 cmd->var.get<var_zinteger> ());
     case var_boolean:
       return value_from_longest (builtin_type (gdbarch)->builtin_int,
-				 *(bool *) cmd->var ? 1 : 0);
+				 cmd->var.get<var_boolean> () ? 1 : 0);
     case var_zuinteger_unlimited:
       return value_from_longest (builtin_type (gdbarch)->builtin_int,
-				 *(int *) cmd->var);
+				 cmd->var.get<var_zuinteger_unlimited> ());
     case var_auto_boolean:
       {
 	int val;
 
-	switch (*(enum auto_boolean*) cmd->var)
+	switch (cmd->var.get<var_auto_boolean> ())
 	  {
 	  case AUTO_BOOLEAN_TRUE:
 	    val = 1;
@@ -2130,24 +2130,34 @@ value_from_setting (const cmd_list_element *cmd, struct gdbarch *gdbarch)
 				   val);
       }
     case var_uinteger:
-      if (*(unsigned int *) cmd->var == UINT_MAX)
+      if (cmd->var.get<var_uinteger> () == 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);
+	   cmd->var.get<var_uinteger> ());
     case var_zuinteger:
       return value_from_ulongest (builtin_type (gdbarch)->builtin_unsigned_int,
-				  *(unsigned int *) cmd->var);
+				  cmd->var.get<var_zuinteger> ());
     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);
+      if (cmd->var)
+	{
+	  const char * var;
+	  if (cmd->var.type () == var_enum)
+	    var = cmd->var.get<var_enum> ();
+	  else
+	    var = cmd->var.get<var_string,
+			       var_string_noescape,
+			       var_optional_filename,
+			       var_filename> ();
+	  return value_cstring (var, strlen (var),
+				builtin_type (gdbarch)->builtin_char);
+	}
       else
 	return value_cstring ("", 1,
 			      builtin_type (gdbarch)->builtin_char);
@@ -2186,7 +2196,7 @@ gdb_maint_setting_internal_fn (struct gdbarch *gdbarch,
 static struct value *
 str_value_from_setting (const cmd_list_element *cmd, struct gdbarch *gdbarch)
 {
-  switch (cmd->var_type)
+  switch (cmd->var.type ())
     {
     case var_integer:
     case var_zinteger:
@@ -2211,9 +2221,19 @@ str_value_from_setting (const cmd_list_element *cmd, struct gdbarch *gdbarch)
 	 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);
+      if (cmd->var)
+	{
+	  const char *var;
+	  if (cmd->var.type () == var_enum)
+	    var = cmd->var.get<var_enum> ();
+	  else
+	    var = cmd->var.get<var_string,
+			       var_string_noescape,
+			       var_optional_filename,
+			       var_filename> ();
+	  return value_cstring (var, strlen (var),
+				builtin_type (gdbarch)->builtin_char);
+	}
       else
 	return value_cstring ("", 1,
 			      builtin_type (gdbarch)->builtin_char);
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index 633a3ad9309..f15747cf337 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -481,12 +481,12 @@ empty_sfunc (const char *args, int from_tty, struct cmd_list_element *c)
    VAR is address of the variable being controlled by this command.
    DOC is the documentation string.  */
 
+template<var_types T>
 static struct cmd_list_element *
 add_set_or_show_cmd (const char *name,
 		     enum cmd_types type,
 		     enum command_class theclass,
-		     var_types var_type,
-		     void *var,
+		     typename detail::var_types_storage<T>::type *var,
 		     const char *doc,
 		     struct cmd_list_element **list)
 {
@@ -494,8 +494,7 @@ 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.set_p<T> (var);
   /* This needs to be something besides NULL so that this isn't
      treated as a help class.  */
   set_cmd_sfunc (c, empty_sfunc);
@@ -511,10 +510,11 @@ add_set_or_show_cmd (const char *name,
 
    Return the newly created set and show commands.  */
 
+template<var_types T>
 static set_show_commands
 add_setshow_cmd_full (const char *name,
 		      enum command_class theclass,
-		      var_types var_type, void *var,
+		      typename detail::var_types_storage<T>::type *var,
 		      const char *set_doc, const char *show_doc,
 		      const char *help_doc,
 		      cmd_const_sfunc_ftype *set_func,
@@ -537,15 +537,15 @@ 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,
-			     full_set_doc, set_list);
+  set = add_set_or_show_cmd<T> (name, set_cmd, theclass, var,
+				full_set_doc, set_list);
   set->doc_allocated = 1;
 
   if (set_func != NULL)
     set_cmd_sfunc (set, set_func);
 
-  show = add_set_or_show_cmd (name, show_cmd, theclass, var_type, var,
-			      full_show_doc, show_list);
+  show = add_set_or_show_cmd<T> (name, show_cmd, theclass, var,
+				 full_show_doc, show_list);
   show->doc_allocated = 1;
   show->show_value_func = show_func;
   /* Disable the default symbol completer.  Doesn't make much sense
@@ -574,10 +574,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<var_enum> (name, theclass, var,
+				       set_doc, show_doc, help_doc,
+				       set_func, show_func,
+				       set_list, show_list);
   commands.set->enums = enumlist;
   return commands;
 }
@@ -602,10 +602,10 @@ 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<var_auto_boolean> (name, theclass, var,
+					      set_doc, show_doc, help_doc,
+					      set_func, show_func,
+					      set_list, show_list);
 
   commands.set->enums = auto_boolean_enums;
 
@@ -631,10 +631,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<var_boolean> (name, theclass, var,
+					 set_doc, show_doc, help_doc,
+					 set_func, show_func,
+					 set_list, show_list);
 
   commands.set->enums = boolean_enums;
 
@@ -655,10 +655,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<var_filename> (name, theclass, var,
+					  set_doc, show_doc, help_doc,
+					  set_func, show_func,
+					  set_list, show_list);
 
   set_cmd_completer (commands.set, filename_completer);
 
@@ -679,10 +679,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<var_string> (name, theclass, 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,10 +704,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 (name, theclass, var_string_noescape, var,
-			    set_doc, show_doc, help_doc,
-			    set_func, show_func,
-			    set_list, show_list);
+    = add_setshow_cmd_full<var_string_noescape> (name, theclass, 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);
@@ -729,10 +729,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<var_optional_filename> (name, theclass, var,
+						   set_doc, show_doc, help_doc,
+						   set_func, show_func,
+						   set_list, show_list);
 		
   set_cmd_completer (commands.set, filename_completer);
 
@@ -773,10 +773,10 @@ 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<var_integer> (name, theclass, var,
+					 set_doc, show_doc, help_doc,
+					 set_func, show_func,
+					 set_list, show_list);
 
   set_cmd_completer (commands.set, integer_unlimited_completer);
 
@@ -799,10 +799,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<var_uinteger> (name, theclass, var,
+					  set_doc, show_doc, help_doc,
+					  set_func, show_func,
+					  set_list, show_list);
 
   set_cmd_completer (commands.set, integer_unlimited_completer);
 
@@ -824,10 +824,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<var_zinteger> (name, theclass, var,
+					     set_doc, show_doc, help_doc,
+					     set_func, show_func,
+					     set_list, show_list);
 }
 
 set_show_commands
@@ -843,10 +843,11 @@ 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<var_zuinteger_unlimited> (name, theclass, var,
+						     set_doc, show_doc,
+						     help_doc, set_func,
+						     show_func, set_list,
+						     show_list);
 
   set_cmd_completer (commands.set, integer_unlimited_completer);
 
@@ -868,10 +869,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<var_zuinteger> (name, theclass, 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 241535ae5b5..6799f965371 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
@@ -230,7 +226,7 @@ struct cmd_list_element
 
   /* Pointer to variable affected by "set" and "show".  Doesn't
      matter if type is not_set.  */
-  void *var = nullptr;
+  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 5fd5fd15c6a..883ac30598e 100644
--- a/gdb/cli/cli-setshow.c
+++ b/gdb/cli/cli-setshow.c
@@ -133,7 +133,7 @@ deprecated_show_value_hack (struct ui_file *ignore_file,
   /* 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)
+  switch (c->var.type ())
     {
     case var_string:
     case var_string_noescape:
@@ -312,7 +312,7 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
   if (arg == NULL)
     arg = "";
 
-  switch (c->var_type)
+  switch (c->var.type ())
     {
     case var_string:
       {
@@ -353,11 +353,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)
+        auto const var = c->var.get<var_string> ();
+	if (var == nullptr
+	    || strcmp (var, newobj) != 0)
 	  {
-	    xfree (*(char **) c->var);
-	    *(char **) c->var = newobj;
+	    xfree (var);
+	    c->var.set<var_string> (newobj);
 
 	    option_changed = 1;
 	  }
@@ -366,13 +367,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);
+      {
+	auto const var = c->var.get<var_string_noescape> ();
+	if (var == nullptr
+	    || strcmp (var, arg) != 0)
+	  {
+	    xfree (var);
+	    c->var.set<var_string_noescape> (xstrdup (arg));
 
-	  option_changed = 1;
-	}
+	    option_changed = 1;
+	  }
+      }
       break;
     case var_filename:
       if (*arg == '\0')
@@ -381,6 +386,7 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
     case var_optional_filename:
       {
 	char *val = NULL;
+	auto const var = c->var.get<var_filename, var_optional_filename> ();
 
 	if (*arg != '\0')
 	  {
@@ -398,11 +404,11 @@ 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)
+	if (var == nullptr
+	    || strcmp (var, val) != 0)
 	  {
-	    xfree (*(char **) c->var);
-	    *(char **) c->var = val;
+	    xfree (val);
+	    c->var.set<var_filename, var_optional_filename> (val);
 
 	    option_changed = 1;
 	  }
@@ -416,9 +422,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<var_boolean> ())
 	  {
-	    *(bool *) c->var = val;
+	    c->var.set<var_boolean> (val);
 
 	    option_changed = 1;
 	  }
@@ -428,9 +434,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<var_auto_boolean> () != val)
 	  {
-	    *(enum auto_boolean *) c->var = val;
+	    c->var.set<var_auto_boolean> (val);
 
 	    option_changed = 1;
 	  }
@@ -439,11 +445,11 @@ 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<var_uinteger, var_zuinteger> () != val)
 	  {
-	    *(unsigned int *) c->var = val;
+	    c->var.set<var_uinteger, var_zuinteger> (val);
 
 	    option_changed = 1;
 	  }
@@ -456,35 +462,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<var_integer, var_zinteger> () != val)
 	  {
-	    *(int *) c->var = val;
+	    c->var.set<var_integer, var_zinteger> (val);
 
 	    option_changed = 1;
 	  }
-	break;
       }
+      break;
     case var_enum:
       {
 	const char *end_arg = arg;
@@ -495,9 +501,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<var_enum> () != match)
 	  {
-	    *(const char **) c->var = match;
+	    c->var.set<var_enum> (match);
 
 	    option_changed = 1;
 	  }
@@ -507,9 +513,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<var_zuinteger_unlimited> () != val)
 	  {
-	    *(int *) c->var = val;
+	    c->var.set<var_zuinteger_unlimited> (val);
 	    option_changed = 1;
 	  }
       }
@@ -577,25 +583,35 @@ 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<var_enum> ();
+	    else
+	      var = c->var.get<var_string,
+			       var_string_noescape,
+			       var_filename,
+			       var_optional_filename> ();
+	    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<var_boolean> () ? "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<var_auto_boolean> ()];
 
 	    gdb::observers::command_param_changed.notify (name, s);
 	  }
@@ -605,7 +621,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<var_uinteger, var_zuinteger> ());
 	    gdb::observers::command_param_changed.notify (name, s);
 	  }
 	  break;
@@ -615,7 +631,8 @@ 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<var_integer, var_zinteger, var_zuinteger_unlimited> ());
 	    gdb::observers::command_param_changed.notify (name, s);
 	  }
 	  break;
@@ -631,24 +648,36 @@ get_setshow_command_value_string (const cmd_list_element *c)
 {
   string_file stb;
 
-  switch (c->var_type)
+  switch (c->var.type ())
     {
     case var_string:
-      if (*(char **) c->var)
-	stb.putstr (*(char **) c->var, '"');
+      {
+	auto const var = c->var.get<var_string> ();
+	if (var != nullptr)
+	  stb.putstr (var, '"');
+      }
       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 *var;
+	if (c->var.type () == var_enum)
+	  var = c->var.get<var_enum> ();
+	else
+	  var = c->var.get<var_string_noescape,
+			   var_optional_filename,
+			   var_filename> ();
+	if (var != nullptr)
+	  stb.puts (var);
+      }
       break;
     case var_boolean:
-      stb.puts (*(bool *) c->var ? "on" : "off");
+      stb.puts (c->var.get<var_boolean> () ? "on" : "off");
       break;
     case var_auto_boolean:
-      switch (*(enum auto_boolean*) c->var)
+      switch (c->var.get<var_auto_boolean> ())
 	{
 	case AUTO_BOOLEAN_TRUE:
 	  stb.puts ("on");
@@ -666,26 +695,33 @@ 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);
+      {
+	auto const var = c->var.get<var_uinteger, var_zuinteger> ();
+	if (c->var.type () == var_uinteger
+	    && var == UINT_MAX)
+	  stb.puts ("unlimited");
+	else
+	  stb.printf ("%u", var);
+      }
       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);
+      {
+	const auto var = c->var.get<var_integer, var_zinteger> ();
+	if (c->var.type () == var_integer
+	    && var == INT_MAX)
+	  stb.puts ("unlimited");
+	else
+	  stb.printf ("%d", var);
+      }
       break;
     case var_zuinteger_unlimited:
       {
-	if (*(int *) c->var == -1)
+	auto const var = c->var.get<var_zuinteger_unlimited> ();
+	if (var == -1)
 	  stb.puts ("unlimited");
 	else
-	  stb.printf ("%d", *(int *) c->var);
+	  stb.printf ("%d", var);
       }
       break;
     default:
diff --git a/gdb/command.h b/gdb/command.h
index 711cbdcf43e..489ca9b1b65 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -125,6 +125,424 @@ typedef enum var_types
   }
 var_types;
 
+
+template<typename T>
+struct accessor_sigs
+{
+  using getter = T (*) ();
+  using setter = void (*) (T);
+};
+
+
+union setting_getter
+{
+  typename accessor_sigs<bool>::getter get_bool;
+  typename accessor_sigs<int>::getter get_int;
+  typename accessor_sigs<unsigned int>::getter get_uint;
+  typename accessor_sigs<auto_boolean>::getter get_auto_boolean;
+  typename accessor_sigs<char *>::getter get_char_p;
+  typename accessor_sigs<const char *>::getter get_const_char_p;
+};
+
+union setting_setter
+{
+  typename accessor_sigs<bool>::setter set_bool;
+  typename accessor_sigs<int>::setter set_int;
+  typename accessor_sigs<unsigned int>::setter set_uint;
+  typename accessor_sigs<auto_boolean>::setter set_auto_boolean;
+  typename accessor_sigs<char *>::setter set_char_p;
+  typename accessor_sigs<const char *>::setter set_const_char_p;
+};
+
+namespace detail
+{
+  template<typename>
+  struct accessor_helper;
+
+  template<>
+  struct accessor_helper<bool>
+  {
+    static accessor_sigs<bool>::getter &getter(setting_getter & getters)
+    {
+      return getters.get_bool;
+    }
+
+    static accessor_sigs<bool>::setter &setter(setting_setter & setters)
+    {
+      return setters.set_bool;
+    }
+  };
+
+  template<>
+  struct accessor_helper<int>
+  {
+    static accessor_sigs<int>::getter &getter(setting_getter & getters)
+    {
+      return getters.get_int;
+    }
+
+    static accessor_sigs<int>::setter &setter(setting_setter & setters)
+    {
+      return setters.set_int;
+    }
+  };
+
+  template<>
+  struct accessor_helper<unsigned int>
+  {
+    static accessor_sigs<unsigned int>::getter &getter(setting_getter & getters)
+    {
+      return getters.get_uint;
+    }
+
+    static accessor_sigs<unsigned int>::setter &setter(setting_setter & setters)
+    {
+      return setters.set_uint;
+    }
+  };
+
+  template<>
+  struct accessor_helper<auto_boolean>
+  {
+    static accessor_sigs<auto_boolean>::getter &getter(setting_getter & getters)
+    {
+      return getters.get_auto_boolean;
+    }
+
+    static accessor_sigs<auto_boolean>::setter &setter(setting_setter & setters)
+    {
+      return setters.set_auto_boolean;
+    }
+  };
+
+  template<>
+  struct accessor_helper<char *>
+  {
+    static accessor_sigs<char *>::getter &getter(setting_getter & getters)
+    {
+      return getters.get_char_p;
+    }
+
+    static accessor_sigs<char *>::setter &setter(setting_setter & setters)
+    {
+      return setters.set_char_p;
+    }
+  };
+
+  template<>
+  struct accessor_helper<const char *>
+  {
+    static accessor_sigs<const char *>::getter &getter(setting_getter & getters)
+    {
+      return getters.get_const_char_p;
+    }
+
+    static accessor_sigs<const char *>::setter &setter(setting_setter & setters)
+    {
+      return setters.set_const_char_p;
+    }
+  };
+
+  /* Helper classes used to associate a storage type for each possible
+     var_type. */
+
+  template<var_types T>
+  struct var_types_storage;
+
+  template<>
+  struct var_types_storage<var_boolean>
+  {
+    using type = bool;
+
+  };
+
+  template<>
+  struct var_types_storage<var_auto_boolean>
+  {
+    using type = enum auto_boolean;
+  };
+
+  template<>
+  struct var_types_storage<var_uinteger>
+  {
+    using type = unsigned int;
+  };
+
+  template<>
+  struct var_types_storage<var_integer>
+  {
+    using type = int;
+  };
+
+  template<>
+  struct var_types_storage<var_string>
+  {
+    using type = char *;
+  };
+
+  template<>
+  struct var_types_storage<var_string_noescape>
+  {
+    using type = char *;
+  };
+
+  template<>
+  struct var_types_storage<var_optional_filename>
+  {
+    using type = char *;
+  };
+
+  template<>
+  struct var_types_storage<var_filename>
+  {
+    using type = char *;
+  };
+
+  template<>
+  struct var_types_storage<var_zinteger>
+  {
+    using type = int;
+  };
+
+  template<>
+  struct var_types_storage<var_zuinteger>
+  {
+    using type = unsigned int;
+  };
+
+  template<>
+  struct var_types_storage<var_zuinteger_unlimited>
+  {
+    using type = int;
+  };
+
+  template<>
+  struct var_types_storage<var_enum>
+  {
+    using type = const char *;
+  };
+
+  /* Helper class used to check if multiple var_types are represented
+     using the same underlying type.  This class is meant to be instantiated
+     using any number of var_types, and will be used to assess common properties
+     of the underlying storage type.
+
+     Each template instantiating will define the following static members:
+    - value: True if and only if all the var_types are stored on the same
+    underlying storage type.
+    - covers_type (var_types t): True if and only if the parameter T is one
+    the templates parameter.
+    - type: Type alias of the underlying type if value is true, unspecified
+    otherwise.
+    */
+
+  template<var_types... Ts>
+  struct var_types_have_same_storage;
+
+  /* Specialization of var_types_have_same_storage when instantiated with only 1
+     template parameter.  */
+  template<var_types T>
+  struct var_types_have_same_storage<T>
+  {
+    static constexpr bool value = true;
+
+    using type = typename var_types_storage<T>::type;
+
+    static constexpr bool covers_type (var_types t)
+    {
+      return t == T;
+    }
+  };
+
+  /* Specialization of var_types_have_same_storage when instantiated with exactly
+     2 template parameters.  */
+  template<var_types T, var_types U>
+  struct var_types_have_same_storage<T, U>
+  {
+    static constexpr bool value
+      = std::is_same<typename var_types_storage<T>::type,
+      typename var_types_storage<U>::type>::value;
+
+    using type = typename var_types_storage<T>::type;
+
+    static constexpr bool covers_type (var_types t)
+    {
+      return var_types_have_same_storage<T>::covers_type (t)
+	|| var_types_have_same_storage<U>::covers_type (t);
+    }
+  };
+
+  /* Specialization of var_types_have_same_storage when instantiated with 3 or more
+     template parameters.  */
+  template<var_types T, var_types U, var_types... Us>
+  struct var_types_have_same_storage<T, U, Us...>
+  {
+    static constexpr bool value
+      = var_types_have_same_storage<T, U>::value
+      && var_types_have_same_storage<T, Us...>::value;
+
+    using type = typename var_types_storage<T>::type;
+
+    static constexpr bool covers_type (var_types t)
+      {
+	return var_types_have_same_storage<T>::covers_type (t)
+	  || var_types_have_same_storage<U, Us...>::covers_type (t);
+      }
+  };
+} /* namespace detail */
+
+/* Abstraction that contains access to data that can be set or shown.
+
+   The underlying data can be of an VAR_TYPES type.  */
+struct base_setting_wrapper
+{
+  /* Access the type of the current var.  */
+  var_types type () const
+  {
+    return m_var_type;
+  }
+
+  /* Return the current value (by pointer).
+
+     The expected template parameter is the VAR_TYPES of the current instance.
+     This is enforced with a runtime check.
+
+     If multiple template parameters are given, check that the underlying
+     pointer type associated with each parameter are the same.  */
+  template<var_types... Ts,
+	   typename = gdb::Requires<detail::var_types_have_same_storage<Ts...>>>
+  typename detail::var_types_have_same_storage<Ts...>::type const *get_p() const
+  {
+    gdb_assert (detail::var_types_have_same_storage<Ts...>::covers_type
+                (this->m_var_type));
+    gdb_assert (!empty ());
+    // TODO
+    //gdb_assert (m_getter == nullptr && m_setter == nullptr);
+
+    return static_cast<
+      typename detail::var_types_have_same_storage<Ts...>::type const *>
+      (this->m_var);
+  }
+
+  /* Return the current value.
+
+     See get_p for discussion on the return type.  */
+  template<var_types... Ts>
+  typename detail::var_types_have_same_storage<Ts...>::type get() const
+  {
+    gdb_assert (detail::var_types_have_same_storage<Ts...>::covers_type
+                (this->m_var_type));
+    auto getter = detail::accessor_helper<
+      typename detail::var_types_have_same_storage<Ts...>::type>::getter
+        (const_cast<base_setting_wrapper *> (this)->m_getter);
+
+    if (getter != nullptr)
+      return (*getter) ();
+    else
+      {
+        gdb_assert (!empty ());
+        return *get_p<Ts...> ();
+      }
+  }
+
+  /* Sets the value V to the underlying buffer.  If we have a user-provided
+     setter, use it to set the setting, otherwise set it to the internally
+     referenced buffer.
+
+     If one template argument is given, it must be the VAR_TYPE of the current
+     instance.  This is enforced at runtime.
+
+     If multiple template parameters are given, they must all share the same
+     underlying storage type (this is checked at compile time), and THIS must
+     be of the type of one of the template parameters (this is checked at
+     runtime).  */
+  template<var_types... Ts,
+           typename = gdb::Requires<
+             detail::var_types_have_same_storage<Ts...>>>
+  void set(typename detail::var_types_have_same_storage<Ts...>::type v)
+  {
+    /* Check that the current instance is of one of the supported types for
+       this instantiation.  */
+    gdb_assert (detail::var_types_have_same_storage<Ts...>::covers_type
+                (this->m_var_type));
+
+    auto setter = detail::accessor_helper<
+        typename detail::var_types_have_same_storage<Ts...>::type>::setter (this->m_setter);
+
+    if (setter != nullptr)
+      {
+        (*setter) (v);
+      }
+    else
+      {
+        gdb_assert (!empty ());
+        *static_cast<typename detail::var_types_have_same_storage<Ts...>::type *>
+          (this->m_var) = v;
+      }
+  }
+
+  template<var_types T>
+  void set_accessors(typename accessor_sigs<typename detail::var_types_storage<T>::type>::setter setter,
+                     typename accessor_sigs<typename detail::var_types_storage<T>::type>::getter getter)
+  {
+    m_var_type = T;
+    detail::accessor_helper<typename detail::var_types_storage<T>::type>::setter (this->m_setter) = setter;
+    detail::accessor_helper<typename detail::var_types_storage<T>::type>::getter (this->m_getter) = getter;
+  }
+
+  /* A setting is valid (can be evaluated to true) if it contains user provided
+     getter and setter or has a pointer set to a setting.  */
+  explicit operator bool() const
+  {
+    // TODO refactor that to not rely on one of the options
+    return (m_getter.get_bool != nullptr && m_setter.set_bool != nullptr) || !this->empty();
+  }
+
+protected:
+  /* The type of the variable M_VAR is pointing to.  If M_VAR is nullptr,
+     M_VAR_TYPE is ignored.  */
+  var_types m_var_type { var_boolean };
+
+  /* Pointer to the enclosed variable.  The type of the variable is encoded
+     in M_VAR_TYPE.  Can be nullptr.  */
+  void *m_var { nullptr };
+
+  /* Pointer to a user provided getter.  */
+  union setting_getter m_getter { .get_bool { nullptr } };
+
+  /* Pointer to a user provided setter.  */
+  union setting_setter m_setter { .set_bool { nullptr } };
+
+  /* Indicates if the current instance has a underlying buffer.  */
+  bool empty () const
+  {
+    return m_var == nullptr;;
+  }
+
+};
+
+
+/* A augmented version of base_setting_wrapper with additional methods to set the
+   underlying buffer and declare the var_type.  */
+struct setting final: base_setting_wrapper
+{
+  /*  Set the type of the current variable.  */
+  void set_type (var_types type)
+  {
+    gdb_assert (empty ());
+    this->m_var_type = type;
+  }
+
+  /* Update the pointer to the underlying variable referenced by this
+     instance.  */
+  template<var_types T>
+  void set_p (typename detail::var_types_storage<T>::type *v)
+  {
+    this->set_type (T);
+    this->m_var = static_cast<void *> (v);
+  }
+};
+
 /* 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 c052d04974a..83fb3a27072 100644
--- a/gdb/guile/scm-param.c
+++ b/gdb/guile/scm-param.c
@@ -113,6 +113,19 @@ struct param_smob
   SCM containing_scm;
 };
 
+/* Wraps a base_setting_wrapper around an existing param_smob.  This abstraction
+   is used
+   to manipulate the value in S->VALUE in a type safe manner the same way as if
+   it was referenced by a setting.  */
+struct setting_wrapper final: base_setting_wrapper
+{
+  explicit setting_wrapper (param_smob *s)
+  {
+    m_var_type = s->type;
+    m_var = &s->value;
+  }
+};
+
 static const char param_smob_name[] = "gdb:parameter";
 
 /* The tag Guile knows the param smob by.  */
@@ -133,8 +146,7 @@ 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 (base_setting_wrapper const &c, int arg_pos, const char *func_name);
 \f
 /* Administrivia for parameter smobs.  */
 
@@ -151,10 +163,9 @@ pascm_print_param_smob (SCM self, SCM port, scm_print_state *pstate)
   if (! pascm_is_valid (p_smob))
     scm_puts (" {invalid}", port);
 
-  gdbscm_printf (port, " %s ", pascm_param_type_name (p_smob->type));
+  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 (setting_wrapper (p_smob), GDBSCM_ARG_NONE, NULL);
   scm_display (value, port);
 
   scm_puts (">", port);
@@ -444,7 +455,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];
+      setting_wrapper (self).set<var_enum> (self->enumeration[0]);
       break;
 
     default:
@@ -566,14 +577,13 @@ pascm_param_type_name (enum var_types param_type)
    If TYPE 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 (base_setting_wrapper const &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 +591,23 @@ 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<var_enum> ();
+	else
+	  str = var.get<var_string,
+			var_string_noescape,
+			var_optional_filename,
+			var_filename> ();
 
-	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<var_boolean> ())
 	  return SCM_BOOL_T;
 	else
 	  return SCM_BOOL_F;
@@ -598,7 +615,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<var_auto_boolean> ();
 
 	if (ab == AUTO_BOOLEAN_TRUE)
 	  return SCM_BOOL_T;
@@ -609,67 +626,84 @@ pascm_param_value (enum var_types type, void *var,
       }
 
     case var_zuinteger_unlimited:
-      if (* (int *) var == -1)
+      if (var.get<var_zuinteger_unlimited> () == -1)
 	return unlimited_keyword;
-      gdb_assert (* (int *) var >= 0);
+      gdb_assert (var.get<var_zuinteger_unlimited> () >= 0);
       /* Fall through.  */
     case var_zinteger:
-      return scm_from_int (* (int *) var);
+      return scm_from_int (var.get<var_zuinteger_unlimited, var_zinteger> ());
 
     case var_uinteger:
-      if (* (unsigned int *) var == UINT_MAX)
+      if (var.get<var_uinteger> ()== UINT_MAX)
 	return unlimited_keyword;
       /* Fall through.  */
     case var_zuinteger:
-      return scm_from_uint (* (unsigned int *) var);
+      return scm_from_uint (var.get<var_uinteger, var_zuinteger> ());
 
     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_wrapper var { 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<var_string,
+			 var_string_noescape,
+			 var_optional_filename,
+			 var_filename> ());
+	  if (var.type () == var_optional_filename)
+	    var.set<var_string,
+		    var_string_noescape,
+		    var_optional_filename,
+		    var_filename> (xstrdup (""));
 	  else
-	    var->stringval = NULL;
+	    var.set<var_string,
+		    var_string_noescape,
+		    var_optional_filename,
+		    var_filename> (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<var_string,
+			  var_string_noescape,
+			  var_optional_filename,
+			  var_filename> ());
+	  var.set<var_string,
+		   var_string_noescape,
+		   var_optional_filename,
+		   var_filename> (string.release ());
 	}
       break;
 
@@ -681,27 +715,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<var_enum> (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<var_boolean> (gdbscm_is_true (value));
       break;
 
     case var_auto_boolean:
@@ -710,19 +744,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<var_auto_boolean> (AUTO_BOOLEAN_AUTO);
       else if (gdbscm_is_true (value))
-	var->autoboolval = AUTO_BOOLEAN_TRUE;
+	var.set<var_auto_boolean> (AUTO_BOOLEAN_TRUE);
       else
-	var->autoboolval = AUTO_BOOLEAN_FALSE;
+	var.set<var_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 +764,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<var_integer, var_zuinteger_unlimited> (UINT_MAX);
 	      else
-		var->intval = -1;
+		var.set<var_integer, var_zuinteger_unlimited> (-1);
 	      break;
 	    }
 	}
@@ -743,25 +777,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<var_uinteger, var_zuinteger> (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<var_zinteger, var_zuinteger_unlimited> (i);
 	}
       break;
 
@@ -934,7 +968,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 +1064,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 (setting_wrapper (p_smob), SCM_ARG1, FUNC_NAME);
     }
   else
     {
@@ -1062,13 +1095,13 @@ 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)
 	{
 	  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 +1113,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 4637fcbc86c..58a54c22940 100644
--- a/gdb/maint.c
+++ b/gdb/maint.c
@@ -1088,7 +1088,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..f2d251a19ea 100644
--- a/gdb/python/py-param.c
+++ b/gdb/python/py-param.c
@@ -88,6 +88,19 @@ struct parmpy_object
   const char **enumeration;
 };
 
+/* Wraps a base_setting_wrapper around an existing param_smob.  This abstraction
+   is used
+   to manipulate the value in S->VALUE in a type safe manner the same way as if
+   it was referenced by a setting.  */
+struct setting_wrapper final: base_setting_wrapper
+{
+  explicit setting_wrapper (parmpy_object *s)
+  {
+    m_var_type = s->type;
+    m_var = &s->value;
+  }
+};
+
 extern PyTypeObject parmpy_object_type
     CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("parmpy_object");
 
@@ -110,7 +123,8 @@ get_attr (PyObject *obj, PyObject *attr_name)
     {
       parmpy_object *self = (parmpy_object *) obj;
 
-      return gdbpy_parameter_value (self->type, &self->value);
+      setting_wrapper wrapper { self };
+      return gdbpy_parameter_value (wrapper);
     }
 
   return PyObject_GenericGetAttr (obj, attr_name);
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 690d2fb43c0..6b6ed009091 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 (base_setting_wrapper const & 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 e42cbc4fd5e..3b2210b5cc5 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -448,9 +448,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 (base_setting_wrapper const & var)
 {
-  switch (type)
+  switch (var.type ())
     {
     case var_string:
     case var_string_noescape:
@@ -458,16 +458,23 @@ gdbpy_parameter_value (enum var_types type, void *var)
     case var_filename:
     case var_enum:
       {
-	const char *str = *(char **) var;
-
-	if (! str)
+	const char *str;
+        if (var.type () == var_enum)
+          str = var.get<var_enum> ();
+        else
+          str = var.get<var_string,
+			var_string_noescape,
+			var_optional_filename,
+			var_filename> ();
+
+	if (str == nullptr)
 	  str = "";
 	return host_string_to_python_string (str).release ();
       }
 
     case var_boolean:
       {
-	if (* (bool *) var)
+	if (var.get<var_boolean> ())
 	  Py_RETURN_TRUE;
 	else
 	  Py_RETURN_FALSE;
@@ -475,7 +482,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<var_auto_boolean> ();
 
 	if (ab == AUTO_BOOLEAN_TRUE)
 	  Py_RETURN_TRUE;
@@ -486,16 +493,18 @@ gdbpy_parameter_value (enum var_types type, void *var)
       }
 
     case var_integer:
-      if ((* (int *) var) == INT_MAX)
+      if (var.get<var_integer> () == 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<var_integer, var_zinteger,
+		 var_zuinteger_unlimited> ()).release ();
 
     case var_uinteger:
       {
-	unsigned int val = * (unsigned int *) var;
+	unsigned int val = var.get<var_uinteger> ();
 
 	if (val == UINT_MAX)
 	  Py_RETURN_NONE;
@@ -504,7 +513,7 @@ gdbpy_parameter_value (enum var_types type, void *var)
 
     case var_zuinteger:
       {
-	unsigned int val = * (unsigned int *) var;
+	unsigned int val = var.get<var_zuinteger> ();
 	return gdb_py_object_from_ulongest (val).release ();
       }
     }
@@ -541,10 +550,10 @@ gdbpy_parameter (PyObject *self, PyObject *args)
     return PyErr_Format (PyExc_RuntimeError,
 			 _("Could not find parameter `%s'."), arg);
 
-  if (! cmd->var)
+  if (!cmd->var)
     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 adc53e324d0..f8cabe8ec87 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -2243,7 +2243,7 @@ show_remote_protocol_packet_cmd (struct ui_file *file, int from_tty,
        packet < &remote_protocol_packets[PACKET_MAX];
        packet++)
     {
-      if (&packet->detect == c->var)
+      if (&packet->detect == c->var.get_p<var_auto_boolean> ())
 	{
 	  show_packet_config_cmd (packet);
 	  return;
-- 
2.31.1


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

* Re: [PATCH 14/16] gdb/testsuite: test get/set value of unregistered Guile parameter
  2021-07-14  4:55 ` [PATCH 14/16] gdb/testsuite: test get/set value of unregistered Guile parameter Simon Marchi
@ 2021-07-23 19:42   ` Simon Marchi
  0 siblings, 0 replies; 50+ messages in thread
From: Simon Marchi @ 2021-07-23 19:42 UTC (permalink / raw)
  To: gdb-patches



On 2021-07-14 12:55 a.m., Simon Marchi wrote:
> When creating a parameter in Guile, you have to create it using
> make-parameter and then register it with GDB with register-parameter!.
> In between, it's still possible (though not documented) to set the
> parameter's value.  I broke this use case by mistake while writing this
> series, so thought it would be good to have a test for it.
> 
> I suppose that people could use this "feature" to give their parameter
> an initial value, even though make-parameter has an initial-value
> parameter for this.  Nevertheless, changing this behavior could break
> some scripts, which is why I think it's important for it to be tested.
> 
> Change-Id: I5b2103e3cec0cfdcccf7ffb00eb05fed8626e66d
> ---
>  gdb/testsuite/gdb.guile/scm-parameter.exp | 8 ++++++++
>  1 file changed, 8 insertions(+)
> 
> diff --git a/gdb/testsuite/gdb.guile/scm-parameter.exp b/gdb/testsuite/gdb.guile/scm-parameter.exp
> index 67120b233664..5a2fb42503d0 100644
> --- a/gdb/testsuite/gdb.guile/scm-parameter.exp
> +++ b/gdb/testsuite/gdb.guile/scm-parameter.exp
> @@ -176,6 +176,14 @@ with_test_prefix "test-restricted-param" {
>  gdb_test "guile (register-parameter! (make-parameter \"height\"))" \
>      "ERROR.*is already defined.*" "error registering existing parameter"
>  
> +# Test printing and setting the value of an unregistered parameter.
> +gdb_test "guile (print (parameter-value (make-parameter \"foo\")))" \
> +    "= #f"
> +gdb_test "guile (define myparam (make-parameter \"foo\"))"
> +gdb_test_no_output "guile (set-parameter-value! myparam #t)"
> +gdb_test "guile (print (parameter-value myparam))" \
> +    "= #t"
> +
>  # Test registering a parameter named with what was an ambiguous spelling
>  # of existing parameters.
>  
> 

I pushed everything up to this point.

Simon

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

* Re: [PATCH 15/16] gdb: make cmd_list_element var an optional union
  2021-07-20 23:03               ` Lancelot SIX
@ 2021-07-23 19:56                 ` Simon Marchi
  2021-07-23 20:46                   ` Lancelot SIX
  2021-07-28 20:13                 ` Tom Tromey
  1 sibling, 1 reply; 50+ messages in thread
From: Simon Marchi @ 2021-07-23 19:56 UTC (permalink / raw)
  To: Lancelot SIX; +Cc: gdb-patches

> Hi,
> 
> I reworked a bit my previous implementation to see to what extent it can
> support what you are looking for.  Short answer: it can (and it is
> completely transparent to the caller).
> 
> In the previous draft version[1], I introduced a struct that groups
> together the var_type and the pointer to the actual data in order to
> abstract a setting (i.e. a value with a type that can be SET or SHOWn).
> This struct provides getters and setters in order to enforce that we
> cast the pointer to the adequate type.  I have modified this data
> structure so we can register user-provided callbacks.  If those
> functions are provided, the getter and setters will call them to
> generate retrieve the value instead of trying to dereference the
> pointer.
> 
> For the moment, I just made sure that this compiles and I played with
> dummy settings in my local tree.  It is not yet finalized nor fully
> operational because do_set_command will try to free the pointer returned
> by the getter when dealing with char* data.  With user provided
> callbacks memory should probably not be managed at that level but by the
> getter / setter.  Nothing worrying, you have a commit that plans to
> change the char* with std::string, this should solve the issue quite
> nicely.
> 
> As for the previous version, this adds a significant amount of templated
> code that can make the implementation appear quite complex.
> 
> I think I’ll wait for your series to land before I finalize this
> proposal (this is just a POC for the moment) unless you want to build on
> top of this approach to introduce user provided callbacks.

Do you suggest I merge my patch that makes it a union first?  Won't that
be counter productive if you plan to make it a void* again?  I'm a bit
lost now, so I am not sure what's the best order for doing things.

Simon

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

* Re: [PATCH 15/16] gdb: make cmd_list_element var an optional union
  2021-07-23 19:56                 ` Simon Marchi
@ 2021-07-23 20:46                   ` Lancelot SIX
  2021-07-23 21:15                     ` Simon Marchi
  0 siblings, 1 reply; 50+ messages in thread
From: Lancelot SIX @ 2021-07-23 20:46 UTC (permalink / raw)
  To: Simon Marchi; +Cc: gdb-patches

Hi,

I plan on spending some more time on it this week end hopefully.

If you do not mind waiting a bit for my patch to be finalized, and want
to go with my approach, there is indeed no point merging yours now.  The
main thing I try to avoid is having both of us working at the same time
on different solutions of the same problem.  Your approach has the
advantage of being almost ready, I can wait for it to be stabilized
before I adapt my work on top on it.  If you are fine waiting for my
approach, I’ll proceed!

My plan is to:

- Improve slightly
  https://sourceware.org/pipermail/gdb-patches/2021-July/180991.html
  (use gdb::Requires instead of static assert, include the python
  binding I forgot because I did not build it, add some selftests of
  the runtime checks introduced by the getters and getters, and ensure
  the documentation is adequate).
- Adapt your patch using std::string on top on my architecture
  (https://sourceware.org/pipermail/gdb-patches/2021-July/180921.html).
- Add a new patch that allows the use of setter / getter (based on what
  was is drafted in
  https://sourceware.org/pipermail/gdb-patches/2021-July/181037.html).
  As a side note, the storage of user provided getter / setter functions
  is done using a unions (void* is not an appropriate for that).  It is
  possible to use the same approach to create getters and setters around
  the union of pointers you provide in your patch (but this would require
  some work, obviously).

Does that makes sense?

Best,
Lancelot.

On Fri, Jul 23, 2021 at 03:56:57PM -0400, Simon Marchi wrote:
> > Hi,
> > 
> > I reworked a bit my previous implementation to see to what extent it can
> > support what you are looking for.  Short answer: it can (and it is
> > completely transparent to the caller).
> > 
> > In the previous draft version[1], I introduced a struct that groups
> > together the var_type and the pointer to the actual data in order to
> > abstract a setting (i.e. a value with a type that can be SET or SHOWn).
> > This struct provides getters and setters in order to enforce that we
> > cast the pointer to the adequate type.  I have modified this data
> > structure so we can register user-provided callbacks.  If those
> > functions are provided, the getter and setters will call them to
> > generate retrieve the value instead of trying to dereference the
> > pointer.
> > 
> > For the moment, I just made sure that this compiles and I played with
> > dummy settings in my local tree.  It is not yet finalized nor fully
> > operational because do_set_command will try to free the pointer returned
> > by the getter when dealing with char* data.  With user provided
> > callbacks memory should probably not be managed at that level but by the
> > getter / setter.  Nothing worrying, you have a commit that plans to
> > change the char* with std::string, this should solve the issue quite
> > nicely.
> > 
> > As for the previous version, this adds a significant amount of templated
> > code that can make the implementation appear quite complex.
> > 
> > I think I’ll wait for your series to land before I finalize this
> > proposal (this is just a POC for the moment) unless you want to build on
> > top of this approach to introduce user provided callbacks.
> 
> Do you suggest I merge my patch that makes it a union first?  Won't that
> be counter productive if you plan to make it a void* again?  I'm a bit
> lost now, so I am not sure what's the best order for doing things.
> 
> Simon

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

* Re: [PATCH 15/16] gdb: make cmd_list_element var an optional union
  2021-07-23 20:46                   ` Lancelot SIX
@ 2021-07-23 21:15                     ` Simon Marchi
  2021-07-23 22:55                       ` Lancelot SIX
  0 siblings, 1 reply; 50+ messages in thread
From: Simon Marchi @ 2021-07-23 21:15 UTC (permalink / raw)
  To: Lancelot SIX; +Cc: gdb-patches

On 2021-07-23 4:46 p.m., Lancelot SIX wrote:
> Hi,
> 
> I plan on spending some more time on it this week end hopefully.
> 
> If you do not mind waiting a bit for my patch to be finalized, and want
> to go with my approach, there is indeed no point merging yours now.  The
> main thing I try to avoid is having both of us working at the same time
> on different solutions of the same problem.  Your approach has the
> advantage of being almost ready, I can wait for it to be stabilized
> before I adapt my work on top on it.  If you are fine waiting for my
> approach, I’ll proceed!
> 
> My plan is to:
> 
> - Improve slightly
>   https://sourceware.org/pipermail/gdb-patches/2021-July/180991.html
>   (use gdb::Requires instead of static assert, include the python
>   binding I forgot because I did not build it, add some selftests of
>   the runtime checks introduced by the getters and getters, and ensure
>   the documentation is adequate).
> - Adapt your patch using std::string on top on my architecture
>   (https://sourceware.org/pipermail/gdb-patches/2021-July/180921.html).
> - Add a new patch that allows the use of setter / getter (based on what
>   was is drafted in
>   https://sourceware.org/pipermail/gdb-patches/2021-July/181037.html).
>   As a side note, the storage of user provided getter / setter functions
>   is done using a unions (void* is not an appropriate for that).  It is
>   possible to use the same approach to create getters and setters around
>   the union of pointers you provide in your patch (but this would require
>   some work, obviously).
> 
> Does that makes sense?

Yeah, the only thing I would ask: could you verify whether the approach
of allowing getters / setters that you envision can work with my
work-in-progress patch here?  Or if my patch can be changed to work with
your approach.

    https://review.lttng.org/c/binutils-gdb/+/5831

The reason for all this churn and refactor is to fix some bugs (as
described in the commit log of the patch), so I want to make sure
there's a way forward.

Also, could you provide a git branch with your latest patch?  It will be
easier than figuring out on what to apply it.

Simon

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

* Re: [PATCH 15/16] gdb: make cmd_list_element var an optional union
  2021-07-23 21:15                     ` Simon Marchi
@ 2021-07-23 22:55                       ` Lancelot SIX
  2021-07-24  2:04                         ` Simon Marchi
  0 siblings, 1 reply; 50+ messages in thread
From: Lancelot SIX @ 2021-07-23 22:55 UTC (permalink / raw)
  To: Simon Marchi; +Cc: gdb-patches

On Fri, Jul 23, 2021 at 05:15:44PM -0400, Simon Marchi wrote:
> On 2021-07-23 4:46 p.m., Lancelot SIX wrote:
> > Hi,
> > 
> > I plan on spending some more time on it this week end hopefully.
> > 
> > If you do not mind waiting a bit for my patch to be finalized, and want
> > to go with my approach, there is indeed no point merging yours now.  The
> > main thing I try to avoid is having both of us working at the same time
> > on different solutions of the same problem.  Your approach has the
> > advantage of being almost ready, I can wait for it to be stabilized
> > before I adapt my work on top on it.  If you are fine waiting for my
> > approach, I’ll proceed!
> > 
> > My plan is to:
> > 
> > - Improve slightly
> >   https://sourceware.org/pipermail/gdb-patches/2021-July/180991.html
> >   (use gdb::Requires instead of static assert, include the python
> >   binding I forgot because I did not build it, add some selftests of
> >   the runtime checks introduced by the getters and getters, and ensure
> >   the documentation is adequate).
> > - Adapt your patch using std::string on top on my architecture
> >   (https://sourceware.org/pipermail/gdb-patches/2021-July/180921.html).
> > - Add a new patch that allows the use of setter / getter (based on what
> >   was is drafted in
> >   https://sourceware.org/pipermail/gdb-patches/2021-July/181037.html).
> >   As a side note, the storage of user provided getter / setter functions
> >   is done using a unions (void* is not an appropriate for that).  It is
> >   possible to use the same approach to create getters and setters around
> >   the union of pointers you provide in your patch (but this would require
> >   some work, obviously).
> > 
> > Does that makes sense?
> 
> Yeah, the only thing I would ask: could you verify whether the approach
> of allowing getters / setters that you envision can work with my
> work-in-progress patch here?  Or if my patch can be changed to work with
> your approach.
> 
>     https://review.lttng.org/c/binutils-gdb/+/5831
> 
> The reason for all this churn and refactor is to fix some bugs (as
> described in the commit log of the patch), so I want to make sure
> there's a way forward.
> 

Yes, the usage seems to be quite close (from the command writer point of
view).  The major diferences at the momment is:
- Your setter returns a bool to let know the caller that the setting has
  been updated where mine does not.  I should change that.

Other than that, you should be able to fit your set_inferior_tty_command
and get_inferior_tty (among others) in my framework quite easily, in a
very similar way.

Your add_setshow_*_cmd are changed from something like

  commands.set->function.set_cmd.set_func.string = set_func;
  commands.show->function.show_cmd.get_func.string = get_func;

to something like

  commands.set->var.set_accessor<var_filename> (set_func, get_func);
  commands.show->var.set_accessor<var_filename> (set_func, get_func);

(I should probably not set both accessors with one function, only one
will ever be used).

> Also, could you provide a git branch with your latest patch?  It will be
> easier than figuring out on what to apply it.

I have pushed my current work (still a work in progress) to the
following branch on sourceware: users/lsix/refactor-typesafe-var.

Lancelot.
> 
> Simon

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

* Re: [PATCH 15/16] gdb: make cmd_list_element var an optional union
  2021-07-23 22:55                       ` Lancelot SIX
@ 2021-07-24  2:04                         ` Simon Marchi
  0 siblings, 0 replies; 50+ messages in thread
From: Simon Marchi @ 2021-07-24  2:04 UTC (permalink / raw)
  To: Lancelot SIX; +Cc: gdb-patches

> Yes, the usage seems to be quite close (from the command writer point of
> view).  The major diferences at the momment is:
> - Your setter returns a bool to let know the caller that the setting has
>   been updated where mine does not.  I should change that.

Returning a bool is necessary to be able to tell whether we should emit
an MI =param-changed notification or not.  I think that's the only use.

> Other than that, you should be able to fit your set_inferior_tty_command
> and get_inferior_tty (among others) in my framework quite easily, in a
> very similar way.
> 
> Your add_setshow_*_cmd are changed from something like
> 
>   commands.set->function.set_cmd.set_func.string = set_func;
>   commands.show->function.show_cmd.get_func.string = get_func;
> 
> to something like
> 
>   commands.set->var.set_accessor<var_filename> (set_func, get_func);
>   commands.show->var.set_accessor<var_filename> (set_func, get_func);

Great!

> (I should probably not set both accessors with one function, only one
> will ever be used).

Just for the sake of discussion:

Conceptually, I find it a little bit wrong that settings are "part" of
the set and show commands.  Ideally, I think a setting (or parameter,
the two terms are used almost interchangeably) should be decoupled from
the set/show commands.  There are various ways to read setting values,
show commands are just one of them.  So, every time some part of GDB
needs to look up a setting, it actually looks up the corresponding show
command.  I think that settings should be kept in their own registry,
indexed by name.  You would have one setting instance for "non-stop",
for example, with a getter and a setter function.  The "set non-stop"
and "show non-stop" cmd_list_element objects would both point to this
instance and use the setting's setter or getter function.

Other parts of GDB that need to access settings, like the gdb.parameter
Python function, would look up the settings registry.  For example,
gdb.parameter('non-stop') would look up 'non-stop' in that registry, and
not look up the 'show non-stop' command as it does today.

>> Also, could you provide a git branch with your latest patch?  It will be
>> easier than figuring out on what to apply it.
> 
> I have pushed my current work (still a work in progress) to the
> following branch on sourceware: users/lsix/refactor-typesafe-var.

Thanks!

Simon

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

* Re: [PATCH 13/16] gdb: remove cmd_list_element::function::sfunc
  2021-07-14  4:55 ` [PATCH 13/16] gdb: remove cmd_list_element::function::sfunc Simon Marchi
@ 2021-07-28 19:10   ` Tom Tromey
  2021-07-28 21:17     ` Simon Marchi
  0 siblings, 1 reply; 50+ messages in thread
From: Tom Tromey @ 2021-07-28 19:10 UTC (permalink / raw)
  To: Simon Marchi via Gdb-patches

>>>>> "Simon" == Simon Marchi via Gdb-patches <gdb-patches@sourceware.org> writes:

Simon> I don't understand what the sfunc function type in
Simon> cmd_list_element::function is for.

Thanks for cleaning this stuff up.
My feeling is that, probably, some of this was from a planned transition
that was never completed.  It's hard to know for sure though.

Simon>  int
Simon>  cli_user_command_p (struct cmd_list_element *cmd)
Simon>  {
Simon> -  return (cmd->theclass == class_user
Simon> -	  && (cmd->func == do_simple_func || cmd->func == do_sfunc));
Simon> +  return cmd->theclass == class_user && cmd->func == do_simple_func;

This seems to change the semantics of this function, but I wonder if it
matters at all.  (Ideally, though, cli_user_command_p would simply go
away.)

Tom

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

* Re: [PATCH 15/16] gdb: make cmd_list_element var an optional union
  2021-07-14  4:55 ` [PATCH 15/16] gdb: make cmd_list_element var an optional union Simon Marchi
  2021-07-14 12:08   ` Lancelot SIX
  2021-07-18 15:44   ` Lancelot SIX
@ 2021-07-28 19:47   ` Tom Tromey
  2021-07-28 20:59     ` Simon Marchi
  2 siblings, 1 reply; 50+ messages in thread
From: Tom Tromey @ 2021-07-28 19:47 UTC (permalink / raw)
  To: Simon Marchi via Gdb-patches

>>>>> "Simon" == Simon Marchi via Gdb-patches <gdb-patches@sourceware.org> writes:

Simon> I propose changing cmd_list_element to be a union of all the possible
Simon> pointer types.  Fundamentally, this doesn't change much what is
Simon> happening.  But I think this helps understand better how the field is
Simon> used and get a bit of type safety (while it is still possible to use the
Simon> wrong member at some point, we can't cast to something completely
Simon> unrealted).

Makes sense to me.

Simon> I wrapped the union in an optional, because we need to check in some
Simon> spots whether var is set or not.  I think that conceptually, an optional
Simon> makes the most sense.  Another option would be to pick an arbitrary
Simon> member of the union and compare it to nullptr, whenever we want to know
Simon> whether var is set, but that seems a bit more hack-ish.

There could be a "void *null_if_not_set" member that's only used for the
check.  This would save some space.  Not sure it matters though.

Tom

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

* Re: [PATCH 15/16] gdb: make cmd_list_element var an optional union
  2021-07-20 23:03               ` Lancelot SIX
  2021-07-23 19:56                 ` Simon Marchi
@ 2021-07-28 20:13                 ` Tom Tromey
  2021-07-28 20:45                   ` Lancelot SIX
  1 sibling, 1 reply; 50+ messages in thread
From: Tom Tromey @ 2021-07-28 20:13 UTC (permalink / raw)
  To: Lancelot SIX via Gdb-patches; +Cc: Simon Marchi, Lancelot SIX

>>>>> "Lancelot" == Lancelot SIX via Gdb-patches <gdb-patches@sourceware.org> writes:

Lancelot> This patch proposes to add an abstraction around the pair of the
Lancelot> var_type and the void* pointer in order to prevent the user having to
Lancelot> handle the cast.  This is achieved by introducing the setting struct,
Lancelot> and a set of templated member functions.  The template parameter is the
Lancelot> VAR_TYPES we want to the access the data for.  The template ensures the
Lancelot> return type of the get method and the parameter type of the set method
Lancelot> matches the data type used to store the actual data.

Seems nice.

Lancelot> 	int get<var_integer, var_zinteger> ()

It seems to me that it might be better if the get<> template itself knew
about the storage equivalence between different var_ types and so code
like this:

Lancelot> +	    var = cmd->var.get<var_string,
Lancelot> +			       var_string_noescape,
Lancelot> +			       var_optional_filename,
Lancelot> +			       var_filename> ();

... could be simplified.

Tom

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

* Re: [PATCH 16/16] gdb: make string-like set show commands use std::string variable
  2021-07-14  4:55 ` [PATCH 16/16] gdb: make string-like set show commands use std::string variable Simon Marchi
@ 2021-07-28 20:27   ` Tom Tromey
  2021-07-28 21:03     ` Simon Marchi
  0 siblings, 1 reply; 50+ messages in thread
From: Tom Tromey @ 2021-07-28 20:27 UTC (permalink / raw)
  To: Simon Marchi via Gdb-patches

>>>>> "Simon" == Simon Marchi via Gdb-patches <gdb-patches@sourceware.org> writes:

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

It seems like a decent idea to me.  I didn't go through the patch.  I
imagine it's mostly fairly mechanical transforms.

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

That seems fine.  It might be nice if we had a way to track these little
cleanup items.  Maybe a tag in bugzilla.

I suppose the main danger of this patch is lingering old-style variables
in code you couldn't build, say some of the less-supported native
targets.  But, we usually handle those on a best-effort basis anyway.
And, maybe they don't use this feature.

Tom

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

* Re: [PATCH 15/16] gdb: make cmd_list_element var an optional union
  2021-07-28 20:13                 ` Tom Tromey
@ 2021-07-28 20:45                   ` Lancelot SIX
  2021-07-29 17:47                     ` Tom Tromey
  0 siblings, 1 reply; 50+ messages in thread
From: Lancelot SIX @ 2021-07-28 20:45 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Lancelot SIX via Gdb-patches

On Wed, Jul 28, 2021 at 02:13:21PM -0600, Tom Tromey wrote:
> >>>>> "Lancelot" == Lancelot SIX via Gdb-patches <gdb-patches@sourceware.org> writes:
> 
> Lancelot> This patch proposes to add an abstraction around the pair of the
> Lancelot> var_type and the void* pointer in order to prevent the user having to
> Lancelot> handle the cast.  This is achieved by introducing the setting struct,
> Lancelot> and a set of templated member functions.  The template parameter is the
> Lancelot> VAR_TYPES we want to the access the data for.  The template ensures the
> Lancelot> return type of the get method and the parameter type of the set method
> Lancelot> matches the data type used to store the actual data.
> 
> Seems nice.
> 
> Lancelot> 	int get<var_integer, var_zinteger> ()
> 
> It seems to me that it might be better if the get<> template itself knew
> about the storage equivalence between different var_ types and so code
> like this:
> 
> Lancelot> +	    var = cmd->var.get<var_string,
> Lancelot> +			       var_string_noescape,
> Lancelot> +			       var_optional_filename,
> Lancelot> +			       var_filename> ();
> 
> ... could be simplified.
> 
> Tom

Hi,

Would you prefer to have something like var.get<var_string> () return a
string happily if var is lets say a var_filename but fail if it is a
var_boolean?

I guess it is possible, but I did intentionally went for a very explicit
approach.  The idea is to have the user of the class state that he is
aware he will access a data of a given type that can be interpreted in
different ways.  This kind of makes more sense when dealing with the
various var_*integer* types where the same value can have different
meaning depending on the effective var_type.

In a way, I guess this is covered by the various fall through of the
switch / case statement where this call is typically used.  The
limitation of this is that the switch will not complain if you try to
share a code path for incompatible types, you will need to wait for a
runtime error instead of a compile time error.

Anyway, if you find it more practical, I can try rework the interface
this way.

By the way, I just posted a complete series that contains this proposal,
plus other improvements[1].

I’ll probably wait a bit to see if I get different feedbacks and
come up with something I fill will work for the most. 

Thanks,
Lancelot.

[1] https://sourceware.org/pipermail/gdb-patches/2021-July/181183.html

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

* Re: [PATCH 15/16] gdb: make cmd_list_element var an optional union
  2021-07-28 19:47   ` Tom Tromey
@ 2021-07-28 20:59     ` Simon Marchi
  2021-07-29 17:41       ` Tom Tromey
  0 siblings, 1 reply; 50+ messages in thread
From: Simon Marchi @ 2021-07-28 20:59 UTC (permalink / raw)
  To: Tom Tromey, Simon Marchi via Gdb-patches

On 2021-07-28 3:47 p.m., Tom Tromey wrote:
>>>>>> "Simon" == Simon Marchi via Gdb-patches <gdb-patches@sourceware.org> writes:
> 
> Simon> I propose changing cmd_list_element to be a union of all the possible
> Simon> pointer types.  Fundamentally, this doesn't change much what is
> Simon> happening.  But I think this helps understand better how the field is
> Simon> used and get a bit of type safety (while it is still possible to use the
> Simon> wrong member at some point, we can't cast to something completely
> Simon> unrealted).
> 
> Makes sense to me.
> 
> Simon> I wrapped the union in an optional, because we need to check in some
> Simon> spots whether var is set or not.  I think that conceptually, an optional
> Simon> makes the most sense.  Another option would be to pick an arbitrary
> Simon> member of the union and compare it to nullptr, whenever we want to know
> Simon> whether var is set, but that seems a bit more hack-ish.
> 
> There could be a "void *null_if_not_set" member that's only used for the
> check.  This would save some space.  Not sure it matters though.

In this case, I don't think we need to squeeze things to save every byte
possible, so I would favor readability / expressiveness of the types.  I
also like optional, because have the possibility to add runtime checks
(like _GLIBCXX_DEBUG does) to make sure we don't dereference an optional
that doesn't contain a value.

I'd like to add that to our gdb::optional version (maybe predicated on
_GLIBCXX_DEBUG would be fine)?  But I wonder why the one from libstdc++
doesn't already have it...

Simon

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

* Re: [PATCH 16/16] gdb: make string-like set show commands use std::string variable
  2021-07-28 20:27   ` Tom Tromey
@ 2021-07-28 21:03     ` Simon Marchi
  0 siblings, 0 replies; 50+ messages in thread
From: Simon Marchi @ 2021-07-28 21:03 UTC (permalink / raw)
  To: Tom Tromey, Simon Marchi via Gdb-patches

On 2021-07-28 4:27 p.m., Tom Tromey wrote:
>>>>>> "Simon" == Simon Marchi via Gdb-patches <gdb-patches@sourceware.org> writes:
> 
> Simon> String-like settings (var_string, var_filename, var_optional_filename,
> Simon> var_string_noescape) currently take a pointer to a `char *` storage
> Simon> variable (typically global) that holds the setting's value.  I'd like to
> Simon> "mordernize" this by changing them to use an std::string for storage.
> 
> It seems like a decent idea to me.  I didn't go through the patch.  I
> imagine it's mostly fairly mechanical transforms.

Yes, although there could be more subtle cases where empty string vs
nullptr did matter.

> Simon> The add_path function in source.c is really complex and twisted, I'd
> Simon> rather not try to change it to work on an std::string right now.
> Simon> Instead, I added an overload that copies the std:string to a `char *`
> Simon> and back.  This means more copying, but this is not used in a hot path
> Simon> at all, so I think it is acceptable.
> 
> That seems fine.  It might be nice if we had a way to track these little
> cleanup items.  Maybe a tag in bugzilla.

I'll try to think adding one when this patch (a subsequent version of
it) gets merged.

> I suppose the main danger of this patch is lingering old-style variables
> in code you couldn't build, say some of the less-supported native
> targets.  But, we usually handle those on a best-effort basis anyway.
> And, maybe they don't use this feature.

I did my best to grep and check for those native files I know aren't
compiled, but as you say it's best effort.  Like with other
code-base-wide changes, I think that if I forgot any spots, they will be
very easy to fix.

Simon

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

* Re: [PATCH 13/16] gdb: remove cmd_list_element::function::sfunc
  2021-07-28 19:10   ` Tom Tromey
@ 2021-07-28 21:17     ` Simon Marchi
  2021-07-29 17:33       ` Tom Tromey
  0 siblings, 1 reply; 50+ messages in thread
From: Simon Marchi @ 2021-07-28 21:17 UTC (permalink / raw)
  To: Tom Tromey, Simon Marchi via Gdb-patches

On 2021-07-28 3:10 p.m., Tom Tromey wrote:
>>>>>> "Simon" == Simon Marchi via Gdb-patches <gdb-patches@sourceware.org> writes:
> 
> Simon> I don't understand what the sfunc function type in
> Simon> cmd_list_element::function is for.
> 
> Thanks for cleaning this stuff up.
> My feeling is that, probably, some of this was from a planned transition
> that was never completed.  It's hard to know for sure though.

That's what I thought as well.

> Simon>  int
> Simon>  cli_user_command_p (struct cmd_list_element *cmd)
> Simon>  {
> Simon> -  return (cmd->theclass == class_user
> Simon> -	  && (cmd->func == do_simple_func || cmd->func == do_sfunc));
> Simon> +  return cmd->theclass == class_user && cmd->func == do_simple_func;
> 
> This seems to change the semantics of this function, but I wonder if it
> matters at all.  (Ideally, though, cli_user_command_p would simply go
> away.)

I'm not 100% sure either, this is all very confusing.

The ways to create commands with class_user I know of are:

 - do_define_command
 - Python and Guile, if the user passes gdb.COMMAND_USER as the class.

do_define_command passes the dummy user_defined_command function as the
command's function, which makes it have a "simple func".  Python and
Guile do not.  So in the end that check will filter out Python and Guile
commands, which I think is what we want to do.

Simon

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

* Re: [PATCH 13/16] gdb: remove cmd_list_element::function::sfunc
  2021-07-28 21:17     ` Simon Marchi
@ 2021-07-29 17:33       ` Tom Tromey
  0 siblings, 0 replies; 50+ messages in thread
From: Tom Tromey @ 2021-07-29 17:33 UTC (permalink / raw)
  To: Simon Marchi; +Cc: Tom Tromey, Simon Marchi via Gdb-patches

>>>>> "Simon" == Simon Marchi <simon.marchi@polymtl.ca> writes:

Simon> The ways to create commands with class_user I know of are:

Simon>  - do_define_command
Simon>  - Python and Guile, if the user passes gdb.COMMAND_USER as the class.

Simon> do_define_command passes the dummy user_defined_command function as the
Simon> command's function, which makes it have a "simple func".  Python and
Simon> Guile do not.  So in the end that check will filter out Python and Guile
Simon> commands, which I think is what we want to do.

Makes sense to me.  Thank you.

Tom

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

* Re: [PATCH 15/16] gdb: make cmd_list_element var an optional union
  2021-07-28 20:59     ` Simon Marchi
@ 2021-07-29 17:41       ` Tom Tromey
  2021-07-29 17:44         ` Simon Marchi
  0 siblings, 1 reply; 50+ messages in thread
From: Tom Tromey @ 2021-07-29 17:41 UTC (permalink / raw)
  To: Simon Marchi; +Cc: Tom Tromey, Simon Marchi via Gdb-patches

>>>>> "Simon" == Simon Marchi <simon.marchi@polymtl.ca> writes:

Simon> I'd like to add that to our gdb::optional version (maybe predicated on
Simon> _GLIBCXX_DEBUG would be fine)?  But I wonder why the one from libstdc++
Simon> doesn't already have it...

It might be there, at least along some paths:

      constexpr _Tp&
      _M_get() noexcept
      {
	__glibcxx_assert(this->_M_is_engaged());
	return static_cast<_Dp*>(this)->_M_payload._M_get();
      }

There are other implementations of this method that don't check, though,
and I don't understand why.

Anyway I think it would be fine to add.

Tom

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

* Re: [PATCH 15/16] gdb: make cmd_list_element var an optional union
  2021-07-29 17:41       ` Tom Tromey
@ 2021-07-29 17:44         ` Simon Marchi
  2021-07-29 17:49           ` Tom Tromey
  0 siblings, 1 reply; 50+ messages in thread
From: Simon Marchi @ 2021-07-29 17:44 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Simon Marchi via Gdb-patches



On 2021-07-29 1:41 p.m., Tom Tromey wrote:
>>>>>> "Simon" == Simon Marchi <simon.marchi@polymtl.ca> writes:
> 
> Simon> I'd like to add that to our gdb::optional version (maybe predicated on
> Simon> _GLIBCXX_DEBUG would be fine)?  But I wonder why the one from libstdc++
> Simon> doesn't already have it...
> 
> It might be there, at least along some paths:
> 
>       constexpr _Tp&
>       _M_get() noexcept
>       {
> 	__glibcxx_assert(this->_M_is_engaged());
> 	return static_cast<_Dp*>(this)->_M_payload._M_get();
>       }
> 
> There are other implementations of this method that don't check, though,
> and I don't understand why.

Indeed, I filed this with gcc and was (rightfully) corrected:

  https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101659

What other implementations are you referring to?

> Anyway I think it would be fine to add.

I'll give it a try.

Simon

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

* Re: [PATCH 15/16] gdb: make cmd_list_element var an optional union
  2021-07-28 20:45                   ` Lancelot SIX
@ 2021-07-29 17:47                     ` Tom Tromey
  2021-07-29 20:12                       ` Lancelot SIX
  0 siblings, 1 reply; 50+ messages in thread
From: Tom Tromey @ 2021-07-29 17:47 UTC (permalink / raw)
  To: Lancelot SIX; +Cc: Tom Tromey, Lancelot SIX via Gdb-patches

>>>>> "Lancelot" == Lancelot SIX <lsix@lancelotsix.com> writes:

Lancelot> Would you prefer to have something like var.get<var_string> () return a
Lancelot> string happily if var is lets say a var_filename but fail if it is a
Lancelot> var_boolean?

Yeah.

Lancelot> I guess it is possible, but I did intentionally went for a very explicit
Lancelot> approach.  The idea is to have the user of the class state that he is
Lancelot> aware he will access a data of a given type that can be interpreted in
Lancelot> different ways.  This kind of makes more sense when dealing with the
Lancelot> various var_*integer* types where the same value can have different
Lancelot> meaning depending on the effective var_type.

I don't understand how to reconcile this with the idea that one could
pass in a bunch of var_ parameters and get some result back.
Like does get<var_integer, var_zinteger> work?

I guess the argument would be that the author of that line is supposed
to understand what's happening and that the meaning of the value might
"vary"?  For me that seems to add a bunch of extra template code but
without any real gain in safety.

Tom

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

* Re: [PATCH 15/16] gdb: make cmd_list_element var an optional union
  2021-07-29 17:44         ` Simon Marchi
@ 2021-07-29 17:49           ` Tom Tromey
  2021-07-29 18:00             ` Simon Marchi
  0 siblings, 1 reply; 50+ messages in thread
From: Tom Tromey @ 2021-07-29 17:49 UTC (permalink / raw)
  To: Simon Marchi; +Cc: Tom Tromey, Simon Marchi via Gdb-patches

>>>>> "Simon" == Simon Marchi <simon.marchi@polymtl.ca> writes:

>> There are other implementations of this method that don't check, though,
>> and I don't understand why.

Simon> Indeed, I filed this with gcc and was (rightfully) corrected:

Simon>   https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101659

Simon> What other implementations are you referring to?

struct _Optional_payload_base has:

      constexpr _Tp&
      _M_get() noexcept
      { return this->_M_payload._M_value; }

I don't know why this one doesn't check, but I suppose given the
resolution of that bug, it's probably fine.

Tom

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

* Re: [PATCH 15/16] gdb: make cmd_list_element var an optional union
  2021-07-29 17:49           ` Tom Tromey
@ 2021-07-29 18:00             ` Simon Marchi
  0 siblings, 0 replies; 50+ messages in thread
From: Simon Marchi @ 2021-07-29 18:00 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Simon Marchi via Gdb-patches

On 2021-07-29 1:49 p.m., Tom Tromey wrote:
> struct _Optional_payload_base has:
> 
>       constexpr _Tp&
>       _M_get() noexcept
>       { return this->_M_payload._M_value; }
> 
> I don't know why this one doesn't check, but I suppose given the
> resolution of that bug, it's probably fine.

This looks like an internal class, perhaps the assertion is made at all
entry points that possibly lead to this being called?

Simon

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

* Re: [PATCH 15/16] gdb: make cmd_list_element var an optional union
  2021-07-29 17:47                     ` Tom Tromey
@ 2021-07-29 20:12                       ` Lancelot SIX
  2021-07-30  2:09                         ` Simon Marchi
  0 siblings, 1 reply; 50+ messages in thread
From: Lancelot SIX @ 2021-07-29 20:12 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Lancelot SIX via Gdb-patches

On Thu, Jul 29, 2021 at 11:47:10AM -0600, Tom Tromey wrote:
> >>>>> "Lancelot" == Lancelot SIX <lsix@lancelotsix.com> writes:
> 
> Lancelot> Would you prefer to have something like var.get<var_string> () return a
> Lancelot> string happily if var is lets say a var_filename but fail if it is a
> Lancelot> var_boolean?
> 
> Yeah.
> 
> Lancelot> I guess it is possible, but I did intentionally went for a very explicit
> Lancelot> approach.  The idea is to have the user of the class state that he is
> Lancelot> aware he will access a data of a given type that can be interpreted in
> Lancelot> different ways.  This kind of makes more sense when dealing with the
> Lancelot> various var_*integer* types where the same value can have different
> Lancelot> meaning depending on the effective var_type.
> 
> I don't understand how to reconcile this with the idea that one could
> pass in a bunch of var_ parameters and get some result back.
> Like does get<var_integer, var_zinteger> work?

Yes, given that both var_integer and var_zinteger use a int*, this would
work. However, something like get<var_uinteger, var_zuienteger> will
fail to compile because var_uinteger uses unsigned int* while
var_zuinteger uses a int*.

> 
> I guess the argument would be that the author of that line is supposed
> to understand what's happening and that the meaning of the value might
> "vary"?

It is the original idea.  Multiple "var_types" can share the same
storage type while still being different logical types (a given value is
to be interpreted differently based on the actual type).  The original
idea was to use it in a 'get<var_sometype>' way but this was conflicting
with how this is used.  I followed that path to see where it lead.

> For me that seems to add a bunch of extra template code but
> without any real gain in safety.

I’ll revisit all of this in a V2 later.  I was worried about the amount
of template boilerplate this adds compared to the value it brings, I
should have worried a bit more!  If I strip the extra template noise,
this can come down to some get_string/set_string/get_int/set_int and so
on.  The 'get<var_?>' can be a light abstraction on top of that, if ever
needed (I am not sure it is).

Thanks for the comments, much appreciated.

Best,
Lancelot.

> 
> Tom

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

* Re: [PATCH 15/16] gdb: make cmd_list_element var an optional union
  2021-07-29 20:12                       ` Lancelot SIX
@ 2021-07-30  2:09                         ` Simon Marchi
  2021-07-30 17:47                           ` Lancelot SIX
  0 siblings, 1 reply; 50+ messages in thread
From: Simon Marchi @ 2021-07-30  2:09 UTC (permalink / raw)
  To: Lancelot SIX, Tom Tromey; +Cc: Lancelot SIX via Gdb-patches



On 2021-07-29 4:12 p.m., Lancelot SIX via Gdb-patches wrote:
> On Thu, Jul 29, 2021 at 11:47:10AM -0600, Tom Tromey wrote:
>>>>>>> "Lancelot" == Lancelot SIX <lsix@lancelotsix.com> writes:
>>
>> Lancelot> Would you prefer to have something like var.get<var_string> () return a
>> Lancelot> string happily if var is lets say a var_filename but fail if it is a
>> Lancelot> var_boolean?
>>
>> Yeah.
>>
>> Lancelot> I guess it is possible, but I did intentionally went for a very explicit
>> Lancelot> approach.  The idea is to have the user of the class state that he is
>> Lancelot> aware he will access a data of a given type that can be interpreted in
>> Lancelot> different ways.  This kind of makes more sense when dealing with the
>> Lancelot> various var_*integer* types where the same value can have different
>> Lancelot> meaning depending on the effective var_type.
>>
>> I don't understand how to reconcile this with the idea that one could
>> pass in a bunch of var_ parameters and get some result back.
>> Like does get<var_integer, var_zinteger> work?
> 
> Yes, given that both var_integer and var_zinteger use a int*, this would
> work. However, something like get<var_uinteger, var_zuienteger> will
> fail to compile because var_uinteger uses unsigned int* while
> var_zuinteger uses a int*.
> 
>>
>> I guess the argument would be that the author of that line is supposed
>> to understand what's happening and that the meaning of the value might
>> "vary"?
> 
> It is the original idea.  Multiple "var_types" can share the same
> storage type while still being different logical types (a given value is
> to be interpreted differently based on the actual type).  The original
> idea was to use it in a 'get<var_sometype>' way but this was conflicting
> with how this is used.  I followed that path to see where it lead.
> 
>> For me that seems to add a bunch of extra template code but
>> without any real gain in safety.

There is some gain in safety... in that it could catch some mistakes at
compile time.  I don't really have an opinion on when the amount of
template complexity gets too high compared to the gain in safety.

> I’ll revisit all of this in a V2 later.  I was worried about the amount
> of template boilerplate this adds compared to the value it brings, I
> should have worried a bit more!  If I strip the extra template noise,
> this can come down to some get_string/set_string/get_int/set_int and so
> on.  The 'get<var_?>' can be a light abstraction on top of that, if ever
> needed (I am not sure it is).

Would the get_string/set_string/get_int/set_int methods still assert
that the var_type matches the storage type accessed?  I think that's
important to have.

Simon

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

* Re: [PATCH 15/16] gdb: make cmd_list_element var an optional union
  2021-07-30  2:09                         ` Simon Marchi
@ 2021-07-30 17:47                           ` Lancelot SIX
  0 siblings, 0 replies; 50+ messages in thread
From: Lancelot SIX @ 2021-07-30 17:47 UTC (permalink / raw)
  To: Simon Marchi; +Cc: Tom Tromey, Lancelot SIX via Gdb-patches

> 
> Would the get_string/set_string/get_int/set_int methods still assert
> that the var_type matches the storage type accessed?  I think that's
> important to have.
> 
> Simon

Yes, I’ll maintain that. The thing I’ll drop it the assertion in
foo.get<var_a, var_b>() that checks that foo.type is one of [var_a, var_b].
The variadic template will go away, but it is the part that brings the
most 'complexity' (i.e. verbose and not necessarily easy to read
template code) but only to bring this restrictive assertion.

The most important part that will remain are 1) the get/set functions
that will help introduce callbacks later and 2) the check that the
var_type and the cast are compatible.

Lancelot.

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

end of thread, other threads:[~2021-07-30 17:47 UTC | newest]

Thread overview: 50+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-07-14  4:55 [PATCH 00/16] Bunch of commands related cleanups Simon Marchi
2021-07-14  4:55 ` [PATCH 01/16] gdb/testsuite: split gdb.python/py-parameter.exp in procs Simon Marchi
2021-07-14  4:55 ` [PATCH 02/16] gdb.base/setshow.exp: use save_vars to save/restore gdb_prompt Simon Marchi
2021-07-14  4:55 ` [PATCH 03/16] gdb.base/setshow.exp: split in procs Simon Marchi
2021-07-14  4:55 ` [PATCH 04/16] gdb.base/setshow.exp: fix duplicate test name Simon Marchi
2021-07-14  4:55 ` [PATCH 05/16] gdb: un-share set_inferior_cwd declaration Simon Marchi
2021-07-14  4:55 ` [PATCH 06/16] gdb: remove inferior::{argc,argv} Simon Marchi
2021-07-14  4:55 ` [PATCH 07/16] gdb: add setter/getter for inferior arguments Simon Marchi
2021-07-14  4:55 ` [PATCH 08/16] gdb: add setter/getter for inferior cwd Simon Marchi
2021-07-14  4:55 ` [PATCH 09/16] gdb: make inferior::m_args an std::string Simon Marchi
2021-07-14  4:55 ` [PATCH 10/16] gdb: make inferior::m_cwd " Simon Marchi
2021-07-14  4:55 ` [PATCH 11/16] gdb: make inferior::m_terminal " Simon Marchi
2021-07-14  4:55 ` [PATCH 12/16] gdb: rename cfunc to simple_func Simon Marchi
2021-07-14  4:55 ` [PATCH 13/16] gdb: remove cmd_list_element::function::sfunc Simon Marchi
2021-07-28 19:10   ` Tom Tromey
2021-07-28 21:17     ` Simon Marchi
2021-07-29 17:33       ` Tom Tromey
2021-07-14  4:55 ` [PATCH 14/16] gdb/testsuite: test get/set value of unregistered Guile parameter Simon Marchi
2021-07-23 19:42   ` Simon Marchi
2021-07-14  4:55 ` [PATCH 15/16] gdb: make cmd_list_element var an optional union Simon Marchi
2021-07-14 12:08   ` Lancelot SIX
2021-07-14 17:12     ` Lancelot SIX
2021-07-14 19:22       ` Simon Marchi
2021-07-14 23:22         ` Lancelot SIX
2021-07-19 14:32           ` Simon Marchi
2021-07-19 19:52             ` Simon Marchi
2021-07-20 23:03               ` Lancelot SIX
2021-07-23 19:56                 ` Simon Marchi
2021-07-23 20:46                   ` Lancelot SIX
2021-07-23 21:15                     ` Simon Marchi
2021-07-23 22:55                       ` Lancelot SIX
2021-07-24  2:04                         ` Simon Marchi
2021-07-28 20:13                 ` Tom Tromey
2021-07-28 20:45                   ` Lancelot SIX
2021-07-29 17:47                     ` Tom Tromey
2021-07-29 20:12                       ` Lancelot SIX
2021-07-30  2:09                         ` Simon Marchi
2021-07-30 17:47                           ` Lancelot SIX
2021-07-18 15:44   ` Lancelot SIX
2021-07-19 14:19     ` Simon Marchi
2021-07-19 20:58       ` Lancelot SIX
2021-07-28 19:47   ` Tom Tromey
2021-07-28 20:59     ` Simon Marchi
2021-07-29 17:41       ` Tom Tromey
2021-07-29 17:44         ` Simon Marchi
2021-07-29 17:49           ` Tom Tromey
2021-07-29 18:00             ` Simon Marchi
2021-07-14  4:55 ` [PATCH 16/16] gdb: make string-like set show commands use std::string variable Simon Marchi
2021-07-28 20:27   ` Tom Tromey
2021-07-28 21:03     ` Simon Marchi

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).