public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
From: Lancelot SIX <lsix@lancelotsix.com>
To: Simon Marchi <simon.marchi@polymtl.ca>
Cc: gdb-patches@sourceware.org
Subject: Re: [PATCH 15/16] gdb: make cmd_list_element var an optional union
Date: Wed, 14 Jul 2021 12:08:51 +0000	[thread overview]
Message-ID: <20210714120851.3pfew5pgcdp6ezn6@ubuntu.lan> (raw)
In-Reply-To: <20210714045520.1623120-16-simon.marchi@polymtl.ca>

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

  reply	other threads:[~2021-07-14 12:08 UTC|newest]

Thread overview: 50+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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 [this message]
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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210714120851.3pfew5pgcdp6ezn6@ubuntu.lan \
    --to=lsix@lancelotsix.com \
    --cc=gdb-patches@sourceware.org \
    --cc=simon.marchi@polymtl.ca \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).