Re: [PATCH] describe: fix --no-exact-match

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




Am 10.08.23 um 02:41 schrieb Jeff King:
> On Wed, Aug 09, 2023 at 06:41:13PM +0200, René Scharfe wrote:
>
>> And sorry for the unused parameter warning.  Just checked; there are
>> 170+ of those remaining before we can enable it in developer mode.  :-/
>> Seems worthwhile, though, especially not slapping UNUSED blindly on
>> them.
>
> I know, I'm working on it. :) There were more than 800 when I started.
> I'm hoping to send out the final patches during this 2.43 cycle.
>
>> Oh.  Do we really need all those?  Anyway, if we added at least the
>> most common ones, we'd be better off already, I'd expect:
>>
>>    $ % git grep -h ' = opt->value;' | sed 's/\*.*$//; s/^ *//' | sort | uniq -c | sort -nr | head -10
>>      29 struct diff_options
>>      12 int
>>       7 struct grep_opt
>>       6 struct rebase_options
>>       6 struct apply_state
>>       5 struct strbuf
>>       5 char
>>       4 struct note_data
>>       3 struct string_list
>>       2 struct strvec
>>
>> Increasing the size of the struct like that would increase the total
>> memory footprint by a few KB at most -- tolerable.
>
> Hmm, I was thinking that "int_value" might not be so bad. But it seems
> like a pretty bad layering violation for parse-options.c to know about
> "struct apply_state" and so on. That really is what void pointers are
> for.

True, keeping a list of external types in parse-options.h is clumsy and
ugly.

> As for size, you should be able to use a union. All of the types inside
> the struct are pointers, so they'd all be the same size. Of course then
> you lose some of the safety. If the caller assigned to "int_value" that
> is distinct from "foo_value", then you can be sure you will get a NULL
> and segfault upon accessing foo_value. With a union, you get whatever
> type-punning undefined behavior the compiler sees fit. And the point is
> making sure the two match.

Which makes a union a non-starter -- we need a reliable error.

> We really don't care about separate storage, though. We want type
> safety. Maybe an option there would be to let the callback function take
> the appropriate type, and cast it. I.e., something like:
>
>   /* define a callback taking the real type */
>   int my_int_opt(int *value, struct option *opt, ...etc...) { ...body... }
>
>   /* when mentioning a callback, include the type, and make sure that
>    * the value and the callback both match it */
>   #define OPT_CALLBACK(s, l, type, v, cb, ...etc...) \
>     { ..., \
>       value.type = (v), \
>       callback = (int (*)(type *, struct option *opt, ...etc...), \
>     }
>   ...
>   OPT_CALLBACK('f', "foo", int, &my_local_int, my_int_opt)

The idea is good, even though I don't understand how your specific
example would work.  Dabbled with something similar for typed qsort
(need to clean it up and submit it one of these days).

> I'm pretty sure that falls afoul of the standard, though, because we
> eventually need to call that function, and we'll do so through a
> function pointer that doesn't match its declaration.

Fighting possible type mismatches by adding a certain type mismatch
won't fly..

> I'm not sure there's a portable and non-insane way of doing what we want
> here. At least at compile-time.

We need a wrapper with the correct signature.  The wrapper is plugged
into struct option.  The typed callback is called by the wrapper and
can be used for a type check in the struct macro.  Demo patch below.


> At run-time you could record type
> information in an enum and check it in the callback. That seems like
> a lot of work for little reward, though.

Agreed -- runtime type checks are for scripting languages..

René



diff --git a/builtin/describe.c b/builtin/describe.c
index b28a4a1f82..ce16c36de2 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -558,15 +558,17 @@ static void describe(const char *arg, int last_one)
 	strbuf_release(&sb);
 }

-static int option_parse_exact_match(const struct option *opt, const char *arg,
-				    int unset)
+static int option_parse_exact_match(const struct option *opt UNUSED,
+				    const char *arg, int unset, int *value)
 {
 	BUG_ON_OPT_ARG(arg);

-	max_candidates = unset ? DEFAULT_CANDIDATES : 0;
+	*value = unset ? DEFAULT_CANDIDATES : 0;
 	return 0;
 }

+DEFINE_PARSE_OPT_CB(option_parse_exact_match);
+
 int cmd_describe(int argc, const char **argv, const char *prefix)
 {
 	int contains = 0;
@@ -578,9 +580,9 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
 		OPT_BOOL(0, "long",       &longformat, N_("always use long format")),
 		OPT_BOOL(0, "first-parent", &first_parent, N_("only follow first parent")),
 		OPT__ABBREV(&abbrev),
-		OPT_CALLBACK_F(0, "exact-match", NULL, NULL,
-			       N_("only output exact matches"),
-			       PARSE_OPT_NOARG, option_parse_exact_match),
+		OPT_CALLBACK_F_T(0, "exact-match", &max_candidates, NULL,
+				 N_("only output exact matches"),
+				 PARSE_OPT_NOARG, option_parse_exact_match),
 		OPT_INTEGER(0, "candidates", &max_candidates,
 			    N_("consider <n> most recent tags (default: 10)")),
 		OPT_STRING_LIST(0, "match", &patterns, N_("pattern"),
diff --git a/parse-options.h b/parse-options.h
index 57a7fe9d91..f957931cfa 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -67,6 +67,14 @@ enum parse_opt_result {
 struct option;
 typedef int parse_opt_cb(const struct option *, const char *arg, int unset);

+#define DEFINE_PARSE_OPT_CB(name)				\
+static inline int name ## __void(const struct option *opt,	\
+				 const char *arg, int unset)	\
+{								\
+	return name(opt, arg, unset, opt->value);		\
+}								\
+struct option
+
 struct parse_opt_ctx_t;
 typedef enum parse_opt_result parse_opt_ll_cb(struct parse_opt_ctx_t *ctx,
 					      const struct option *opt,
@@ -198,6 +206,16 @@ struct option {
 	.flags = (f), \
 	.callback = (cb), \
 }
+#define OPT_CALLBACK_F_T(s, l, v, a, h, f, cb) { \
+	.type = OPTION_CALLBACK, \
+	.short_name = (s) + (0 ? cb(NULL, NULL, 0, (v)) : 0), \
+	.long_name = (l), \
+	.value = (v), \
+	.argh = (a), \
+	.help = (h), \
+	.flags = (f), \
+	.callback = cb ## __void, \
+}
 #define OPT_STRING_F(s, l, v, a, h, f) { \
 	.type = OPTION_STRING, \
 	.short_name = (s), \




[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux